Django REST framework JWT

Django REST framework JWT

在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token认证机制。

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者(客户端)和服务提供者(服务端)间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于身份认证,也可被数据加密传输。

JWT的构成

JWT就一段字符串,由三段信息构成的,将这三段信息文本用.拼接一起就构成了Jwt token字符串。就像这样:

eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAiMTUwMTIzNDU1IiwgImlhdCI6ICIxNTAxMDM0NTUiLCAibmFtZSI6ICJ3YW5neGlhb21pbmciLCAiYWRtaW4iOiB0cnVlLCAiYWNjX3B3ZCI6ICJRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjkifQ==.815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

header

jwt的头部承载两部分信息:

  • typ: 声明token类型,这里是jwt ,typ的值也可以是:Bear
  • alg: 声明签证的加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:

{
  'typ': 'JWT',
  'alg': 'HS256'
}

然后将头部进行base64编码,构成了jwt的第一部分头部

python代码举例:

import base64, json
header_data = {"typ": "jwt", "alg": "HS256"}
header = base64.b64encode( json.dumps(header_data).encode() ).decode()
print(header) # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9

payload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货仓,这些有效信息包含三个部分:

  • 标准声明
  • 公共声明
  • 私有声明

标准声明指定jwt实现规范中要求的属性。 (建议但不强制使用) :

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token, 从而回避重放攻击。

公共声明 : 公共的声明可以添加任何的公开信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,里面存放的是一些可以在服务端或者客户端通过秘钥进行加密和解密的加密信息。往往采用的RSA非对称加密算法。

python举例,定义一个payload。

import base64, json
from datetime import datetime
iat = int(datetime.now().timestamp())
payload_data = {
    "sub": "root",
    "exp": iat+3600,  # 假设一小时过期
    "iat": iat,
    "name": "wangxiaoming",
    "admin": True,
    "acc_pwd": "QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9",
}
# 将其进行base64编码,得到JWT的第二部分。
payload = base64.b64encode(json.dumps(payload_data).encode()).decode()
print(payload) # eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NDQ4LCAiaWF0IjogMTYzMTUyNDg0OCwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=

signature

JWT的第三部分是一个签证信息,用于辨真伪,防篡改。这个签证信息由三部分组成:

  • header (base64后的头部)

  • payload (base64后的载荷)

  • secret(保存在服务端的秘钥字符串,不会提供给客户端的)

    import base64, json, hashlib

    if name == 'main':
    """jwt 头部的生成"""
    header_data = {"typ": "jwt", "alg": "HS256"}
    header = base64.b64encode( json.dumps(header_data).encode() ).decode()
    print(header) # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9

      """jwt 载荷的生成"""
      payload_data = {
          "sub": "root",
          "exp": "150123455",
          "iat": "150103455",
          "name": "wangxiaoming",
          "admin": True,
          "acc_pwd": "QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9",
      }
      # 将其进行base64编码,得到JWT的第二部分。
      payload = base64.b64encode(json.dumps(payload_data).encode()).decode()
      print(payload) # eyJzdWIiOiAicm9vdCIsICJleHAiOiAiMTUwMTIzNDU1IiwgImlhdCI6ICIxNTAxMDM0NTUiLCAibmFtZSI6ICJ3YW5neGlhb21pbmciLCAiYWRtaW4iOiB0cnVlLCAiYWNjX3B3ZCI6ICJRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjkifQ==
    
      # from django.conf import settings
      # secret = settings.SECRET_KEY
      secret = 'django-insecure-hbcv-y9ux0&8qhtkgmh1skvw#v7ru%t(z-#chw#9g5x1r3z=$p'
      data = header + payload + secret  # 秘钥绝对不能提供给客户端。
      HS256 = hashlib.sha256()
      HS256.update(data.encode('utf-8'))
      signature = HS256.hexdigest()
      print(signature) # 815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b
    
      # jwt 最终的生成
      token = f"{header}.{payload}.{signature}"
      print(token)
      # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAiMTUwMTIzNDU1IiwgImlhdCI6ICIxNTAxMDM0NTUiLCAibmFtZSI6ICJ3YW5neGlhb21pbmciLCAiYWRtaW4iOiB0cnVlLCAiYWNjX3B3ZCI6ICJRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjkifQ==.815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b
    

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

python代码举例:

import base64, json, hashlib
from datetime import datetime

if __name__ == '__main__':
    """jwt 头部的生成"""
    header_data = {"typ": "jwt", "alg": "HS256"}
    header = base64.b64encode( json.dumps(header_data).encode() ).decode()
    print(header) # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9

    """jwt 载荷的生成"""
    iat = int(datetime.now().timestamp())
    payload_data = {
        "sub": "root",
        "exp": iat+3600,  # 假设1小时过期
        "iat": iat,
        "name": "wangxiaoming",
        "admin": True,
        "acc_pwd": "QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9",
    }
    # 将其进行base64编码,得到JWT的第二部分。
    payload = base64.b64encode(json.dumps(payload_data).encode()).decode()
    print(payload) # eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NDQ4LCAiaWF0IjogMTYzMTUyNDg0OCwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=

    # from django.conf import settings
    # secret = settings.SECRET_KEY
    SECRET_KEY = 'django-insecure-hbcv-y9ux0&8qhtkgmh1skvw#v7ru%t(z-#chw#9g5x1r3z=$p'
    data = header + payload + SECRET_KEY  # 秘钥绝对不能提供给客户端。
    HS256 = hashlib.sha256()
    HS256.update(data.encode('utf-8'))
    signature = HS256.hexdigest()
    print(signature) # 815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b

    # jwt 最终的生成
    token = f"{header}.{payload}.{signature}"
    print(token)
    # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NTA2LCAiaWF0IjogMTYzMTUyNDkwNiwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=.a8c677945fc277d8e677514420a1bff645da00498c2af8fb29189d652391a435

    """验证逻辑"""
    # token = "eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NTA2LCAiaWF0IjogMTYzMTUyNDkwNiwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=.a8c677945fc277d8e677514420a1bff645da00498c2af8fb29189d652391a435"
    # token = "eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTIxNzA5LCAiaWF0IjogMTYzMTUxODEwOSwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=.b82524a2155a24bcb4f085993545ec74766688849fd832e4edf9dd592167a2e3"
    token = "eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiJyb290IiwiZXhwIjoxNjMxNTI5MDg4LCJpYXQiOjE2MzE1MjU0ODgsIm5hbWUiOiJ3YW5neGlhb2hvbmciLCJhZG1pbiI6dHJ1ZSwiYWNjX3B3ZCI6IlFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOSJ9.b533c5515444c51058557017e433d411379862d91640c8beed6f2617b1da2feb"
    header, payload, signature = token.split(".")

    # 验证是否过期了
    payload_data = json.loads( base64.b64decode(payload.encode()) )
    exp = payload_data.get("exp",None)
    if exp is not None and int(exp) < int(datetime.now().timestamp()):
        raise Exception("token过期!!!")

    # 验证token是否有效,是否被串改
    # from django.conf import settings
    # secret = settings.SECRET_KEY
    SECRET_KEY = 'django-insecure-hbcv-y9ux0&8qhtkgmh1skvw#v7ru%t(z-#chw#9g5x1r3z=$p' # 获取原来的秘钥
    data = header + payload + SECRET_KEY  # 秘钥绝对不能提供给客户端。
    HS256 = hashlib.sha256()
    HS256.update(data.encode('utf-8'))
    new_signature = HS256.hexdigest()

    if new_signature != signature:
        print("认证失败")
    else:
        print("认证通过")

关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。

文档网站:https://jpadilla.github.io/django-rest-framework-jwt/

安装配置JWT

安装

pip install djangorestframework-jwt

settings.dev,配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}
import datetime
JWT_AUTH = {
    # 设置jwt的有效期
    'JWT_EXPIRATION_DELTA': datetime.timedelta(weeks=1),  # 一周有效
}
  • JWT_EXPIRATION_DELTA 指明token的有效期

生成jwt

Django REST framework JWT 扩展的说明文档中提供了手动签发JWT的方法

https://jpadilla.github.io/django-rest-framework-jwt/#creating-a-new-token-manually

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)

在用户注册或登录成功后,在序列化器中返回用户信息以后同时返回token即可。

后端实现登陆认证接口

Django REST framework JWT提供了登录获取token的视图,可以直接使用它绑定一个url地址即可。

在users子应用路由urls.py中

from rest_framework_jwt.views import obtain_jwt_token
from django.urls import path
urlpatterns = [
    path('login/', obtain_jwt_token),
]

接下来,我们可以通过postman来测试下功能,可以发送form表单,也可以发送json,username和password是必填字段

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Django REST framework JWT - Python技术站

(0)
上一篇 2023年4月2日 下午5:48
下一篇 2023年4月2日 下午5:48

相关文章

  • 数据库系统原理之数据库应用设计与开发实例

    数据库应用设计与开发实例 第一节 需求描述与分析 在此,结合某高校个性化课程在线选课的实际需求,给出一个简化的需求分析 一、功能性需求 1 管理员后台模块 学生信息管理 教师信息管理 课程信息管理 班级信息管理 2 学生使用模块 查询课程 浏览所选课程 查询成绩 3 教师使用模块 我的课程 登分 二、非功能性需求 在线选课系统 浏览器/服务器(B/S) 结构…

    MySQL 2023年4月17日
    00
  • 数据库系统原理之数据管理技术的发展

    数据管理技术的发展 第一节 数据库技术发展概述 数据模型是数据库系统的核心和基础 以数据模型的发展为主线,数据库技术可以相应地分为三个发展阶段: 第一代的网状、层次数据库系统 第二代的关系数据库系统 新一代的数据库系统 一、第一代数据库系统 层次数据库系统 层次模型 网状数据库系统 网状模型 层次模型是网状模型的特例 第一代数据库系统有如下两类代表: 196…

    MySQL 2023年4月17日
    00
  • 数据库系统原理之数据库安全与保护

    数据库安全与保护 第一节 数据库完整性 数据库完整性是指数据库中数据的正确性和相容性。 数据完整性约束是为了防止数据库中存在不符合语义的数据,为了维护数据的完整性,DBMS 必须提供一种机制来检查数据库中的数据,以判断其是否满足语义规定的条件。 这些加在数据库数据之上的语义约束条件就是数据完整性约束。 DBMS 检查数据是否满足完整性约束条件的机制就称为完整…

    MySQL 2023年4月17日
    00
合作推广
合作推广
分享本页
返回顶部