UserController.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # @Author : Rocky
  2. # @File : UserController.py
  3. # @Time : 2024/11/27 16:35
  4. import hashlib
  5. import hmac
  6. import os
  7. import time
  8. import requests
  9. from Ansjer.config import LOGGER
  10. from django.views.generic.base import View
  11. from Model.models import WeChatMiniProgram
  12. from Object.Enums.WeChatEnum import WeChatMiniProgramAPIEnum, WeChatMiniProgramConfigEnum
  13. from Object.RedisObject import RedisObject
  14. from Object.ResponseObject import ResponseObject
  15. class EquipmentFamilyView(View):
  16. def get(self, request, *args, **kwargs):
  17. request.encoding = 'utf-8'
  18. operation = kwargs.get('operation')
  19. return self.validation(request.GET, request, operation)
  20. def post(self, request, *args, **kwargs):
  21. request.encoding = 'utf-8'
  22. operation = kwargs.get('operation')
  23. return self.validation(request.POST, request, operation)
  24. def validation(self, request_dict, request, operation):
  25. lang = request_dict.get('lang', 'cn')
  26. response = ResponseObject(lang)
  27. if operation == 'getPhoneNumber':
  28. return self.get_phone_number(request_dict, response)
  29. elif operation == 'login':
  30. return self.login(request_dict, response)
  31. else:
  32. valid = self.check_session_key(request)
  33. if not valid:
  34. return response.json(309)
  35. @classmethod
  36. def check_session_key(cls, request):
  37. """
  38. 校验 session_key 是否有效
  39. https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/checkSessionKey.html
  40. @return: bool
  41. """
  42. try:
  43. login_status = request.META.get('HTTP_AUTHORIZATION')
  44. if not login_status:
  45. return False
  46. wechat_mini_program_qs = WeChatMiniProgram.objects.filter(login_status=login_status). \
  47. values('openid', 'session_key')
  48. if not wechat_mini_program_qs.exists():
  49. return False
  50. openid = wechat_mini_program_qs[0]['openid']
  51. session_key = wechat_mini_program_qs[0]['session_key']
  52. signature = cls.generate_signature(session_key)
  53. access_token = cls.get_access_token()
  54. params = {
  55. 'openid': openid,
  56. 'access_token': access_token,
  57. 'signature': signature,
  58. 'sig_method': 'hmac_sha256'
  59. }
  60. r = requests.get(url=WeChatMiniProgramAPIEnum.checkSessionKeyAPI.value, params=params, timeout=5)
  61. result = eval(r.content)
  62. errcode = result.get('errcode')
  63. assert errcode == 0
  64. return True
  65. except Exception as e:
  66. return False
  67. @staticmethod
  68. def get_access_token():
  69. """
  70. 获取小程序全局唯一后台接口调用凭据,token有效期为7200s
  71. https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
  72. @return: access_token
  73. """
  74. try:
  75. redis_obj = RedisObject()
  76. access_token_key = WeChatMiniProgramConfigEnum.AccessTokenKey.value
  77. expires_time = redis_obj.get_ttl(access_token_key)
  78. # 如果有效时间大于十分钟,返回token,否则刷新token
  79. if expires_time > 10 * 60:
  80. access_token = redis_obj.get_data(access_token_key)
  81. return access_token
  82. else:
  83. params = {
  84. 'grant_type': 'client_credential',
  85. 'appid': WeChatMiniProgramConfigEnum.AppID.value,
  86. 'secret': WeChatMiniProgramConfigEnum.AppSecret.value
  87. }
  88. r = requests.get(url=WeChatMiniProgramAPIEnum.getAccessTokenAPI.value, params=params, timeout=5)
  89. result = eval(r.content)
  90. access_token = result.get('access_token')
  91. assert access_token
  92. expires_in = result.get('expires_in')
  93. # 保存到Redis
  94. redis_obj.set_ex_data(key=access_token_key, val=access_token, expire=expires_in)
  95. return access_token
  96. except Exception as e:
  97. LOGGER.info('微信小程序获取token异常:error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  98. return None
  99. @classmethod
  100. def get_phone_number(cls, request_dict, response):
  101. """
  102. 获取手机号码
  103. @param request_dict:
  104. @param response:
  105. @return: res
  106. """
  107. code = request_dict.get('code', None)
  108. if not code:
  109. return response.json(444)
  110. try:
  111. access_token = cls.get_access_token()
  112. url = WeChatMiniProgramAPIEnum.getPhoneNumberAPI.value.format(access_token)
  113. data = {
  114. 'code': code
  115. }
  116. r = requests.post(url=url, data=data, timeout=5)
  117. result = eval(r.content)
  118. errcode = result.get('errcode')
  119. assert errcode == 0
  120. pure_phone_number = result['phone_info']['purePhoneNumber']
  121. res = {
  122. 'pure_phone_number': pure_phone_number
  123. }
  124. return response.json(0, res)
  125. except Exception as e:
  126. LOGGER.info('微信小程序获取手机号码异常:error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  127. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  128. @classmethod
  129. def login(cls, request_dict, response):
  130. """
  131. 小程序登录
  132. https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
  133. https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
  134. @param request_dict:
  135. @param response:
  136. @return:
  137. """
  138. js_code = request_dict.get('js_code', None)
  139. if not js_code:
  140. return response.json(444)
  141. try:
  142. params = {
  143. 'js_code': js_code,
  144. 'grant_type': 'authorization_code',
  145. 'appid': WeChatMiniProgramConfigEnum.AppID.value,
  146. 'secret': WeChatMiniProgramConfigEnum.AppSecret.value
  147. }
  148. r = requests.get(url=WeChatMiniProgramAPIEnum.code2SessionAPI.value, params=params, timeout=5)
  149. result = eval(r.content)
  150. errcode = result.get('errcode')
  151. assert errcode == 0
  152. openid = result['openid']
  153. unionid = result['unionid']
  154. session_key = result['session_key']
  155. # 生成登录态
  156. login_status = cls.generate_login_status(openid, session_key)
  157. now_time = int(time.time())
  158. wechat_mini_program_qs = WeChatMiniProgram.objects.filter(openid=openid)
  159. if wechat_mini_program_qs.exists():
  160. wechat_mini_program_qs.update(
  161. login_status=login_status, session_key=session_key, updated_time=now_time
  162. )
  163. else:
  164. WeChatMiniProgram.objects.create(
  165. login_status=login_status, openid=openid, unionid=unionid, session_key=session_key,
  166. created_time=now_time, updated_time=now_time
  167. )
  168. res = {
  169. 'login_status': login_status
  170. }
  171. return response.json(0, res)
  172. except Exception as e:
  173. LOGGER.info('微信小程序登录异常:error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  174. return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  175. @staticmethod
  176. def generate_login_status(openid, session_key):
  177. """
  178. 生成登录态
  179. @param openid:
  180. @param session_key:
  181. @return: login_status
  182. """
  183. salt = os.urandom(16).hex()
  184. # 将openid, session_key和salt拼接成一个字符串
  185. to_hash = openid + session_key + salt
  186. # 使用hashlib生成SHA256哈希值
  187. hash_object = hashlib.sha256(to_hash.encode())
  188. # 获取十六进制格式的哈希值
  189. login_status = hash_object.hexdigest()
  190. return login_status
  191. @staticmethod
  192. def generate_signature(session_key):
  193. """
  194. 用户登录态签名,用session_key对空字符串签名得到的结果。即 signature = hmac_sha256(session_key, "")
  195. @param session_key:
  196. @return: signature
  197. """
  198. # 将session_key和要签名的数据(这里是空字符串)转换为字节类型
  199. session_key_bytes = session_key.encode('utf-8')
  200. data = b"" # 空字符串转换为字节类型
  201. # 创建一个新的hmac对象,使用sha256作为hash函数
  202. hmac_object = hmac.new(session_key_bytes, data, hashlib.sha256)
  203. # 获取十六进制格式的签名结果
  204. signature = hmac_object.hexdigest()
  205. return signature