Android 앱에 reCAPTCHA Enterprise 통합

이 페이지에서는 Android 앱에 reCAPTCHA Enterprise를 통합하는 방법을 설명합니다.

의심스러운 트래픽에 시각적 챌린지를 제공하려면 SafetyNet reCAPTCHA API를 사용하면 됩니다.

시작하기 전에

  1. reCAPTCHA Enterprise 환경을 준비합니다.

  2. Android 앱 플랫폼의 reCAPTCHA 키를 만듭니다.

    또는 다음 단계 중 하나를 수행하여 Android용 기존 reCAPTCHA 키의 ID를 복사할 수 있습니다.

    • Google Cloud 콘솔에서 기존 키의 ID를 복사하려면 다음을 수행합니다.

      1. reCAPTCHA Enterprise 페이지로 이동합니다.

        reCAPTCHA Enterprise로 이동

      2. reCAPTCHA 키 목록에서 복사할 키 위에 마우스 포인터를 올려놓고 를 클릭합니다.
    • REST API를 사용하여 기존 키의 ID를 복사하려면 projects.keys.list 메서드를 사용합니다.
    • gcloud CLI를 사용하여 기존 키의 ID를 복사하려면 gcloud recaptcha keys list 명령어를 사용합니다.

Android 환경 준비

기본 Android

  1. 최신 버전의 Android 스튜디오를 다운로드하고 설치하여 개발 환경을 준비합니다.

  2. 최소 Android SDK 값이 API 19: Android 4.4(KitKat)로 설정된 앱이 있는지 확인합니다. 앱의 최소 SDK를 API 19로 설정하거나 새 모바일 앱을 만들 수 있습니다.

  3. 새 모바일 앱을 만드는 경우 새로운 Android 스튜디오 프로젝트를 시작하여 테스트 애플리케이션을 만듭니다.

    1. 빈 활동을 선택합니다. 앱에서 Jetpack Compose를 사용하려면 빈 Compose 활동을 선택합니다.
    2. 언어를 kotlin으로 설정합니다.
    3. 최소 SDK 값을 API 19: Android 4.4(KitKat)로 설정합니다.
  4. 다음 스니펫과 같이 Google의 Maven 저장소 google()이 프로젝트 수준 build.gradle 파일의 저장소 목록에 있는지 확인합니다.

    allprojects {
        repositories {
            google()
        }
    }
    

    자세한 내용은 Google의 Maven 저장소를 참조하세요.

  5. reCAPTCHA Enterprise API 종속 항목을 추가하려면 앱 수준 build.gradle 파일의 dependencies 섹션에 다음 빌드 규칙을 추가합니다.

      implementation 'com.google.android.recaptcha:recaptcha:18.5.0-beta02'
    

    Android 앱에 종속 항목을 추가하는 방법에 대한 자세한 내용은 빌드 종속 항목 추가를 참조하세요.

  6. 애플리케이션 매니페스트에서 첫 번째 <manifest> 태그와 첫 번째 <application> 태그 사이에 인터넷 권한을 추가합니다(예: AndroidManifest.xml). reCAPTCHA Enterprise API를 사용하려면 네트워크 작업이 필요하므로 이 권한이 필요합니다.

    <manifest ...>
    
        <uses-permission android:name="android.permission.INTERNET" />
    
        <application ...>
        ...
      </application>
    </manifest>
    
  7. 새 프로젝트에서 AndroidX 라이브러리를 사용하려면 SDK를 Android 9.0 이상으로 컴파일하고 다음 코드 스니펫을 gradle.properties에 추가하세요.

    android.useAndroidX=true
    android.enableJetifier=true
    

    자세한 내용은 AndroidX로 마이그레이션을 참조하세요.

Flutter

Flutter를 통해 reCAPTCHA Enterprise를 사용하는 방법에 대한 자세한 내용은 Flutter 문서를 참조하세요.

React Native

React Native를 통해 reCAPTCHA Enterprise를 사용하는 방법에 대한 자세한 내용은 React Native 문서를 참조하세요.

Android 앱에 reCAPTCHA Enterprise 통합

  1. Android 앱용으로 만든 reCAPTCHA 키(KEY_ID)를 사용하여 클라이언트를 인스턴스화합니다.

    Kotlin

    class MainActivity : AppCompatActivity() {
    
        private lateinit var recaptchaClient: RecaptchaClient
    
        override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)
          initializeRecaptchaClient()
          // ... rest of onCreate code
        }
    
        private fun initializeRecaptchaClient() {
          lifecycleScope.launch {
            Recaptcha.getClient(application, "KEY_ID")
              .onSuccess { client ->
                recaptchaClient = client
              }
              .onFailure { exception ->
                // Handle communication errors ...
                // See "Handle communication errors" section
              }
          }
        }
    
        // ... rest of activity code.
    }
    

    자바

    public final class MainActivity extends Activity {
      @Nullable private RecaptchaTasksClient recaptchaTasksClient = null;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initializeRecaptchaClient();
        // ... rest of onCreate code
      }
    
      private void initializeRecaptchaClient() {
        Recaptcha
          .getTasksClient(getApplication(), "KEY_ID")
          .addOnSuccessListener(
              this,
              new OnSuccessListener<RecaptchaTasksClient>() {
                @Override
                public void onSuccess(RecaptchaTasksClient client) {
                  MainActivity.this.recaptchaTasksClient = client;
                }
              })
          .addOnFailureListener(
              this,
              new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                  // Handle communication errors ...
                  // See "Handle communication errors" section
                }
              });
      }
    
      // ... rest of activity code.
    }
    
  2. reCAPTCHA Enterprise를 사용하여 보호되는 앱의 각 작업에 대해 execute 메서드를 호출하여 RecaptchaAction을 전달합니다. reCAPTCHA Enterprise는 기본 제공 작업 집합을 제공하며 필요한 경우 커스텀 작업을 만들 수도 있습니다.

    다음 코드 스니펫은 execute를 사용하여 LOGIN 작업을 보호하는 방법을 보여줍니다.

    Kotlin

    private fun executeLoginAction() {
      lifecycleScope.launch {
        recaptchaClient
          .execute(RecaptchaAction.LOGIN)
          .onSuccess { token ->
            // Handle success ...
            // See "What's next" section for instructions
            // about handling tokens.
          }
          .onFailure { exception ->
            // Handle communication errors ...
            // See "Handle communication errors" section
          }
      }
    }
    

    자바

    private void executeLoginAction(View v) {
      assert recaptchaTasksClient != null;
      recaptchaTasksClient
        .executeTask(RecaptchaAction.LOGIN)
        .addOnSuccessListener(
            this,
            new OnSuccessListener<String>() {
              @Override
              public void onSuccess(String token) {
                // Handle success ...
                // See "What's next" section for instructions
                // about handling tokens.
              }
            })
        .addOnFailureListener(
            this,
            new OnFailureListener() {
              @Override
              public void onFailure(@NonNull Exception e) {
                // Handle communication errors ...
                // See "Handle communication errors" section
              }
            });
    }
    

다음 샘플 코드 스니펫은 단일 활동 앱에서 getClient()execute() 메서드의 전체 통합을 보여줍니다.

Kotlin

class MainActivity : AppCompatActivity() {

  private lateinit var recaptchaClient: RecaptchaClient

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    initializeRecaptchaClient()
    findViewById<View>(R.id.btn_login).setOnClickListener { executeLoginAction() }
    findViewById<View>(R.id.btn_redeem).setOnClickListener { executeRedeemAction() }
  }

  private fun initializeRecaptchaClient() {
    lifecycleScope.launch {
      Recaptcha.getClient(application, "KEY_ID")
        .onSuccess { client ->
          recaptchaClient = client
        }
        .onFailure { exception ->
          // Handle communication errors ...
          // See "Handle communication errors" section
        }
    }
  }

  private fun executeLoginAction() {
    lifecycleScope.launch {
      recaptchaClient
        .execute(RecaptchaAction.LOGIN)
        .onSuccess { token ->
          // Handle success ...
          // See "What's next" section for instructions
          // about handling tokens.
        }
        .onFailure { exception ->
          // Handle communication errors ...
          // See "Handle communication errors" section
        }
    }
  }

  private fun executeRedeemAction(){
    lifecycleScope.launch {
      recaptchaClient
        .execute(RecaptchaAction.custom("redeem"))
        .onSuccess { token ->
          // Handle success ...
          // See "What's next" section for instructions
          // about handling tokens.
        }
        .onFailure { exception ->
          // Handle communication errors ...
          // See "Handle communication errors" section
        }
    }
  }
}

자바

public final class MainActivity extends Activity {
  @Nullable private RecaptchaTasksClient recaptchaTasksClient = null;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initializeRecaptchaClient();
    findViewById(R.id.btn_login).setOnClickListener(this::executeLoginAction);
    findViewById(R.id.btn_redeem).setOnClickListener(this::executeRedeemAction);
  }

  private void initializeRecaptchaClient() {
    Recaptcha
      .getTasksClient(getApplication(), "KEY_ID")
      .addOnSuccessListener(
          this,
          new OnSuccessListener<RecaptchaTasksClient>() {
            @Override
            public void onSuccess(RecaptchaTasksClient client) {
              MainActivity.this.recaptchaTasksClient = client;
            }
          })
      .addOnFailureListener(
          this,
          new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
              // Handle communication errors ...
              // See "Handle communication errors" section
            }
          });
  }

  private void executeLoginAction(View v) {
    assert recaptchaTasksClient != null;
    recaptchaTasksClient
      .executeTask(RecaptchaAction.LOGIN)
      .addOnSuccessListener(
          this,
          new OnSuccessListener<String>() {
            @Override
            public void onSuccess(String token) {
              // Handle success ...
              // See "What's next" section for instructions
              // about handling tokens.
            }
          })
      .addOnFailureListener(
          this,
          new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
              // Handle communication errors ...
              // See "Handle communication errors" section
            }
          });
  }

  private void executeRedeemAction(View v) {
    assert recaptchaTasksClient != null;
    recaptchaTasksClient
      .executeTask(RecaptchaAction.custom("redeem"))
      .addOnSuccessListener(
          this,
          new OnSuccessListener<String>() {
            @Override
            public void onSuccess(String token) {
              // Handle success ...
              // See "What's next" section for instructions
              // about handling tokens.
            }
          })
      .addOnFailureListener(
          this,
          new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
              // Handle communication errors ...
              // See "Handle communication errors" section
            }
          });
  }
}

API 호출 제한 시간 설정

각 API의 timeout 속성을 사용하여 getClientexecute API에 대한 제한 시간 값을 지정할 수 있습니다.

getClient에는 10초, execute API에는 10초의 기본 제한 시간이 사용됩니다.

Kotlin

  • getClient 호출 시 제한 시간을 설정합니다.

        lifecycleScope.launch {
          Recaptcha.getClient(application, "KEY_ID", timeout = 20000L)
            .onSuccess { client ->
              recaptchaClient = client
            }
            .onFailure { exception ->
              // Handle communication errors ...
              // See "Handle communication errors" section
            }
        }
    

    이 코드 스니펫은 getClient의 제한 시간을 20초로 설정합니다.

  • execute 호출 시 제한 시간을 설정합니다.

        lifecycleScope.launch {
          recaptchaClient
            .execute(RecaptchaAction.LOGIN(), timeout = 10000L)
            .onSuccess { token ->
              // Handle success ...
              // See "What's next" section for instructions
              // about handling tokens.
            }
            .onFailure { exception ->
              // Handle communication errors ...
              // See "Handle communication errors" section
            }
        }
    

    이 코드 스니펫은 execute의 제한 시간을 10초로 설정합니다.

자바

  • getClient 호출 시 제한 시간을 설정합니다.

      Recaptcha
        .getTasksClient(getApplication(), "KEY_ID", 20000L)
        .addOnSuccessListener(
            this,
            new OnSuccessListener<RecaptchaTasksClient>() {
              @Override
              public void onSuccess(RecaptchaTasksClient client) {
                MainActivity.this.recaptchaTasksClient = client;
              }
            })
        .addOnFailureListener(
            this,
            new OnFailureListener() {
              @Override
              public void onFailure(@NonNull Exception e) {
                // Handle communication errors ...
                // See "Handle communication errors" section
              }
            });
    

    이 코드 스니펫은 getClient의 제한 시간을 20초로 설정합니다.

  • execute 호출 시 제한 시간을 설정합니다.

      recaptchaTasksClient
        .executeTask(RecaptchaAction.custom("redeem"), 10000L)
        .addOnSuccessListener(
            this,
            new OnSuccessListener<String>() {
              @Override
              public void onSuccess(String token) {
                // Handle success ...
                // See "What's next" section for instructions
                // about handling tokens.
              }
            })
        .addOnFailureListener(
            this,
            new OnFailureListener() {
              @Override
              public void onFailure(@NonNull Exception e) {
                // Handle communication errors ...
                // See "Handle communication errors" section
              }
            });
    

    이 코드 스니펫은 execute의 제한 시간을 10초로 설정합니다.

통신 오류 처리하기

앱이 reCAPTCHA 서비스와 성공적으로 통신할 수 없으면 API에 오류가 발생한 것일 수 있습니다. 이러한 오류를 정상적으로 처리하는 로직을 앱에 추가해야 합니다.

일반적인 API 오류의 완화에 대한 자세한 내용은 RecaptchaStatusCode를 참조하세요.

API 참조

Android용 reCAPTCHA API의 전체 참조는 com.google.android.recaptcha를 참조하세요.

다음 단계

  • reCAPTCHA 응답 토큰을 평가하려면 평가를 만듭니다.