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 22 Python
Python实现的异步代理爬虫及代理池
Mar 17 Python
利用python打印出菱形、三角形以及矩形的方法实例
Aug 08 Python
python中闭包Closure函数作为返回值的方法示例
Dec 17 Python
python中pip的安装与使用教程
Aug 10 Python
python交换两个变量的值方法
Jan 12 Python
解决python3 安装不了PIL的问题
Aug 16 Python
python对验证码降噪的实现示例代码
Nov 12 Python
使用批处理脚本自动生成并上传NuGet包(操作方法)
Nov 19 Python
Python操作Sonqube API获取检测结果并打印过程解析
Nov 27 Python
Django框架中间件定义与使用方法案例分析
Nov 28 Python
使用python 的matplotlib 画轨道实例
Jan 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
老照片 - 几十年前的收音机与人
2021/03/02 无线电
php中jQuery插件autocomplate的简单使用笔记
2012/06/14 PHP
基于php下载文件的详解
2013/06/02 PHP
php中字符查找函数strpos、strrchr与strpbrk用法
2014/11/18 PHP
PHP内置的Math函数效率测试
2014/12/01 PHP
引用 js在IE与FF之间的区别详细解析
2013/11/20 Javascript
JavaScript基础教程之alert弹出提示框实例
2014/10/16 Javascript
全面解析JavaScript里的循环方法之forEach,for-in,for-of
2020/04/20 Javascript
jQuery 生成svg矢量二维码
2016/08/09 Javascript
js内置对象处理_打印学生成绩单的简单实现
2016/09/24 Javascript
jquery Form轻松实现文件上传
2017/05/24 jQuery
jQuery实现 RadioButton做必选校验功能
2017/06/15 jQuery
浅谈vue-cli加载不到dev-server.js的解决办法
2017/11/24 Javascript
简单使用webpack打包文件的实现
2019/10/29 Javascript
JavaScript 判断数据类型的4种方法
2020/09/11 Javascript
[53:15]Mineski vs iG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python编写暴力破解FTP密码小工具
2014/11/19 Python
Python命令启动Web服务器实例详解
2017/02/23 Python
python实现简单http服务器功能
2018/09/17 Python
Python 函数返回值的示例代码
2019/03/11 Python
Python 使用list和tuple+条件判断详解
2019/07/30 Python
python的scipy实现插值的示例代码
2019/11/12 Python
Python 余弦相似度与皮尔逊相关系数 计算实例
2019/12/23 Python
python学习将数据写入文件并保存方法
2020/06/07 Python
解决pytorch 交叉熵损失输出为负数的问题
2020/07/07 Python
Python如何重新加载模块
2020/07/29 Python
Python如何爬取51cto数据并存入MySQL
2020/08/25 Python
HTML5和以前HTML4的区别整理
2013/10/20 HTML / CSS
香港No.1得奖零食网:香港零食大王
2016/07/22 全球购物
华为C++笔试题
2014/08/05 面试题
2014年教师德育工作总结
2014/11/10 职场文书
2014年卫生监督工作总结
2014/12/09 职场文书
三八节活动主持词
2015/07/04 职场文书
python爬虫selenium模块详解
2021/03/30 Python
python3 删除所有自定义变量的操作
2021/04/08 Python
KVM基础命令详解
2022/04/30 Servers