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程序员开发中常犯的10个错误
Jul 07 Python
详解Python中find()方法的使用
May 18 Python
python3 破解 geetest(极验)的滑块验证码功能
Feb 24 Python
Python使用Scrapy爬虫框架全站爬取图片并保存本地的实现代码
Mar 04 Python
对numpy中布尔型数组的处理方法详解
Apr 17 Python
对python模块中多个类的用法详解
Jan 10 Python
Python实现Event回调机制的方法
Feb 13 Python
Windows下pycharm创建Django 项目(虚拟环境)过程解析
Sep 16 Python
python实现的分层随机抽样案例
Feb 25 Python
Python安装OpenCV的示例代码
Mar 05 Python
520使用Python实现“我爱你”表白
May 20 Python
python上selenium的弹框操作实现
Jul 13 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+mysql+ajax轻量级聊天室实现方法详解
2016/10/17 PHP
PHP基于curl post实现发送url及相关中文乱码问题解决方法
2017/11/25 PHP
PHP+Redis 消息队列 实现高并发下注册人数统计的实例
2018/01/29 PHP
php和vue配合使用技巧和方法
2019/05/09 PHP
php引用和拷贝的区别知识点总结
2019/09/23 PHP
jscript之List Excel Color Values
2007/06/13 Javascript
IE6下出现JavaScript未结束的字符串常量错误的解决方法
2010/11/21 Javascript
围观tangram js库
2010/12/28 Javascript
jquery禁用右键单击功能屏蔽F5刷新
2014/03/17 Javascript
滚动条响应鼠标滑轮事件实现上下滚动的js代码
2014/06/30 Javascript
JS实现仿腾讯微博无刷新删除微博效果代码
2015/10/16 Javascript
EXT中单击button按钮grid添加一行(光标位置可设置)的实例代码
2016/06/02 Javascript
原生JS实现风箱式demo,并封装了一个运动框架(实例代码)
2016/07/22 Javascript
利用VUE框架,实现列表分页功能示例代码
2017/01/12 Javascript
js实现文字跑马灯效果
2017/02/23 Javascript
微信小程序的分类页面制作
2017/06/27 Javascript
集合Bootstrap自定义confirm提示效果
2017/09/19 Javascript
JS中创建自定义类型的常用模式总结【工厂模式,构造函数模式,原型模式,动态原型模式等】
2019/01/19 Javascript
JS原生瀑布流效果实现
2019/04/26 Javascript
基于Vue.js与WordPress Rest API构建单页应用详解
2019/09/16 Javascript
vue实现短信验证码登录功能(流程详解)
2019/12/10 Javascript
原生js实现ajax请求和JSONP跨域请求操作示例
2020/03/14 Javascript
Python列表list数组array用法实例解析
2014/10/28 Python
Python3如何解决字符编码问题详解
2017/04/23 Python
在python3环境下的Django中使用MySQL数据库的实例
2017/08/29 Python
Python线程下使用锁的技巧分享
2018/09/13 Python
对Python正则匹配IP、Url、Mail的方法详解
2018/12/25 Python
python hashlib加密实现代码
2019/10/17 Python
PyTorch和Keras计算模型参数的例子
2020/01/02 Python
正隆泰信息技术有限公司上机题
2012/06/14 面试题
出国留学担保书
2014/05/20 职场文书
学校交通安全责任书
2014/08/25 职场文书
2014年客房服务员工作总结
2014/11/18 职场文书
2014年后勤工作总结
2014/11/18 职场文书
2015年大学社团工作总结
2015/04/09 职场文书
8g内存用python读取10文件_面试题-python 如何读取一个大于 10G 的txt文件?
2021/05/28 Python