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中的字符串知识点
Apr 14 Python
Python生成不重复随机值的方法
May 11 Python
python itchat实现微信自动回复的示例代码
Aug 14 Python
Python调用ctypes使用C函数printf的方法
Aug 23 Python
答题辅助python代码实现
Jan 16 Python
Python3实现带附件的定时发送邮件功能
Dec 22 Python
Python向MySQL批量插数据的实例讲解
Mar 31 Python
Python模拟简单电梯调度算法示例
Aug 20 Python
python实现接口并发测试脚本
Jun 25 Python
Django实现文件上传下载功能
Oct 06 Python
Python获取、格式化当前时间日期的方法
Feb 10 Python
python实现会员管理系统
Mar 18 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类与对象中的private访问控制的疑问
2012/11/01 PHP
探讨方法的重写(覆载)详解
2013/06/08 PHP
PHP随手笔记整理之PHP脚本和JAVA连接mysql数据库
2015/11/25 PHP
php使用CURL模拟GET与POST向微信接口提交及获取数据的方法
2016/09/23 PHP
利用jquery的获取JS文件中的字符串内容
2012/02/14 Javascript
jQuery插件开发详细教程
2014/06/06 Javascript
Jquery自定义button按钮的几种方法
2014/06/11 Javascript
js中的json对象详细介绍
2014/10/29 Javascript
javascript实现全角与半角字符的转换
2015/01/07 Javascript
JS实现文字向下滚动完整实例
2015/02/06 Javascript
vuejs动态组件给子组件传递数据的方法详解
2016/09/09 Javascript
原生js仿浏览器滚动条效果
2017/03/02 Javascript
使用nodeJs来安装less及编译less文件为css文件的方法
2017/11/20 NodeJs
js实现HTML中Select二级联动的实例
2018/01/05 Javascript
基于vue v-for 多层循环嵌套获取行数的方法
2018/09/26 Javascript
使用form-create动态生成vue自定义组件和嵌套表单组件
2019/01/18 Javascript
js实现一款简单踩白块小游戏(曾经很火)
2019/12/02 Javascript
jQuery实现消息弹出框效果
2019/12/10 jQuery
Nodejs在局域网配置https访问的实现方法
2020/10/17 NodeJs
[59:42]Secret vs Alliacne 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
浅谈Python 中整型对象的存储问题
2016/05/16 Python
python获取指定时间差的时间实例详解
2017/04/11 Python
python添加菜单图文讲解
2019/06/04 Python
一些常用的HTML5模式(pattern) 总结
2015/07/14 HTML / CSS
html5实现移动端适配完美写法
2017/11/16 HTML / CSS
MYPROTEIN澳大利亚官方网站:欧洲运动营养品牌
2019/06/26 全球购物
信息技术专业大学生个人的自我评价
2013/10/05 职场文书
酒店行政人事部经理职务说明书
2014/02/26 职场文书
创建卫生先进单位实施方案
2014/03/10 职场文书
未受刑事制裁公证证明
2014/09/20 职场文书
学习党的群众路线教育实践活动剖析材料
2014/10/13 职场文书
美丽人生观后感
2015/06/03 职场文书
银行求职信范文
2019/05/13 职场文书
解决pytorch-gpu 安装失败的记录
2021/05/24 Python
sql server 累计求和实现代码
2022/02/28 SQL Server
Windows server 2022创建创建林、域树、子域的步骤
2022/06/25 Servers