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计算圆周长、面积、球体体积并画出圆
Apr 08 Python
浅谈python对象数据的读写权限
Sep 12 Python
python运行其他程序的实现方法
Jul 14 Python
springboot配置文件抽离 git管理统 配置中心详解
Sep 02 Python
pytorch逐元素比较tensor大小实例
Jan 03 Python
django model的update时auto_now不被更新的原因及解决方式
Apr 01 Python
如何基于线程池提升request模块效率
Apr 18 Python
基于python实现上传文件到OSS代码实例
May 09 Python
python中线程和进程有何区别
Jun 17 Python
Python3读写ini配置文件的示例
Nov 06 Python
python tkinter Entry控件的焦点移动操作
May 22 Python
Python面向对象编程之类的概念
Nov 01 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设计模式  Command(命令模式)
2011/06/17 PHP
取得单条网站评论以数组形式进行输出
2014/07/28 PHP
jQuery textarea的长度进行验证
2009/05/06 Javascript
JavaScript高级程序设计阅读笔记(五) ECMAScript中的运算符(一)
2012/02/27 Javascript
Javascript 垃圾收集机制介绍理解
2013/05/14 Javascript
js里取容器大小、定位、距离等属性搜集整理
2013/08/19 Javascript
js改变Iframe中Src的方法
2015/05/05 Javascript
javascript中createElement的两种创建方式
2015/05/14 Javascript
在javascript中使用com组件的简单实现方法
2016/08/17 Javascript
原生js开发的日历插件
2017/02/04 Javascript
javascript基本数据类型和转换
2017/03/17 Javascript
Vue的路由动态重定向和导航守卫实例
2018/03/17 Javascript
React+Webpack快速上手指南(小结)
2018/08/15 Javascript
ES6 更易于继承的类语法的使用
2019/02/11 Javascript
JS实现的简单tab切换功能完整示例
2019/06/20 Javascript
原生js实现分页效果
2020/09/23 Javascript
vue 计算属性和侦听器的使用小结
2021/01/25 Vue.js
[03:59]5分钟带你了解什么是DOTA2(第二期)
2017/02/07 DOTA
Python对list列表结构中的值进行去重的方法总结
2016/05/07 Python
详解Python中类的定义与使用
2017/04/11 Python
Python读取Pickle文件信息并计算与当前时间间隔的方法分析
2019/01/30 Python
10款最好的Python开发编辑器
2019/07/03 Python
Python集合基本概念与相关操作实例分析
2019/10/30 Python
Python3.7实现验证码登录方式代码实例
2020/02/14 Python
使用Python实现将多表分批次从数据库导出到Excel
2020/05/15 Python
HTML5拖放功能_动力节点Java学院整理
2017/07/13 HTML / CSS
优秀的教师个人的中文求职信
2013/09/21 职场文书
物流专业毕业生推荐信范文
2013/11/18 职场文书
信息技术教学反思
2014/02/12 职场文书
法律进企业活动方案
2014/03/04 职场文书
2014年企业员工工作总结
2014/12/09 职场文书
审美与表现自我评价
2015/03/09 职场文书
公司总经理岗位职责
2015/04/01 职场文书
门面租赁合同范文
2019/08/06 职场文书
SpringBoot SpringEL表达式的使用
2021/07/25 Java/Android
浅析Python OpenCV三种滤镜效果
2022/04/11 Python