django 微信网页授权登陆的实现


Posted in Python onJuly 30, 2019

一、准备工作

0x00 开发前准备

  • 服务号!!!
  • 微信认证。
  • 备案过的域名。
  • 服务器。

 0x01 手动触发dns更新

django 微信网页授权登陆的实现

0x02 配置业务域名

django 微信网页授权登陆的实现 

0x03 将服务器请求转发到本地

修改服务器的 /etc/ssh/sshd_config 加入 GatewayPorts yes

ssh -R 0.0.0.0:80:localhost:8080 user@server_host

二、微信网页授权

0x01 授权流程

用户同意授权,获取 code

想办法让用户页面跳转到微信的授权链接(比如在修饰器中进行跳转):

def get_wx_authorize_url(appid : str, state: str = None):
  if state is None:
    state = "".join([random.choice(string.ascii_letters + string.digits) for _ in range(20)])
  redirect_url = 'your callback url' # 回调链接,在这里面进行用户信息入库的操作
  response_type = 'code'
  scope = 'snsapi_userinfo'
  wx_url = f"https://open.weixin.qq.com/connect/oauth2/authorize?appid={appid}&redirect_uri={redirect_url}&response_type={response_type}&scope={scope}&state={state}#wechat_redirect"
  return wx_url

通过 code 换取 access_tokenopenid

def request_access_token(appid : str, secret : str, code: str):
  secret = settings.WX_SECRET
  api = f"https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code=[code]&grant_type=authorization_code"
  r = requests.get(api)
  return r.json()

通过 access_token 换取 用户信息

def request_userinfo(access_token: str, openid: str):
  api = f"https://api.weixin.qq.com/sns/userinfo?access_token={access_token}&openid={openid}&lang=zh_CN"
  r = requests.get(api)
  return r.json()

用户信息入库

需要注意的是:微信返回的数据编码格式为 ISO-8859-1 ,需要转换成 utf-8

def convert_string_encoding(s: str, from_encoding: str, to_encoding: str) -> str:
  """先根据 from_encoding 转换成bytes,然后在 decode 为 to_encoding 的字符串
  """
  return bytes(s, encoding=from_encoding).decode(to_encoding)
nickname = convert_string_encoding(resp['nickname'], 'ISO-8859-1', 'utf-8')

跳转回原来访问的链接

我的实现方式是在数据库保存一条记录,以 statekey

from app.models import WXUser, RedirectUrl
from utils import get_wx_authorize_url, get_random_string
from django.shortcuts import redirect

def login_required(func):
  def wrapper(request, *args, **kwargs):
    openid = request.openid
    try:
      user = WXUser.objects.get(openid=openid)
      request.wxuser = user
    except WXUser.DoesNotExist:
      state = get_random_string()
      redirect_url = get_wx_authorize_url(state=state)

      # 存储跳转链接
      try:
        r = RedirectUrl.objects.get(state=state)
      except RedirectUrl.DoesNotExist:
        r = RedirectUrl()
        r.state = state
      origin_url = request.get_raw_uri()
      r.url = origin_url
      r.save()

      return redirect(redirect_url)
    return func(request, *args, **kwargs)

  return wrapper

然后在我们设置的回调接口(会带上 codestate )里面,就可以通过 state 从数据库里获取原链接。

class RedirectUrl(BaseModel):
  state = models.TextField(unique=True)
  url = models.TextField()

0x02 中间件

这个中间件使用 jwt 作为认证手段,为什么不使用 session ,那可以讲另一个故事了,这里不赘述了。

HTTP_AUTHORIZATION (请求头中的 Authorization 字段)或者 key 为 jwttokencookie 中抽取出 jwt token ,从中解析出 openid ,添加到 request 变量中,之后就可以在后续的 views里面通过 request.openid 直接获取 openid 了。

def jwt_decode(token: Union[str, bytes]) -> tuple:
  """
  :param token : 可以是 bytes 也可以是 str,如果是 str,会先 encode 转成 bytes
  :return: 第一个参数为 payload,第二个参数为异常类型
  """
  if isinstance(token, str):
    token = token.encode()
  secret = settings.JWT_SECRET
  try:
    return jwt.decode(token, secret, algorithms=["HS256"]), None
  except Exception as e:
    # 统一捕捉异常:
    # jwt.exceptions.DecodeError
    # jwt.exceptions.InvalidSignatureError
    # jwt.exceptions.ExpiredSignatureError
    return None, e


class JWTAuthMiddleware(object):
  """
  小程序认证中间件
  """

  def __init__(self, get_response=None):
    self.get_response = get_response

  def __call__(self, request, *args, **kws):
    token = self.get_authorization_header(request)

    payload, error = jwt_decode(token)
    if not error:
      openid = payload['openid']
      request.openid = openid
    else:
      request.openid = None

    response = self.get_response(request, *args, **kws)
    return response

  def get_authorization_header(self, request):
    """
    从 AUTHORIZATION 请求头或者cookie 中获取 jwt code
    cookie 的 jwt code 的 key 为 jwtcode
    :param request:
    :return: rawtoken
    """

    auth_header = request.META.get('HTTP_AUTHORIZATION', '')
    cookie = request.COOKIES

    rawtoken = None
    if auth_header != "":
      try:
        rawtoken = auth_header.split(" ")[1]
      except IndexError as e:
        pass
    if 'jwttoken' in cookie:
      rawtoken = cookie['jwttoken']
    return rawtoken

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现文件名批量替换和内容替换
Mar 20 Python
收藏整理的一些Python常用方法和技巧
May 18 Python
python读取文本绘制动态速度曲线
Jun 21 Python
Python开发最牛逼的IDE——pycharm
Aug 01 Python
python 中文件输入输出及os模块对文件系统的操作方法
Aug 27 Python
python 分离文件名和路径以及分离文件名和后缀的方法
Oct 21 Python
Python网页正文转换语音文件的操作方法
Dec 09 Python
对sklearn的使用之数据集的拆分与训练详解(python3.6)
Dec 14 Python
Django Rest framework解析器和渲染器详解
Jul 25 Python
python多维数组分位数的求取方式
Mar 03 Python
python解析xml文件方式(解析、更新、写入)
Mar 05 Python
[原创]赚疯了!转手立赚800+?大佬的python「抢茅台脚本」使用教程
Jan 12 Python
python tkinter库实现气泡屏保和锁屏
Jul 29 #Python
django迁移数据库错误问题解决
Jul 29 #Python
python实现桌面托盘气泡提示
Jul 29 #Python
python实现桌面气泡提示功能
Jul 29 #Python
pycharm设置鼠标悬停查看方法设置
Jul 29 #Python
django rest framework vue 实现用户登录详解
Jul 29 #Python
python实现倒计时小工具
Jul 29 #Python
You might like
php whois查询API制作方法
2011/06/23 PHP
PHP里的中文变量说明
2011/07/23 PHP
解析file_get_contents模仿浏览器头(user_agent)获取数据
2013/06/27 PHP
PHP删除数组中空值的方法介绍
2014/04/14 PHP
php生成短域名函数
2015/03/23 PHP
Yii2增删改查之查询 where参数详细介绍
2016/08/08 PHP
php使用mysqli和pdo扩展,测试对比mysql数据库的执行效率完整示例
2019/05/09 PHP
让你的PHP,APACHE,NGINX支持大文件上传
2021/03/09 PHP
JQuery为textarea添加maxlength属性并且兼容IE
2013/04/25 Javascript
基于MVC3方式实现下拉列表联动(JQuery)
2013/09/02 Javascript
JS将表单导出成EXCEL的实例代码
2013/11/11 Javascript
JS实现图片高亮展示效果实例
2015/11/24 Javascript
浅谈toLowerCase和toLocaleLowerCase的区别
2016/08/15 Javascript
jQuery图片加载显示loading效果
2016/11/04 Javascript
微信公众平台开发教程(五)详解自定义菜单
2016/12/02 Javascript
bootstrap日历插件datetimepicker使用方法
2016/12/14 Javascript
bootstrapValidator bootstrap-select验证不可用的解决办法
2017/01/11 Javascript
JS实现的全排列组合算法示例
2017/10/09 Javascript
微信小程序 如何保持登录状态
2019/08/16 Javascript
vue实现微信浏览器左上角返回按钮拦截功能
2020/01/18 Javascript
带你使用webpack快速构建web项目的方法
2020/11/12 Javascript
Python Mysql自动备份脚本
2008/07/14 Python
Python中一些自然语言工具的使用的入门教程
2015/04/13 Python
Python简单进程锁代码实例
2015/04/27 Python
Python中scatter函数参数及用法详解
2017/11/08 Python
浅析Python装饰器以及装饰器模式
2018/05/28 Python
Django框架视图介绍与使用详解
2019/07/18 Python
django框架使用方法详解
2019/07/18 Python
python实现树的深度优先遍历与广度优先遍历详解
2019/10/26 Python
美国韩国化妆品和护肤品购物网站:Beautytap
2018/07/29 全球购物
商务英语应届生自我鉴定
2013/12/08 职场文书
房地产融资计划书
2014/01/10 职场文书
2015小学五年级班主任工作总结
2015/05/21 职场文书
美甲店的创业计划书模板
2019/08/23 职场文书
教你一步步实现一个简易promise
2021/11/02 Javascript
Android Flutter实现图片滑动切换效果
2022/04/07 Java/Android