134 lines
6.1 KiB
Python
134 lines
6.1 KiB
Python
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)
|