|
|
@@ -0,0 +1,248 @@
|
|
|
+#python
|
|
|
+#unity/日常积累
|
|
|
+
|
|
|
+谷歌game service v2 版本,会返回授权码:
|
|
|
+auth_code (str): Google授权码(以4/开头的字符串)
|
|
|
+
|
|
|
+在执行相关逻辑之前,确保安装了,python库中,谷歌认证相关:
|
|
|
+
|
|
|
+``` shell
|
|
|
+ pip install google-auth requests
|
|
|
+```
|
|
|
+
|
|
|
+另外,还需要准备的材料:
|
|
|
+
|
|
|
+![[Pasted image 20250620103522.png]]
|
|
|
+
|
|
|
+## google_token.py
|
|
|
+
|
|
|
+``` py
|
|
|
+import requests
|
|
|
+import json
|
|
|
+import sys
|
|
|
+from google.oauth2 import id_token
|
|
|
+from google.auth.transport import requests as google_requests
|
|
|
+
|
|
|
+def exchange_auth_code(auth_code, client_id, client_secret):
|
|
|
+ """
|
|
|
+ 用授权码交换获取访问令牌和ID令牌
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ auth_code (str): Google授权码(以4/开头的字符串)
|
|
|
+ client_id (str): 你的Google OAuth客户端ID
|
|
|
+ client_secret (str): 你的Google OAuth客户端密钥
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ dict: 包含tokens和用户信息的字典
|
|
|
+ """
|
|
|
+ token_url = "https://oauth2.googleapis.com/token"
|
|
|
+ payload = {
|
|
|
+ "code": auth_code,
|
|
|
+ "client_id": client_id,
|
|
|
+ "client_secret": client_secret,
|
|
|
+ "redirect_uri": "http://localhost",
|
|
|
+ "grant_type": "authorization_code"
|
|
|
+ }
|
|
|
+
|
|
|
+ print(f"正在交换授权码...")
|
|
|
+ try:
|
|
|
+ response = requests.post(token_url, data=payload)
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ token_data = response.json()
|
|
|
+ print(f"令牌交换成功!")
|
|
|
+
|
|
|
+ # 获取各种令牌
|
|
|
+ access_token = token_data.get("access_token")
|
|
|
+ id_token_str = token_data.get("id_token")
|
|
|
+ refresh_token = token_data.get("refresh_token")
|
|
|
+
|
|
|
+ # 安全地打印令牌信息,避免NoneType错误
|
|
|
+ if access_token:
|
|
|
+ print(f"\n访问令牌(Access Token): {access_token[:20]}...(已截断)")
|
|
|
+ else:
|
|
|
+ print("\n访问令牌(Access Token): None")
|
|
|
+
|
|
|
+ if id_token_str:
|
|
|
+ print(f"ID令牌(ID Token): {id_token_str[:20]}...(已截断)")
|
|
|
+ else:
|
|
|
+ print("ID令牌(ID Token): None")
|
|
|
+
|
|
|
+ if refresh_token:
|
|
|
+ print(f"刷新令牌(Refresh Token): {refresh_token[:20]}...(已截断)")
|
|
|
+ else:
|
|
|
+ print("刷新令牌(Refresh Token): None")
|
|
|
+
|
|
|
+ # 保存完整令牌到文件以备后用
|
|
|
+ try:
|
|
|
+ with open("tokens.json", "w") as f:
|
|
|
+ json.dump(token_data, f, indent=2)
|
|
|
+ print(f"\n已将完整令牌保存到 tokens.json 文件")
|
|
|
+ except Exception as e:
|
|
|
+ print(f"保存令牌到文件失败: {e}")
|
|
|
+
|
|
|
+ # 验证ID令牌并获取用户信息
|
|
|
+ if id_token_str:
|
|
|
+ try:
|
|
|
+ user_info = verify_id_token(id_token_str, client_id)
|
|
|
+ return {
|
|
|
+ "tokens": token_data,
|
|
|
+ "user_info": user_info
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ print(f"ID令牌验证失败: {e}")
|
|
|
+ return {
|
|
|
+ "tokens": token_data,
|
|
|
+ "user_info": None
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ print("响应中没有ID令牌")
|
|
|
+ return {
|
|
|
+ "tokens": token_data,
|
|
|
+ "user_info": None
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ print(f"错误: {response.status_code}")
|
|
|
+ print(response.text)
|
|
|
+ return None
|
|
|
+ except Exception as e:
|
|
|
+ print(f"请求过程中发生错误: {e}")
|
|
|
+ return None
|
|
|
+
|
|
|
+def verify_id_token(id_token_str, client_id):
|
|
|
+ """
|
|
|
+ 验证ID令牌并返回用户信息
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ id_token_str (str): 要验证的ID令牌
|
|
|
+ client_id (str): 你的Google OAuth客户端ID
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ dict: 用户信息字典
|
|
|
+ """
|
|
|
+ print(f"\n正在验证ID令牌...")
|
|
|
+ try:
|
|
|
+ idinfo = id_token.verify_oauth2_token(
|
|
|
+ id_token_str,
|
|
|
+ google_requests.Request(),
|
|
|
+ client_id
|
|
|
+ )
|
|
|
+
|
|
|
+ # 验证发行者
|
|
|
+ if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
|
|
|
+ raise ValueError('错误的令牌发行者.')
|
|
|
+
|
|
|
+ print(f"ID令牌验证成功!")
|
|
|
+ return idinfo
|
|
|
+ except Exception as e:
|
|
|
+ print(f"ID令牌验证失败: {e}")
|
|
|
+ raise
|
|
|
+
|
|
|
+def get_user_info_from_token(access_token):
|
|
|
+ """
|
|
|
+ 使用访问令牌获取用户信息
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ access_token (str): 访问令牌
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ dict: 用户信息字典
|
|
|
+ """
|
|
|
+ if not access_token:
|
|
|
+ print("访问令牌为空,无法获取用户信息")
|
|
|
+ return None
|
|
|
+
|
|
|
+ print(f"\n使用访问令牌获取用户信息...")
|
|
|
+
|
|
|
+ userinfo_url = "https://openidconnect.googleapis.com/v1/userinfo"
|
|
|
+ headers = {
|
|
|
+ "Authorization": f"Bearer {access_token}"
|
|
|
+ }
|
|
|
+
|
|
|
+ try:
|
|
|
+ response = requests.get(userinfo_url, headers=headers)
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ user_info = response.json()
|
|
|
+ print(f"获取用户信息成功!")
|
|
|
+ return user_info
|
|
|
+ else:
|
|
|
+ print(f"获取用户信息失败: {response.status_code}")
|
|
|
+ print(response.text)
|
|
|
+ return None
|
|
|
+ except Exception as e:
|
|
|
+ print(f"获取用户信息过程中发生错误: {e}")
|
|
|
+ return None
|
|
|
+
|
|
|
+def main():
|
|
|
+ """主函数,处理命令行参数并执行令牌交换"""
|
|
|
+ try:
|
|
|
+ if len(sys.argv) < 4:
|
|
|
+ print("用法: python exchange_code.py <授权码> <客户端ID> <客户端密钥>")
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ auth_code = sys.argv[1]
|
|
|
+ client_id = sys.argv[2]
|
|
|
+ client_secret = sys.argv[3]
|
|
|
+
|
|
|
+ # 安全地截断显示参数
|
|
|
+ auth_code_display = auth_code[:10] + "...(已截断)" if len(auth_code) > 10 else auth_code
|
|
|
+ client_id_display = client_id[:15] + "...(已截断)" if len(client_id) > 15 else client_id
|
|
|
+ client_secret_display = client_secret[:5] + "...(已截断)" if len(client_secret) > 5 else client_secret
|
|
|
+
|
|
|
+ print("-" * 50)
|
|
|
+ print("Google授权码交换工具")
|
|
|
+ print("-" * 50)
|
|
|
+ print(f"授权码: {auth_code_display}")
|
|
|
+ print(f"客户端ID: {client_id_display}")
|
|
|
+ print(f"客户端密钥: {client_secret_display}")
|
|
|
+ print("-" * 50)
|
|
|
+
|
|
|
+ result = exchange_auth_code(auth_code, client_id, client_secret)
|
|
|
+
|
|
|
+ if result:
|
|
|
+ tokens = result.get("tokens", {})
|
|
|
+ user_info = result.get("user_info")
|
|
|
+
|
|
|
+ # 如果ID令牌验证失败,尝试使用访问令牌获取用户信息
|
|
|
+ if not user_info and tokens.get("access_token"):
|
|
|
+ user_info = get_user_info_from_token(tokens["access_token"])
|
|
|
+
|
|
|
+ if user_info:
|
|
|
+ print("\n----- 用户信息 -----")
|
|
|
+ print(f"用户ID: {user_info.get('sub') or user_info.get('id', 'N/A')}")
|
|
|
+ print(f"电子邮件: {user_info.get('email', 'N/A')}")
|
|
|
+ print(f"姓名: {user_info.get('name', 'N/A')}")
|
|
|
+ print(f"头像: {user_info.get('picture', 'N/A')}")
|
|
|
+
|
|
|
+ # 保存用户信息到文件
|
|
|
+ try:
|
|
|
+ with open("user_info.json", "w") as f:
|
|
|
+ json.dump(user_info, f, indent=2)
|
|
|
+ print(f"\n已将用户信息保存到 user_info.json 文件")
|
|
|
+ except Exception as e:
|
|
|
+ print(f"保存用户信息到文件失败: {e}")
|
|
|
+ else:
|
|
|
+ print("\n无法获取用户信息")
|
|
|
+ else:
|
|
|
+ print("\n令牌交换失败")
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ print("\n处理完成!")
|
|
|
+ except Exception as e:
|
|
|
+ print(f"程序执行过程中发生错误: {e}")
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|
|
|
+```
|
|
|
+
|
|
|
+调用方式
|
|
|
+
|
|
|
+``` shell
|
|
|
+python .\google_token.py 4/0AUJR-x5Sbmm9wToj9ai84DdPRf-FaubSQieqnV0k2OWkVeqGg_XW08bGgyMDIxoTilbH3A 956550854222-pfjuen977e59l6hif1tj08bibh93tguu.apps.googleusercontent.com AIzaSyDLJnWUHh6o-F4e9N0fUsbGeS2sIoqyLr4
|
|
|
+```
|
|
|
+
|
|
|
+传参分别是:授权码、web客户端ID、web客户端密钥
|
|
|
+
|
|
|
+![[Pasted image 20250620103712.png]]
|