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的几种标准输出重定向方式
Aug 15 Python
centos6.7安装python2.7.11的具体方法
Jan 16 Python
selenium+python 去除启动的黑色cmd窗口方法
May 22 Python
Python实用技巧之利用元组代替字典并为元组元素命名
Jul 11 Python
Python HTML解析模块HTMLParser用法分析【爬虫工具】
Apr 05 Python
Python 3.8 新功能全解
Jul 25 Python
python PIL和CV对 图片的读取,显示,裁剪,保存实现方法
Aug 07 Python
Python实现socket非阻塞通讯功能示例
Nov 06 Python
Python-opencv实现红绿两色识别操作
Jun 04 Python
Python 日期与时间转换的方法
Aug 01 Python
Python中rapidjson参数校验实现
Jul 25 Python
Elasticsearch 聚合查询和排序
Apr 19 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内存溢出问题的解决方法
2013/06/25 PHP
round robin权重轮循算法php实现代码
2016/05/28 PHP
phpfpm的作用和用法
2019/10/10 PHP
Yii框架 session 数据库存储操作方法示例
2019/11/18 PHP
PHP标准库 (SPL)――Countable用法示例
2020/06/05 PHP
获取当前网页document.url location.href区别总结
2008/05/10 Javascript
JQuery 网站换肤功能实现代码
2009/11/02 Javascript
各种页面定时跳转(倒计时跳转)代码总结
2013/10/24 Javascript
js确认删除对话框效果的示例代码
2014/02/20 Javascript
js实现ifram取父窗口URL地址的方法
2015/02/09 Javascript
理解 JavaScript Scoping & Hoisting(二)
2015/11/18 Javascript
Bootstrap table表格简单操作
2017/02/07 Javascript
微信JSAPI Ticket接口签名详解
2020/06/28 Javascript
jquery 一键复制到剪切板的实例
2017/09/20 jQuery
关于vue路由缓存清除在main.js中的设置
2019/11/06 Javascript
微信小程序用户盒子、宫格列表的实现
2020/07/01 Javascript
jQuery 实现DOM元素拖拽交换位置的实例代码
2020/07/14 jQuery
解决vue-router路由拦截造成死循环问题
2020/08/05 Javascript
jQuery实现简单轮播图效果
2020/12/27 jQuery
Python的ORM框架SQLObject入门实例
2014/04/28 Python
深入解析Python编程中JSON模块的使用
2015/10/15 Python
Python队列的定义与使用方法示例
2017/06/24 Python
python email smtplib模块发送邮件代码实例
2018/04/26 Python
python pandas生成时间列表
2019/06/29 Python
window7下的python2.7版本和python3.5版本的opencv-python安装过程
2019/10/24 Python
如何表示python中的相对路径
2020/07/08 Python
thinkphp5 路由分发原理
2021/03/18 PHP
css3实现可滑动跳转的分页插件示例
2014/05/08 HTML / CSS
HTML5之SVG 2D入门1—SVG(可缩放矢量图形)概述
2013/01/30 HTML / CSS
乡镇领导干部个人对照检查材料思想汇报
2014/09/23 职场文书
拾金不昧表扬稿大全
2015/05/05 职场文书
北京爱情故事观后感
2015/06/12 职场文书
2016年小学教师政治学习心得体会
2016/01/23 职场文书
女人创业励志语录,句句蕴含能量,激发你的潜能
2019/08/20 职场文书
使用Spring处理x-www-form-urlencoded方式
2021/11/02 Java/Android
instantclient客户端 连接oracle数据库
2022/04/26 Oracle