การตรวจสอบสิทธิ์จะระบุตัวตนของบุคคล และมักเรียกว่าการลงชื่อสมัครใช้หรือการลงชื่อเข้าใช้ของผู้ใช้ การให้สิทธิ์คือกระบวนการให้หรือปฏิเสธการเข้าถึง ข้อมูลหรือทรัพยากร เช่น แอปของคุณขอความยินยอมจากผู้ใช้เพื่อ เข้าถึง 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
}
});
}