import threading from datetime import datetime, timedelta from functools import wraps from pathlib import Path from urllib.parse import urlencode from zoneinfo import ZoneInfo import orjson import pydash import requests from Crypto.PublicKey import RSA from django.conf import settings from django.http import HttpResponseRedirect from jwt import JWT, jwk_from_pem from jwt.utils import get_int_from_datetime from rest_framework.decorators import action from rest_framework.request import Request from rest_framework.viewsets import ViewSet from wechat_token.token数据库 import weitoken def sync_lock_线程锁(lock): def decorator(fn): @wraps(fn) def wrapper(*args, **kwargs): with lock: return fn(*args, **kwargs) return wrapper return decorator class AllowMyAppRedirect(HttpResponseRedirect): allowed_schemes = ["http", "https", "myapp"] class 接收微信的code回调ViewSets(ViewSet): lock = threading.Lock() private_key_pem_私钥 = Path(settings.BASE_DIR).joinpath("django_jwt_token/private.pem").read_bytes() def jwt_生成token(self, openid, uniond): # 读取私钥的 bytes字符串 # 创建一个 JWT对象 jwt对象实例 = JWT() # 创建对象 # 创建 payload message = { "openid": openid, "uniond": uniond, 'iat': get_int_from_datetime(datetime.now(tz=ZoneInfo(key="utc"))), # 生成jwt-token的时间 'exp': get_int_from_datetime(datetime.now(tz=ZoneInfo(key="utc")) + timedelta(hours=1)), # 多长时间过期 } # 获取私钥对象 (并且要私钥的密码) 私钥对象 = RSA.import_key(extern_key=self.private_key_pem_私钥, passphrase=settings.SECRET_KEY).export_key() signing_key = jwk_from_pem(pem_content=私钥对象, ) token = jwt对象实例.encode(payload=message, key=signing_key, alg="RS256") return token def get网站授权access_token(self, code, state): uri = "https://api.weixin.qq.com/sns/oauth2/access_token" postData = requests.post(url=uri, params={ "appid": "wx6c9d4541e0285d02", "secret": "bb87178b80e23f2fc96b688577dafca4", "code": code, "grant_type": "authorization_code" }).json() access_token = pydash.get(obj=postData, path="access_token") expires_in = pydash.get(obj=postData, path="expires_in") refresh_token = pydash.get(obj=postData, path="refresh_token") openid = pydash.get(obj=postData, path="openid") scope = pydash.get(obj=postData, path="scope") unionid = pydash.get(obj=postData, path="unionid") return postData def get获取用户个人信息(self, code, state): uri = f"https://api.weixin.qq.com/sns/userinfo" token, is创建 = weitoken.objects.update_or_create(token_name="web_token") if is创建: wx_access_token_dict = self.get网站授权access_token(code=code, state=state) token.token_value = pydash.get(obj=wx_access_token_dict, path="access_token") token.save() access_token = pydash.get(obj=wx_access_token_dict, path="access_token") openid = pydash.get(obj=wx_access_token_dict, path="openid") getData = requests.get(url=uri, params={"access_token": access_token, "openid": openid}) res = orjson.loads(getData.content.decode(encoding="utf-8")) return res else: time创建时间 = token.updateTime.timestamp() # 更新时间的时间戳 current_时间戳 = get_int_from_datetime(value=datetime.now()) if current_时间戳 - time创建时间 > 3600: # 时间过期时,需要重新请求 拿到新的 access_token wx_access_token_dict = self.get网站授权access_token(code=code, state=state) # 更新token的值 token.token_value = pydash.get(obj=wx_access_token_dict, path="access_token") token.save() access_token = pydash.get(obj=wx_access_token_dict, path="access_token") openid = pydash.get(obj=wx_access_token_dict, path="openid") getData = requests.get(url=uri, params={"access_token": access_token, "openid": openid}) res = orjson.loads(getData.content.decode(encoding="utf-8")) return res else: wx_access_token_dict = self.get网站授权access_token(code=code, state=state) # 更新token的值 token.token_value = pydash.get(obj=wx_access_token_dict, path="access_token") token.save() access_token = pydash.get(obj=wx_access_token_dict, path="access_token") openid = pydash.get(obj=wx_access_token_dict, path="openid") getData = requests.get(url=uri, params={"access_token": access_token, "openid": openid}) res = orjson.loads(getData.content.decode(encoding="utf-8")) return res @action(detail=False, methods=["get", ], url_path="get_code", ) # @sync_lock_线程锁( lock=lock ) def get_code(self, request: Request): """ 前端,带着code的get请求,在这里拿到code,获取登陆用户的信息, 重点: 通过回调,前端,(处理业务逻辑) 由于我这里使用的是Mac桌面版本. :param request: :return: """ code = request.query_params.get(key="code", default=None) state = request.data.get("state", None) user_info_用户信息 = self.get获取用户个人信息(code=code, state=state) openid = pydash.get(obj=user_info_用户信息, path="openid") unionid = pydash.get(obj=user_info_用户信息, path="unionid") print("为什么没有值了呢", openid, unionid) token = self.jwt_生成token(openid=openid, uniond=unionid) setData = pydash.set_(obj=user_info_用户信息, path="token", value=token) query_string = urlencode(setData, ) uri = f"myapp://open/home?{query_string}" return AllowMyAppRedirect(redirect_to=uri)