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实现多线程下载文件的代码实例
Jun 01 Python
实例讲解Python中的私有属性
Aug 21 Python
Python Paramiko模块的安装与使用详解
Nov 18 Python
CentOS 6.5下安装Python 3.5.2(与Python2并存)
Jun 05 Python
django session完成状态保持的方法
Nov 27 Python
Python OpenCV实现鼠标画框效果
Aug 19 Python
python opencv实现gif图片分解的示例代码
Dec 13 Python
pytorch逐元素比较tensor大小实例
Jan 03 Python
Python openpyxl模块原理及用法解析
Jan 19 Python
关于keras中keras.layers.merge的用法说明
May 23 Python
Python Scrapy多页数据爬取实现过程解析
Jun 12 Python
实战Python爬虫爬取酷我音乐
Apr 11 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四大安全策略
2014/03/12 PHP
php中ob_flush函数和flush函数用法分析
2015/03/18 PHP
php实现QQ空间获取当前用户的用户名并生成图片
2015/07/25 PHP
php 指定范围内多个随机数代码实例
2016/07/18 PHP
Laravel下生成验证码的类
2017/11/15 PHP
JS 实现双色表格实现代码
2009/11/24 Javascript
JavaScript实现给按钮加上双重动作的方法
2015/08/14 Javascript
jQuery弹簧插件编写基础之“又见弹窗”
2015/12/11 Javascript
jQuery中bind(),live(),delegate(),on()绑定事件方法实例详解
2016/01/19 Javascript
简单几步实现返回顶部效果
2016/12/05 Javascript
关于 jQuery Easyui异步加载tree的问题解析
2016/12/06 Javascript
详解Vue用自定义指令完成一个下拉菜单(select组件)
2017/10/31 Javascript
Vue精简版风格指南(推荐)
2018/01/30 Javascript
解决bootstrap-select 动态加载数据不显示的问题
2018/08/10 Javascript
javascript中一些奇葩的日期换算方法总结
2018/11/14 Javascript
Vue.js实现开发购物车功能的方法详解
2019/02/22 Javascript
JavaScript运行机制实例分析
2020/04/11 Javascript
[03:31]DOTA2英雄基础教程 大地之灵
2013/12/17 DOTA
[05:10]2014DOTA2国际邀请赛 通往胜利之匙赛场探秘之旅
2014/07/18 DOTA
[57:36]DOTA2-DPC中国联赛 正赛 SAG vs CDEC BO3 第三场 2月1日
2021/03/11 DOTA
Python中使用md5sum检查目录中相同文件代码分享
2015/02/02 Python
利用scrapy将爬到的数据保存到mysql(防止重复)
2018/03/31 Python
使用Python和xlwt向Excel文件中写入中文的实例
2018/04/21 Python
python使用matplotlib模块绘制多条折线图、散点图
2020/04/26 Python
Python正则表达式匹配数字和小数的方法
2019/07/03 Python
pyinstaller打包找不到文件的问题解决
2020/04/15 Python
python实现磁盘日志清理的示例
2020/11/05 Python
用CSS3实现背景渐变的方法
2015/07/14 HTML / CSS
英国家用电器折扣网站:Electrical Discount UK
2018/09/17 全球购物
匈牙利超级网上商店和优惠:Alza.hu
2019/12/17 全球购物
教师自荐信范文
2013/12/09 职场文书
大学生个人自荐信
2014/02/24 职场文书
大学生实习介绍信
2015/05/05 职场文书
小学生必读成语故事大全:送给暑假的你们
2019/07/09 职场文书
Java方法重载和方法重写的区别到底在哪?
2021/06/11 Java/Android
Vue h函数的使用详解
2022/02/18 Vue.js