项目初次提交
This commit is contained in:
7
utils/wx/__init__.py
Normal file
7
utils/wx/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/3/15 20:18
|
||||
# @File : __init__.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
136
utils/wx/oauth.py
Normal file
136
utils/wx/oauth.py
Normal file
@ -0,0 +1,136 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/3/15 20:44
|
||||
# @File : oauth.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
import requests
|
||||
from core.logger import logger
|
||||
from utils.cache import Cache
|
||||
from utils.wx.wx_access_token import WxAccessToken
|
||||
from redis.asyncio import Redis
|
||||
|
||||
|
||||
class WXOAuth:
|
||||
|
||||
def __init__(self, rd: Redis, index: int = 0):
|
||||
"""
|
||||
初始化微信认证
|
||||
:param index: 选择小程序,0:微信服务端
|
||||
"""
|
||||
# 重试次数
|
||||
self.retry_count = 5
|
||||
self.appid = None
|
||||
self.secret = None
|
||||
self.rd = rd
|
||||
self.tab_name = None
|
||||
if index == 0:
|
||||
self.tab_name = "wx_server"
|
||||
|
||||
async def __get_settings(self, retry: int = 3):
|
||||
"""
|
||||
获取配置信息
|
||||
"""
|
||||
if not self.tab_name:
|
||||
logger.error(f"请选择认证的微信平台")
|
||||
wx_config = await Cache(self.rd).get_tab_name(self.tab_name, retry)
|
||||
self.appid = wx_config.get("wx_server_app_id")
|
||||
self.secret = wx_config.get("wx_server_app_secret")
|
||||
|
||||
async def get_code2session(self, code: str) -> dict:
|
||||
"""
|
||||
通过微信用户临时登录凭证 code 进行校验,获取用户openid,与 session 信息
|
||||
|
||||
官方文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
|
||||
:param code: 登录时获取的 code
|
||||
:return: 正确:{'session_key': 'F8/5LZrdtINYLPEdUJgXXQ==', 'openid': 'okLlC5Kcv7DH2J99dz-Z2FwJeEeU'}
|
||||
:return: 报错:{'errcode': 40029, 'errmsg': 'invalid code, rid: 62308e5d-0b0b697e-1db652eb'}
|
||||
"""
|
||||
if not self.appid or not self.secret:
|
||||
await self.__get_settings()
|
||||
api = "https://api.weixin.qq.com/sns/jscode2session"
|
||||
params = {
|
||||
"appid": self.appid,
|
||||
"secret": self.secret,
|
||||
"js_code": code,
|
||||
"grant_type": "authorization_code"
|
||||
}
|
||||
response = requests.get(url=api, params=params)
|
||||
result = response.json()
|
||||
if "openid" not in result:
|
||||
logger.error(f"微信校验失败:{result}, code:{code}")
|
||||
else:
|
||||
logger.info(f"微信校验成功:{result}, code:{code}")
|
||||
return result
|
||||
|
||||
async def get_phone_number(self, code: str):
|
||||
"""
|
||||
获取微信用户手机号
|
||||
|
||||
官方文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/phonenumber/phonenumber.getPhoneNumber.html
|
||||
:param code: 动态令牌。可通过动态令牌换取用户手机号。
|
||||
:return: 成功:{'errcode': 0, 'errmsg': 'ok', 'phone_info': {'phoneNumber': '15093430559'
|
||||
, 'purePhoneNumber': '15093430559', 'countryCode': '86', 'watermark': {'timestamp': 1647355468, 'appid': 'wx069c452f9a733df1'}}}
|
||||
失败:{'errcode': 40001, 'errmsg': 'invalid credential, access_token is invalid or not latest rid: 62690257-2894b530-58c6fcf3'}
|
||||
"""
|
||||
if not self.appid or not self.secret:
|
||||
await self.__get_settings()
|
||||
api = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"
|
||||
at = WxAccessToken(self.appid, self.secret, self.rd)
|
||||
access_token = await at.get()
|
||||
if not access_token.get("status", False):
|
||||
result = {'errcode': 40001, 'errmsg': '获取微信令牌失败'}
|
||||
# print(result)
|
||||
logger.error(f"获取微信用户手机号失败:{result}")
|
||||
return result
|
||||
params = {
|
||||
"access_token": access_token.get("token"),
|
||||
}
|
||||
data = {
|
||||
"code": code,
|
||||
}
|
||||
response = requests.post(url=api, params=params, json=data)
|
||||
result = response.json()
|
||||
if result.get("errcode", 0) == 0:
|
||||
# print("获取微信用户手机号成功", result)
|
||||
logger.info(f"获取微信用户手机号成功:{result}, code:{code}")
|
||||
else:
|
||||
# print("获取微信用户手机号失败", result)
|
||||
logger.error(f"获取微信用户手机号失败:{result}, code:{code}")
|
||||
if result.get("errcode", 0) == 40001:
|
||||
await at.update()
|
||||
if self.retry_count > 0:
|
||||
logger.error(f"重试获取微信手机号,重试剩余次数, {self.retry_count}")
|
||||
self.retry_count -= 1
|
||||
return await self.get_phone_number(code)
|
||||
return result
|
||||
|
||||
async def parsing_phone_number(self, code: str):
|
||||
"""
|
||||
解析微信用户手机号
|
||||
:param code: 动态令牌。可通过动态令牌换取用户手机号。
|
||||
:return:
|
||||
"""
|
||||
result = await self.get_phone_number(code)
|
||||
if result.get("errcode") == 0:
|
||||
phone_info = result["phone_info"]
|
||||
assert isinstance(phone_info, dict)
|
||||
return phone_info["phoneNumber"]
|
||||
return None
|
||||
|
||||
async def parsing_openid(self, code: str):
|
||||
"""
|
||||
解析openid
|
||||
:param code: 动态令牌。可通过动态令牌换取用户手机号。
|
||||
:return: openid | None
|
||||
"""
|
||||
result = await self.get_code2session(code)
|
||||
if "openid" in result:
|
||||
return result["openid"]
|
||||
return None
|
||||
|
||||
|
||||
# if __name__ == '__main__':
|
||||
# WXOAuth().get_code2session("063mPDFa1v16PC0yqhGa1uQ86t4mPDFV")
|
61
utils/wx/wx_access_token.py
Normal file
61
utils/wx/wx_access_token.py
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/11/27 18:37
|
||||
# @File : wx_access_token.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 获取小程序全局唯一后台接口调用凭据
|
||||
|
||||
import requests
|
||||
from redis.asyncio import Redis
|
||||
from core.logger import logger
|
||||
|
||||
|
||||
class WxAccessToken:
|
||||
"""
|
||||
获取到的access_token存储在redis数据库中
|
||||
|
||||
获取小程序全局唯一后台接口调用凭据(access_token)。调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存。
|
||||
|
||||
官方文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
|
||||
"""
|
||||
|
||||
def __init__(self, appid: str, secret: str, redis: Redis, grant_type: str = "client_credential", *args, **kwargs):
|
||||
self.__url = "https://api.weixin.qq.com/cgi-bin/token"
|
||||
self.__method = "get"
|
||||
self.appidKey = f"{appid}_access_token"
|
||||
self.redis = redis
|
||||
self.params = {
|
||||
"appid": appid,
|
||||
"secret": secret,
|
||||
"grant_type": grant_type
|
||||
}
|
||||
|
||||
async def get(self) -> dict:
|
||||
"""
|
||||
获取小程序access_token
|
||||
"""
|
||||
token = await self.redis.get(self.appidKey)
|
||||
if not token:
|
||||
return await self.update()
|
||||
return {"status": True, "token": token}
|
||||
|
||||
async def update(self) -> dict:
|
||||
"""
|
||||
更新小程序access_token
|
||||
"""
|
||||
print("开始更新 access_token")
|
||||
method = getattr(requests, self.__method)
|
||||
response = method(url=self.__url, params=self.params)
|
||||
result = response.json()
|
||||
|
||||
if result.get("errcode", "0") != "0":
|
||||
print("获取access_token失败", result)
|
||||
logger.error(f"获取access_token失败:{result}")
|
||||
return {"status": False, "token": None}
|
||||
|
||||
print("成功获取到", result)
|
||||
await self.redis.set(self.appidKey, result.get("access_token"), ex=2000)
|
||||
logger.info(f"获取access_token成功:{result}")
|
||||
|
||||
return {"status": True, "token": result.get("access_token")}
|
Reference in New Issue
Block a user