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实现爬虫下载漫画示例
Feb 16 Python
详解Python各大聊天系统的屏蔽脏话功能原理
Dec 01 Python
利用python模拟实现POST请求提交图片的方法
Jul 25 Python
python执行使用shell命令方法分享
Nov 08 Python
python执行精确的小数计算方法
Jan 21 Python
Python内置random模块生成随机数的方法
May 31 Python
通过实例学习Python Excel操作
Jan 06 Python
django API 中接口的互相调用实例
Apr 01 Python
详解selenium + chromedriver 被反爬的解决方法
Oct 28 Python
Python基于Opencv识别两张相似图片
Apr 25 Python
Pygame Time时间控制的具体使用详解
Nov 17 Python
python实现双向链表原理
May 25 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
php5数字型字符串加解密代码
2008/04/24 PHP
PHP 获取远程网页内容的代码(fopen,curl已测)
2011/06/06 PHP
thinkphp的CURD和查询方式介绍
2013/12/19 PHP
图解找出PHP配置文件php.ini的路径的方法
2014/08/20 PHP
PHP 生成微信红包代码简单
2016/03/25 PHP
PHP中常见的密码处理方式和建议总结
2018/10/14 PHP
用document.documentElement取代document.body的原因分析
2009/11/12 Javascript
使用Jquery实现每日签到功能
2015/04/03 Javascript
谈谈JavaScript异步函数发展历程
2015/09/29 Javascript
JavaScript获取浏览器信息的方法
2015/11/20 Javascript
Bootstrap创建可折叠的组件
2016/02/23 Javascript
JQuery之proxy实现绑定代理方法
2016/08/01 Javascript
Javascript动画效果(2)
2016/10/11 Javascript
实例解析jQuery中如何取消后续执行内容
2016/12/01 Javascript
jQuery快速高效制作网页交互特效
2017/02/24 Javascript
详谈js中数组(array)和对象(object)的区别
2017/02/27 Javascript
node.js express中app.param的用法详解
2017/07/16 Javascript
angularJS实现动态添加,删除div方法
2018/02/27 Javascript
VUE解决 v-html不能触发点击事件的问题
2019/10/28 Javascript
Node.js学习之内置模块fs用法示例
2020/01/22 Javascript
jquery实现图片放大镜效果
2020/12/23 jQuery
javascript实现固定侧边栏
2021/02/09 Javascript
[01:23:24]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant BO3 第三场 2月7日
2021/03/11 DOTA
Python2和Python3中print的用法示例总结
2017/10/25 Python
Python获取当前脚本文件夹(Script)的绝对路径方法代码
2019/08/27 Python
Django异步任务线程池实现原理
2019/12/17 Python
python爬虫判断招聘信息是否存在的实例代码
2020/11/20 Python
canvas实现圆绘制的示例代码
2019/09/11 HTML / CSS
印度网上购物首选目的地:Flipkart
2016/08/01 全球购物
北美最大的手工艺品零售商之一:Michaels Stores
2019/02/27 全球购物
工程承包协议书
2014/04/22 职场文书
五四青年节的活动方案
2014/08/20 职场文书
课前一分钟演讲稿
2014/08/26 职场文书
地理科学专业自荐信
2014/09/01 职场文书
vue实现水波涟漪效果的点击反馈指令
2021/05/31 Vue.js
详解Redis基本命令与使用场景
2021/06/01 Redis