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中的编码知识整理汇总
Jan 26 Python
在win和Linux系统中python命令行运行的不同
Jul 03 Python
windows环境下tensorflow安装过程详解
Mar 30 Python
对python sklearn one-hot编码详解
Jul 10 Python
使用Selenium破解新浪微博的四宫格验证码
Oct 19 Python
PyQt5根据控件Id获取控件对象的方法
Jun 25 Python
python UDP(udp)协议发送和接收的实例
Jul 22 Python
django 类视图的使用方法详解
Jul 24 Python
python 进程间数据共享multiProcess.Manger实现解析
Sep 23 Python
python全局变量引用与修改过程解析
Jan 07 Python
解决python图像处理图像赋值后变为白色的问题
Jun 04 Python
使用Keras预训练好的模型进行目标类别预测详解
Jun 27 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
ThinkPHP有变量的where条件分页实例
2014/11/03 PHP
PHP图像识别技术原理与实现
2016/10/27 PHP
游戏人文件夹程序 ver 4.03
2006/07/14 Javascript
[转]JS宝典学习笔记
2007/02/07 Javascript
javascript实现的动态文字变换
2007/07/28 Javascript
Javascript学习笔记7 原型链的原理
2010/01/11 Javascript
ExtJs 3.1 XmlTreeLoader Example Error
2010/02/09 Javascript
jQuery判断checkbox(复选框)是否被选中以及全选、反选实现代码
2014/02/21 Javascript
jquery ajax应用中iframe自适应高度问题解决方法
2014/04/12 Javascript
详解网站中图片日常使用以及优化手法
2017/01/09 Javascript
利用Angular+Angular-Ui实现分页(代码加简单)
2017/03/10 Javascript
基于模板引擎Jade的应用(详解)
2017/12/12 Javascript
vue 插值 v-once,v-text, v-html详解
2018/01/19 Javascript
微信小程序自定义多列选择器使用详解
2019/06/21 Javascript
一步一步实现Vue的响应式(对象观测)
2019/09/02 Javascript
JavaScript自动生成 年月范围 选择功能完整示例【基于jQuery插件】
2019/09/03 jQuery
jQuery 图片查看器插件 Viewer.js用法简单示例
2020/04/04 jQuery
vue-axios同时请求多个接口 等所有接口全部加载完成再处理操作
2020/11/09 Javascript
Vue使用Element实现增删改查+打包的步骤
2020/11/25 Vue.js
python连接sql server乱码的解决方法
2013/01/28 Python
Python中内置的日志模块logging用法详解
2016/07/12 Python
Python实现Windows和Linux之间互相传输文件(文件夹)的方法
2017/05/08 Python
Python实现连接两个无规则列表后删除重复元素并升序排序的方法
2018/02/05 Python
Python paramiko模块使用解析(实现ssh)
2019/08/30 Python
当当网官方旗舰店:中国图书销售夺金品牌
2018/04/02 全球购物
程序员机试试题汇总
2012/03/07 面试题
物理系毕业生自荐信
2013/11/01 职场文书
项目合作计划书
2014/01/09 职场文书
幼儿园教师培训方案
2014/02/04 职场文书
产品质量承诺书范文
2014/03/27 职场文书
考核评语大全
2014/04/29 职场文书
运动会广播稿诗歌版
2014/09/12 职场文书
职工年度考核评语
2014/12/31 职场文书
金秋助学感谢信
2015/01/21 职场文书
MySQL 1130异常,无法远程登录解决方案详解
2021/08/23 MySQL
使用CSS3实现按钮悬停闪烁动态特效代码
2021/08/30 HTML / CSS