การตรวจสอบสิทธิ์จะระบุตัวตนของบุคคล และมักเรียกว่าการลงชื่อสมัครใช้หรือการลงชื่อเข้าใช้ของผู้ใช้ การให้สิทธิ์คือกระบวนการให้หรือปฏิเสธการเข้าถึง ข้อมูลหรือทรัพยากร เช่น แอปของคุณขอความยินยอมจากผู้ใช้เพื่อ เข้าถึง Google ไดรฟ์ของผู้ใช้
การเรียกการตรวจสอบสิทธิ์และการให้สิทธิ์ควรเป็น 2 ขั้นตอนที่แยกกันและแตกต่างกัน ตามความต้องการของแอป
หากแอปมีฟีเจอร์ที่ใช้ข้อมูล Google API ได้ แต่ไม่จำเป็นต้องเป็นส่วนหนึ่งของฟีเจอร์หลักของแอป คุณควรออกแบบแอปให้สามารถจัดการกรณีที่เข้าถึงข้อมูล API ไม่ได้ ตัวอย่างเช่น คุณอาจซ่อนรายการไฟล์ที่บันทึกไว้ล่าสุดเมื่อผู้ใช้ไม่ได้ให้สิทธิ์เข้าถึงไดรฟ์
คุณควรขอสิทธิ์เข้าถึงขอบเขตที่จำเป็นต่อการเข้าถึง Google API เฉพาะเมื่อผู้ใช้ดำเนินการที่ต้องใช้สิทธิ์เข้าถึง API ใด API หนึ่งเท่านั้น เช่น คุณควรขอสิทธิ์เข้าถึงไดรฟ์ของผู้ใช้ทุกครั้งที่ผู้ใช้แตะปุ่ม "บันทึกลงในไดรฟ์"
การแยกการให้สิทธิ์จากการตรวจสอบสิทธิ์จะช่วยให้คุณหลีกเลี่ยงการสร้างความสับสนให้ผู้ใช้ใหม่ หรือทำให้ผู้ใช้สงสัยว่าเหตุใดจึงมีการขอสิทธิ์บางอย่าง
สำหรับการตรวจสอบสิทธิ์ เราขอแนะนำให้ใช้ Credential Manager API สําหรับการให้สิทธิ์การดําเนินการที่ต้องเข้าถึงข้อมูลผู้ใช้ที่ Google จัดเก็บไว้ เราขอแนะนําให้ใช้ AuthorizationClient
สร้าง โปรเจ็กต์
- เปิดโปรเจ็กต์ใน หรือสร้างโปรเจ็กต์หากยังไม่มี
- ใน ตรวจสอบว่าข้อมูลทั้งหมดครบถ้วนและถูกต้อง
- ตรวจสอบว่าแอปมีชื่อแอป โลโก้แอป และหน้าแรกของแอปที่ถูกต้อง ค่าเหล่านี้จะแสดงต่อผู้ใช้ในหน้าจอขอความยินยอมให้ใช้ฟีเจอร์ลงชื่อเข้าใช้ด้วย Google เมื่อลงชื่อสมัครใช้ และในหน้าจอแอปและบริการของบุคคลที่สาม
- ตรวจสอบว่าคุณได้ระบุ URL ของนโยบายความเป็นส่วนตัวและ ข้อกำหนดในการให้บริการของแอปแล้ว
- ใน สร้างรหัสไคลเอ็นต์ Android สำหรับแอปหากยังไม่มี คุณจะต้องระบุชื่อแพ็กเกจและลายเซ็น SHA-1 ของแอป
- ใน ให้สร้างรหัสไคลเอ็นต์ "เว็บแอปพลิเคชัน" ใหม่หากยังไม่ได้สร้าง คุณสามารถละเว้นช่อง "ต้นทาง JavaScript ที่ได้รับอนุญาต" และ "URI การเปลี่ยนเส้นทางที่ได้รับอนุญาต" ในตอนนี้ได้ ระบบจะใช้รหัสไคลเอ็นต์นี้เพื่อระบุเซิร์ฟเวอร์แบ็กเอนด์ เมื่อเซิร์ฟเวอร์สื่อสารกับบริการตรวจสอบสิทธิ์ของ Google
ประกาศทรัพยากร Dependency
ในไฟล์ build.gradle ของโมดูล ให้ประกาศการขึ้นต่อกันโดยใช้ไลบรารีบริการข้อมูลประจำตัวของ Google เวอร์ชันล่าสุด
dependencies { // ... other dependencies implementation "com.google.android.gms:play-services-auth:21.4.0" }
ขอสิทธิ์ที่การดำเนินการของผู้ใช้ต้องใช้
เมื่อใดก็ตามที่ผู้ใช้ดำเนินการที่ต้องใช้ขอบเขตเพิ่มเติม ให้เรียกใช้ AuthorizationClient.authorize()
เช่น หากผู้ใช้ดำเนินการ ที่ต้องเข้าถึงพื้นที่เก็บข้อมูลของแอปไดรฟ์ ให้ทำดังนี้
Kotlin
val requestedScopes: List<Scope> = listOf(DriveScopes.DRIVE_FILE) val authorizationRequest = AuthorizationRequest.builder() .setRequestedScopes(requestedScopes) .build() Identity.getAuthorizationClient(activity) .authorize(authorizationRequestBuilder.build()) .addOnSuccessListener { authorizationResult -> if (authorizationResult.hasResolution()) { val pendingIntent = authorizationResult.pendingIntent // Access needs to be granted by the user startAuthorizationIntent.launchIntentSenderRequest.Builder(pendingIntent!!.intentSender).build() } else { // Access was previously granted, continue with user action saveToDriveAppFolder(authorizationResult); } } .addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }
Java
List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE); AuthorizationRequest authorizationRequest = AuthorizationRequest.builder() .setRequestedScopes(requestedScopes) .build(); Identity.getAuthorizationClient(activity) .authorize(authorizationRequest) .addOnSuccessListener(authorizationResult -> { if (authorizationResult.hasResolution()) { // Access needs to be granted by the user startAuthorizationIntent.launch( new IntentSenderRequest.Builder( authorizationResult.getPendingIntent().getIntentSender() ).build() ); } else { // Access was previously granted, continue with user action saveToDriveAppFolder(authorizationResult); } }) .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));
เมื่อกำหนด ActivityResultLauncher
ให้จัดการคำตอบตามที่แสดงใน ข้อมูลโค้ดต่อไปนี้ ซึ่งเราจะถือว่าดำเนินการใน Fragment โค้ดจะตรวจสอบ ว่าได้รับสิทธิ์ที่จำเป็นเรียบร้อยแล้ว จากนั้นจึงดำเนินการ การกระทำของผู้ใช้
Kotlin
private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest> override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? { // ... startAuthorizationIntent = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult -> try { // extract the result val authorizationResult = Identity.getAuthorizationClient(requireContext()) .getAuthorizationResultFromIntent(activityResult.data) // continue with user action saveToDriveAppFolder(authorizationResult); } catch (ApiException e) { // log exception } } }
Java
private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent; @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // ... startAuthorizationIntent = registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), activityResult -> { try { // extract the result AuthorizationResult authorizationResult = Identity.getAuthorizationClient(requireActivity()) .getAuthorizationResultFromIntent(activityResult.getData()); // continue with user action saveToDriveAppFolder(authorizationResult); } catch (ApiException e) { // log exception } }); }
หากเข้าถึง Google API ในฝั่งเซิร์ฟเวอร์ ให้เรียกใช้เมธอด getServerAuthCode()
จาก AuthorizationResult
เพื่อรับรหัสการให้สิทธิ์ซึ่งคุณจะส่งไปยังแบ็กเอนด์เพื่อแลกเป็นโทเค็นเพื่อการเข้าถึงและโทเค็นเพื่อการรีเฟรช ดูข้อมูลเพิ่มเติมได้ที่รักษาสิทธิ์เข้าถึงข้อมูลของผู้ใช้ต่อไป
เพิกถอนสิทธิ์เข้าถึงข้อมูลหรือทรัพยากรของผู้ใช้
หากต้องการเพิกถอนสิทธิ์เข้าถึงที่ให้ไว้ก่อนหน้านี้ ให้โทรหา AuthorizationClient.revokeAccess()
ตัวอย่างเช่น หากผู้ใช้กำลังนำบัญชีออกจากแอปของคุณ และก่อนหน้านี้แอปของคุณได้รับสิทธิ์เข้าถึง DriveScopes.DRIVE_FILE
ให้ใช้โค้ดต่อไปนี้เพื่อเพิกถอนสิทธิ์เข้าถึง
Kotlin
val requestedScopes: MutableList<Scope> = mutableListOf(DriveScopes.DRIVE_FILE) RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder() .setAccount(account) .setScopes(requestedScopes) .build() Identity.getAuthorizationClient(activity) .revokeAccess(revokeAccessRequest) .addOnSuccessListener { Log.i(TAG, "Successfully revoked access") } .addOnFailureListener { e -> Log.e(TAG, "Failed to revoke access", e) }
Java
List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE); RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder() .setAccount(account) .setScopes(requestedScopes) .build(); Identity.getAuthorizationClient(activity) .revokeAccess(revokeAccessRequest) .addOnSuccessListener(unused -> Log.i(TAG, "Successfully revoked access")) .addOnFailureListener(e -> Log.e(TAG, "Failed to revoke access", e));
ล้างแคชโทเค็น
ระบบจะแคชโทเค็นเพื่อการเข้าถึง OAuth ไว้ในเครื่องเมื่อได้รับจากเซิร์ฟเวอร์ ซึ่งจะช่วยเพิ่มความเร็วในการเข้าถึงและลดการเรียกใช้เครือข่าย ระบบจะลบโทเค็นเหล่านี้ออกจากแคชโดยอัตโนมัติเมื่อหมดอายุ แต่โทเค็นอาจใช้ไม่ได้เนื่องจากสาเหตุอื่นๆ ด้วย หากได้รับ IllegalStateException
เมื่อใช้โทเค็น ให้ล้างแคชในเครื่องเพื่อให้แน่ใจว่าคำขอการให้สิทธิ์ครั้งถัดไปสำหรับโทเค็นเพื่อการเข้าถึงจะไปที่เซิร์ฟเวอร์ OAuth ข้อมูลโค้ดต่อไปนี้จะนำ invalidAccessToken
ออกจาก แคชในเครื่อง
Kotlin
Identity.getAuthorizationClient(activity) .clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build()) .addOnSuccessListener { Log.i(TAG, "Successfully removed the token from the cache") } .addOnFailureListener{ e -> Log.e(TAG, "Failed to clear token", e) }
Java
Identity.getAuthorizationClient(activity) .clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build()) .addOnSuccessListener(unused -> Log.i(TAG, "Successfully removed the token from the cache")) .addOnFailureListener(e -> Log.e(TAG, "Failed to clear the token cache", e));
รับข้อมูลผู้ใช้ระหว่างการให้สิทธิ์
การตอบกลับการให้สิทธิ์ไม่มีข้อมูลเกี่ยวกับบัญชีผู้ใช้ที่ใช้ แต่มีเพียงโทเค็นสำหรับขอบเขตที่ขอ ตัวอย่างเช่น การตอบกลับสำหรับการขอโทเค็นการเข้าถึงเพื่อเข้าถึง Google ไดรฟ์ของ ผู้ใช้จะไม่เปิดเผยข้อมูลระบุตัวตนของบัญชีที่ผู้ใช้เลือก แม้ว่าจะใช้เพื่อเข้าถึงไฟล์ในไดรฟ์ของผู้ใช้ได้ก็ตาม หากต้องการดูข้อมูล เช่น ชื่อหรืออีเมลของผู้ใช้ คุณมีตัวเลือกต่อไปนี้
ลงชื่อเข้าใช้ผู้ใช้ด้วยบัญชี Google โดยใช้ API ของเครื่องมือจัดการข้อมูลเข้าสู่ระบบก่อนขอการให้สิทธิ์ การตอบกลับการตรวจสอบสิทธิ์ จากเครื่องมือจัดการข้อมูลเข้าสู่ระบบมีข้อมูลผู้ใช้ เช่น อีเมล และยังตั้งค่าบัญชีเริ่มต้นของแอปเป็นบัญชีที่เลือกด้วย หาก จำเป็น คุณสามารถติดตามบัญชีนี้ในแอปได้ คำขอการให้สิทธิ์ในภายหลัง จะใช้บัญชีเป็นค่าเริ่มต้นและข้ามขั้นตอนการเลือกบัญชีในโฟลว์การให้สิทธิ์ หากต้องการใช้บัญชีอื่นเพื่อให้สิทธิ์ โปรดดู การให้สิทธิ์จากบัญชีที่ไม่ใช่บัญชีเริ่มต้น
ในคำขอการให้สิทธิ์ นอกเหนือจากขอบเขตที่คุณต้องการ (เช่น
Drive scope
) ให้ขอขอบเขตuserinfo
,profile
และopenid
หลังจากได้รับโทเค็นเพื่อการเข้าถึงแล้ว ให้รับข้อมูลผู้ใช้โดยส่งGET
คำขอ HTTP ไปยังปลายทาง OAuth userinfo (https://www.googleapis.com/oauth2/v3/userinfo) โดยใช้ไลบรารี HTTP ที่ต้องการ และใส่โทเค็นเพื่อการเข้าถึงที่คุณได้รับในส่วนหัว ซึ่งเทียบเท่ากับคำสั่งcurl
ต่อไปนี้curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"
การตอบกลับคือ
UserInfo
ซึ่งจำกัดไว้เฉพาะขอบเขตที่ ขอไว้ และจัดรูปแบบเป็น JSON
การให้สิทธิ์จากบัญชีที่ไม่ใช่บัญชีเริ่มต้น
หากคุณใช้เครื่องมือจัดการข้อมูลเข้าสู่ระบบเพื่อตรวจสอบสิทธิ์และเรียกใช้ AuthorizationClient.authorize()
ระบบจะตั้งค่าบัญชีเริ่มต้นของแอปเป็นบัญชีที่ผู้ใช้เลือก ซึ่งหมายความว่าการเรียกใช้เพื่อ การให้สิทธิ์ในภายหลังจะใช้บัญชีเริ่มต้นนี้ หากต้องการบังคับให้แสดงตัวเลือกบัญชี ให้ นำผู้ใช้ออกจากระบบแอปโดยใช้ API clearCredentialState()
จาก เครื่องมือจัดการข้อมูลเข้าสู่ระบบ
คงสิทธิ์เข้าถึงข้อมูลของผู้ใช้ต่อไป
หากต้องการเข้าถึงข้อมูลของผู้ใช้จากแอป ให้เรียกใช้ AuthorizationClient.authorize()
1 ครั้ง ในเซสชันต่อๆ ไป และตราบใดที่ผู้ใช้ยังไม่ได้นำสิทธิ์ที่ให้ไว้ ออก ให้เรียกใช้เมธอดเดียวกันเพื่อรับโทเค็นเพื่อการเข้าถึงเพื่อให้บรรลุวัตถุประสงค์โดยไม่ต้องมีการโต้ตอบจากผู้ใช้ ในทางกลับกัน หากคุณต้องการเข้าถึงข้อมูลของผู้ใช้ใน โหมดออฟไลน์จากเซิร์ฟเวอร์แบ็กเอนด์ คุณจะต้องขอโทเค็นประเภทอื่นที่เรียกว่า "โทเค็นการรีเฟรช"
เราออกแบบโทเค็นเพื่อการเข้าถึงให้มีอายุการใช้งานสั้นๆ และมีอายุ 1 ชั่วโมงโดยเจตนา หากมีการสกัดกั้นหรือบุกรุกโทเค็นเพื่อการเข้าถึง ช่วงเวลาที่โทเค็นมีอายุการใช้งานแบบจำกัดจะช่วยลดโอกาสในการนำไปใช้ในทางที่ผิด หลังจากโทเค็นหมดอายุ โทเค็นจะ ใช้ไม่ได้ และเซิร์ฟเวอร์ทรัพยากรจะปฏิเสธ ความพยายามใดๆ ที่จะใช้โทเค็น เนื่องจากโทเค็นเพื่อการเข้าถึงมีอายุสั้น เซิร์ฟเวอร์จึงใช้โทเค็นการรีเฟรชเพื่อ รักษาการเข้าถึงข้อมูลของผู้ใช้ต่อไป โทเค็นการรีเฟรชคือโทเค็นที่มีอายุการใช้งานยาวนาน ซึ่งไคลเอ็นต์ใช้เพื่อขอโทเค็นเพื่อการเข้าถึงที่มีอายุการใช้งานสั้น จากเซิร์ฟเวอร์การให้สิทธิ์ เมื่อโทเค็นเพื่อการเข้าถึงเก่าหมดอายุ โดยไม่ต้องมีการโต้ตอบจากผู้ใช้
หากต้องการรับโทเค็นการรีเฟรช คุณจะต้องรับรหัสการให้สิทธิ์ (หรือรหัสการให้สิทธิ์) ในขั้นตอนการให้สิทธิ์ในแอปก่อนโดยขอ "สิทธิ์เข้าถึงแบบออฟไลน์" จากนั้นแลกรหัสการให้สิทธิ์เป็นโทเค็นการรีเฟรชในเซิร์ฟเวอร์ คุณต้องจัดเก็บโทเค็นการรีเฟรชที่ใช้ได้นานไว้ในเซิร์ฟเวอร์อย่างปลอดภัย เนื่องจากโทเค็นเหล่านี้ใช้ซ้ำๆ เพื่อขอโทเค็นเพื่อการเข้าถึงใหม่ได้ ดังนั้น เราจึงไม่แนะนำอย่างยิ่งให้จัดเก็บโทเค็นการรีเฟรชไว้ในอุปกรณ์เนื่องจากข้อกังวลด้านความปลอดภัย แต่ควรจัดเก็บไว้ในเซิร์ฟเวอร์แบ็กเอนด์ของแอป ซึ่งเป็นที่ที่ใช้แลกเปลี่ยนโทเค็นเพื่อเข้าถึง
หลังจากส่งรหัสการให้สิทธิ์ไปยังเซิร์ฟเวอร์แบ็กเอนด์ของแอปแล้ว คุณสามารถแลกรหัสดังกล่าว เป็นโทเค็นเพื่อการเข้าถึงที่มีอายุสั้นในเซิร์ฟเวอร์และโทเค็นการรีเฟรชที่มีอายุยาวได้โดย ทำตามขั้นตอนในคู่มือการให้สิทธิ์บัญชี การแลกเปลี่ยนนี้ควรเกิดขึ้นในแบ็กเอนด์ของแอปเท่านั้น
Kotlin
// Ask for offline access during the first authorization request val authorizationRequest = AuthorizationRequest.builder() .setRequestedScopes(requestedScopes) .requestOfflineAccess(serverClientId) .build() Identity.getAuthorizationClient(activity) .authorize(authorizationRequest) .addOnSuccessListener { authorizationResult -> startAuthorizationIntent.launchIntentSenderRequest.Builder( pendingIntent!!.intentSender ).build() } .addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }
Java
// Ask for offline access during the first authorization request AuthorizationRequest authorizationRequest = AuthorizationRequest.builder() .setRequestedScopes(requestedScopes) .requestOfflineAccess(serverClientId) .build(); Identity.getAuthorizationClient(getContext()) .authorize(authorizationRequest) .addOnSuccessListener(authorizationResult -> { startAuthorizationIntent.launch( new IntentSenderRequest.Builder( authorizationResult.getPendingIntent().getIntentSender() ).build() ); }) .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize"));
ข้อมูลโค้ดต่อไปนี้ถือว่าการให้สิทธิ์เริ่มต้นจาก Fragment
Kotlin
private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest> override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? { // ... startAuthorizationIntent = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult -> try { val authorizationResult = Identity.getAuthorizationClient(requireContext()) .getAuthorizationResultFromIntent(activityResult.data) // short-lived access token accessToken = authorizationResult.accessToken // store the authorization code used for getting a refresh token safely to your app's backend server val authCode: String = authorizationResult.serverAuthCode storeAuthCodeSafely(authCode) } catch (e: ApiException) { // log exception } } }
Java
private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent; @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // ... startAuthorizationIntent = registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), activityResult -> { try { AuthorizationResult authorizationResult = Identity.getAuthorizationClient(requireActivity()) .getAuthorizationResultFromIntent(activityResult.getData()); // short-lived access token accessToken = authorizationResult.getAccessToken(); // store the authorization code used for getting a refresh token safely to your app's backend server String authCode = authorizationResult.getServerAuthCode() storeAuthCodeSafely(authCode); } catch (ApiException e) { // log exception } }); }