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判断windows系统是32位还是64位的方法
May 11 Python
详解duck typing鸭子类型程序设计与Python的实现示例
Jun 03 Python
Python中遇到的小问题及解决方法汇总
Jan 11 Python
浅谈Python对内存的使用(深浅拷贝)
Jan 17 Python
Python使用lambda表达式对字典排序操作示例
Jul 25 Python
Python 3.8 新功能全解
Jul 25 Python
详解PyTorch手写数字识别(MNIST数据集)
Aug 16 Python
完美解决pycharm 不显示代码提示问题
Jun 02 Python
Python环境管理virtualenv&virtualenvwrapper的配置详解
Jul 01 Python
Python 利用OpenCV给照片换底色的示例代码
Aug 03 Python
pandas处理csv文件的方法步骤
Oct 16 Python
pip install命令安装扩展库整理
Mar 02 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 404错误页面实现代码
2009/06/22 PHP
PHP内存使用情况如何获取
2015/10/10 PHP
PHP开发中常用的十个代码样例
2016/02/02 PHP
tp5框架使用composer实现日志记录功能示例
2019/01/10 PHP
document.designMode的功能与使用方法介绍
2007/11/22 Javascript
扩展jQuery 键盘事件的几个基本方法
2009/10/30 Javascript
javascript中检测变量的类型的代码
2010/12/28 Javascript
js URL参数的拼接方法比较
2012/02/15 Javascript
Jquery实现页面加载时弹出对话框代码
2013/04/19 Javascript
js中settimeout方法加参数
2014/02/28 Javascript
单击和双击事件的冲突处理示例代码
2014/04/03 Javascript
javascript框架设计读书笔记之字符串的扩展和修复
2014/12/02 Javascript
深入理解AngularJS中的ng-bind-html指令和$sce服务
2016/09/08 Javascript
jQuery实现贪吃蛇小游戏(附源码下载)
2017/03/04 Javascript
详解Vue.js组件可复用性的混合(mixin)方式和自定义指令
2017/09/06 Javascript
element-ui 限制日期选择的方法(datepicker)
2018/05/16 Javascript
详解使用mpvue开发github小程序总结
2018/07/25 Javascript
mpvue微信小程序开发之实现一个弹幕评论
2019/11/24 Javascript
JS原型prototype和__proto__用法实例分析
2020/03/14 Javascript
[42:06]2019国际邀请赛全明星赛 8.23
2019/09/05 DOTA
Python 命令行非阻塞输入的小例子
2013/09/27 Python
Python入门篇之字典
2014/10/17 Python
Python中的装饰器用法详解
2015/01/14 Python
python如何拆分含有多种分隔符的字符串
2018/03/20 Python
浅析python3字符串格式化format()函数的简单用法
2018/12/07 Python
关于Python字符串显示u...的解决方式
2020/03/06 Python
Python如何在windows环境安装pip及rarfile
2020/06/15 Python
Linux安装Python3如何和系统自带的Python2并存
2020/07/23 Python
iRobot官网:改变生活的家用机器人品牌
2016/09/20 全球购物
澳大利亚买卖正宗二手奢侈品交易平台:Luxe.It.Fwd
2019/10/16 全球购物
聊城大学毕业生自荐书
2014/02/01 职场文书
信息与计算科学专业推荐信
2014/02/23 职场文书
学生自我鉴定格式及范文
2014/09/16 职场文书
机票销售员态度不好检讨书
2014/09/27 职场文书
实验心得体会范文
2016/01/25 职场文书
Python作用域和名称空间的详细介绍
2022/04/13 Python