웹 UI 스팸 방지 및 CAPTCHA 지원
GitLab v19.1GitLab 애플리케이션의 새로운 UI 영역에 스팸 방지 및 CAPTCHA 지원을 추가하는 방법은 기존 코드의 구현 방식에 따라 다릅니다. 세 가지 시나리오가 지원됩니다. Apollo 사용 (Fetch/XHR 요청을 통한 GraphQL API)
GitLab 애플리케이션의 새로운 UI 영역에 스팸 방지 및 CAPTCHA 지원을 추가하는 방법은 기존 코드의 구현 방식에 따라 다릅니다.
지원되는 요청 제출 시나리오#
세 가지 시나리오가 지원됩니다. 두 가지는 Apollo 또는 Axios를 사용하는 JavaScript XHR/Fetch 요청에 사용되며, 하나는 표준 HTML 폼 요청에만 사용됩니다:
- JavaScript 기반 제출 (Vue를 통할 수도 있음)
Apollo 사용 (Fetch/XHR 요청을 통한 GraphQL API)
-
Axios 사용 (Fetch/XHR 요청을 통한 REST API)
-
표준 HTML 폼 제출 (HTML 요청)
구현의 일부는 어떤 시나리오를 지원해야 하는지에 따라 달라집니다.
JavaScript XHR/Fetch 요청에 특화된 구현 작업#
두 가지 방법이 완전히 지원됩니다:
-
GraphQL API를 사용하는 Apollo.
-
GraphQL API를 사용하는 Axios.
프론트엔드와 백엔드 간의 스팸 및 CAPTCHA 관련 데이터 통신에는 모델에 추가 필드를 추가할 필요가 없습니다. 대신 다음을 통해 통신이 처리됩니다:
-
요청의 커스텀 헤더 값을 통해.
-
응답의 최상위 JSON 필드를 통해.
스팸 및 CAPTCHA 관련 로직도 재사용 가능한 모듈과 헬퍼 메서드로 깔끔하게 추상화되어 있으며, 기존 로직을 래핑할 수 있습니다. 잠재적인 스팸이 감지되거나 CAPTCHA 표시가 필요한 경우에만 기존 플로우를 변경합니다. 이 접근 방식을 통해 애플리케이션의 새 영역에 스팸 및 CAPTCHA 지원을 최소한의 변경으로 추가할 수 있습니다. 프론트엔드의 경우, 잠재적으로 제로(zero) 변경이 필요할 수 있습니다!
프론트엔드에서는 Apollo에 ApolloLink를 사용하고 Axios에는 Axios 인터셉터를 사용하여 추상적이고 투명하게 처리됩니다. CAPTCHA 표시는 표준 GitLab UI / Pajamas 모달 컴포넌트로 처리됩니다. 관련 프론트엔드 코드는 모두 app/assets/javascripts/captcha 아래에서 찾을 수 있습니다.
그러나 요청 인터셉션 및 모달의 실제 처리가 투명하게 이루어지고, 폼이나 페이지의 관련 JavaScript 또는 Vue 컴포넌트에 대한 필수 변경 사항이 없더라도, 요청 또는 오류 처리에서 변경이 필요할 수 있습니다. 기존 동작이 올바르게 작동하지 않을 수 있기 때문에 변경이 필요합니다. 예를 들어, 실패하거나 취소된 CAPTCHA 표시가 표준 요청 플로우나 UI 업데이트를 방해하는 경우입니다. 잠재적인 문제를 발견하기 위해 모든 시나리오에 대한 신중한 탐색적 테스트가 중요합니다.
이 시퀀스 다이어그램은 프론트엔드의 JavaScript XHR/Fetch 요청에 대한 표준 CAPTCHA 플로우를 보여줍니다:
sequenceDiagram participant U as User participant V as Vue/JS Application participant A as ApolloLink or Axios Interceptor participant G as GitLab API U->>V: Save model V->>A: Request A->>G: Request G--xA: Response with error and spam/CAPTCHA related fields A->>U: CAPTCHA presented in modal U->>A: CAPTCHA solved to obtain valid CAPTCHA response A->>G: Request with valid CAPTCHA response and SpamLog ID in headers G-->>A: Response with success A-->>V: Response with success
백엔드도 믹스인 모듈과 헬퍼 메서드를 통해 깔끔하게 추상화되어 있습니다. 관련 백엔드 컨트롤러 액션(일반적으로 create/update)에 필요한 세 가지 주요 변경 사항은 다음과 같습니다:
-
Update Service 클래스 생성자에
perform_spam_check: true를 전달합니다. Create Service에서는 기본적으로true로 설정됩니다. -
스팸 검사에서 모델에 대한 변경 사항이 스팸일 가능성이 있다고 나타내면:
모델에 오류가 추가됩니다.
-
모델의
needs_recaptcha속성이 true로 설정됩니다. -
기존 컨트롤러 액션 반환 값(렌더링 또는 리디렉션)을
#with_captcha_check_json_format헬퍼 메서드에 전달된 블록으로 래핑하면, 이 메서드가 투명하게 처리합니다:
CAPTCHA가 활성화되어 있는지 확인하고, 활성화된 경우 다음 단계로 진행합니다.
- 모델에 오류가 있는지,
needs_recaptcha플래그가 true인지 확인합니다.
예인 경우: 적절한 스팸 또는 CAPTCHA 필드를 JSON 응답에 추가하고 409 - Conflict HTTP 상태 코드를 반환합니다.
- 아니오인 경우(CAPTCHA가 비활성화되어 있거나 스팸이 감지되지 않은 경우): 블록에 전달된 표준 요청 반환 로직이 실행됩니다.
추상화 덕분에 설명하는 것보다 구현이 더 간단합니다. 숨겨진 세부 사항에 대해 많이 걱정할 필요가 없습니다!
다음 변경 사항을 적용하세요:
컨트롤러 액션에 지원 추가#
기능의 프론트엔드가 GraphQL API만 사용하는 것이 아니라 컨트롤러 액션에 직접 제출하는 경우, 적절한 컨트롤러에 지원을 추가해야 합니다.
액션 메서드는 컨트롤러 클래스에 직접 있을 수도 있고, 컨트롤러 클래스에 포함된 모듈로 추상화될 수도 있습니다. 예시에서는 모듈을 사용합니다. 컨트롤러를 직접 수정할 때의 유일한 차이점은:
extend ActiveSupport::Concern이 필요하지 않다는 것입니다.
module WidgetsActions
# NOTE: This `extend` probably already exists, but it MUST be moved to occur BEFORE all
# `include` statements. Otherwise, confusing bugs may occur in which the methods
# in the included modules cannot be found.
extend ActiveSupport::Concern
include SpammableActions::CaptchaCheck::JsonFormatActionsSupport
def create
widget = ::Widgets::CreateService.new(
project: project,
current_user: current_user,
params: params
).execute
respond_to do |format|
format.json do
with_captcha_check_json_format do
# The action's existing `render json: ...` (or wrapper method) and related logic. Possibly
# including different rendering cases if the model is valid or not. It's all wrapped here
# within the `with_captcha_check_json_format` block. For example:
if widget.valid?
render json: serializer.represent(widget)
else
render json: { errors: widget.errors.full_messages }, status: :unprocessable_entity
end
end
end
end
end
end
HTML 폼 요청에 특화된 구현 작업#
애플리케이션의 일부 영역은 JavaScript 클라이언트를 통한 GraphQL API를 사용하도록 전환되지 않았으며, 대신 HTML MIME 타입 요청을 통한 표준 Rails HAML 폼 제출에 의존합니다. 이러한 영역에서 액션은 사전 렌더링된 HTML(HAML) 페이지를 응답 본문으로 반환합니다. 안타깝게도 이 경우
불가능합니다
위에 설명된 JavaScript 기반 프론트엔드 지원을 사용하는 것이. 대신 HAML 템플릿을 통해 CAPTCHA 폼 렌더링을 처리하는 대체 방법을 사용해야 합니다.
모든 것이 여전히 깔끔하게 추상화되어 있으며, 백엔드 컨트롤러의 구현은 JavaScript/JSON 기반 방법과 거의 동일합니다. 모듈 이름과 헬퍼 메서드에서 JSON을 HTML로 바꾸세요(적절한 대소문자 사용).
액션 메서드는 컨트롤러에 직접 있을 수도 있고, 모듈에 있을 수도 있습니다. 이 예에서는 컨트롤러에 직접 있으며, create 대신 update 메서드를 사용합니다:
class WidgetsController < ApplicationController
include SpammableActions::CaptchaCheck::HtmlFormatActionsSupport
def update
# Existing logic to find the `widget` model instance...
::Widgets::UpdateService.new(
project: project,
current_user: current_user,
params: params,
perform_spam_check: true
).execute(widget)
respond_to do |format|
format.html do
if widget.valid?
# NOTE: `spammable_path` is required by the `SpammableActions::AkismetMarkAsSpamAction`
# module, and it should have already been implemented on this controller according to
# the instructions above. It is reused here to avoid duplicating the route helper call.
redirect_to spammable_path
else
# If we got here, there were errors on the model instance - from a failed spam check
# and/or other validation errors on the model. Either way, we'll re-render the form,
# and if a CAPTCHA render is necessary, it will be automatically handled by
# `with_captcha_check_html_format`
with_captcha_check_html_format { render :edit }
end
end
end
end
end