Django REST framework 之JWT认证

Json Web Token

1、JWT简介

JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。它具备两个特点:

  • 简洁(Compact)

    可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快

  • 自包含(Self-contained)

    负载中包含了所有用户所需要的信息,避免了多次查询数据库

2、JWT 组成

  • Header 头部

头部包含了两部分,token 类型和采用的加密算法

{
  "alg": "HS256",
  "typ": "JWT"
}
  • typ: (Type)类型。在JOSE Header中这是个可选参数,但这里我们需要指明类型是JWT
  • alg: (Algorithm)算法,必须是JWS支持的算法

它会使用 base64url编码组成 JWT 结构的第一部分

Base64URL 算法

这个算法跟 Base64 算法基本类似,是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+/=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。

  • Payload 负载

这部分就是我们存放信息的地方了,你可以把用户 ID 等信息放在这里,JWT 规范里面对这部分有进行了比较详细的介绍,JWT 规定了7个官方字段,供选用

iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

常用的有,

{
    "iss": "lee JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "6465644@163.com"
}

同样的,它会使用 base64url 编码组成 JWT 结构的第二部分

  • Signature 签名

签名的作用是保证 JWT 没有被篡改过

前面两部分都是使用 base64url 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及我们提供的一个密钥,这个密钥只有服务器才知道,不能泄露给用户,然后使用 header 中指定的签名算法(HS256)进行签名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU3ZmVmMTY0ZTU0YWY2NGZmYzUzZGJkNSIsInhzcmYiOiI0ZWE1YzUwOGE2NTY2ZTc2MjQwNTQzZjhmZWIwNmZkNDU3Nzc3YmUzOTU0OWM0MDE2NDM2YWZkYTY1ZDIzMzBlIiwiaWF0IjoxNDc2NDI3OTMzfQ.PA3QjeyZSUh7H0GfE0vJaKW4LjKJuC3dVLQiY4hii8s
  • 签名的目的

  最后一步签名的过程,实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

3、JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

  1. 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形同lll.zzz.xxx的字符串。
  3. 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
  4. 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)

5.后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。

4、JWT 的几个特点

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

Django REST framework 中使用 JWT认证

使用django-rest-framework-jwt这个库来帮助我们简单的使用jwt进行身份验证
并解决一些前后端分离而产生的跨域问题

  • 安装

直接使用pip安装即可,目前支持Python、Django、DRF主流版本

pip install djangorestframework-jwt
  • 使用
在settings.py文件中,将JSONWebTokenAuthentication 添加到REST framework框架的DEFAULT_AUTHENTICATION_CLASSES.
REST_FRAMEWORK = {
    ‘DEFAULT_PERMISSION_CLASSES‘: (
        ‘rest_framework.permissions.IsAuthenticated‘,
    ),
    ‘DEFAULT_AUTHENTICATION_CLASSES‘: (
        ‘rest_framework_jwt.authentication.JSONWebTokenAuthentication‘,
        ‘rest_framework.authentication.SessionAuthentication‘,
        ‘rest_framework.authentication.BasicAuthentication‘,
    ),

  同样,你还可以使用基于APIView类的视图,在每个视图或每个视图集的基础上设置身份验证方案。与 Token 认证一样,尽可能使用基于APIView类的视图认证方式。

但使用基于APIView类的视图认证方式时,不要忘记导入类。

from rest_framework_jwt.authentication import JSONWebTokenAuthentication

在你的urls.py文件中添加以下URL路由,以便通过POST包含用户名和密码的令牌获取。

from rest_framework_jwt.views import obtain_jwt_token
urlpatterns += [
    url(r‘^api-token-auth/‘, obtain_jwt_token)
]

如果你使用用户名admin和密码admin123456创建了用户,则可以通过在终端中执行以下操作来测试JWT是否正常工作。

$ curl -X POST -d "username=admin&password=admin123456" http://127.0.0.1:8000/api-token-auth/

或者,你可以使用Django REST framework支持的所有内容类型来获取身份验证令牌。例如:

$ curl -X POST -H "Content-Type: application/json" -d ‘{"username":"admin","password":"admin123456"}‘ http://127.0.0.1:8000/api-token-auth/

现在访问需要认证的API时,就必须要包含Authorization: JWT <your_token> 头信息了:

$ curl -H "Authorization: JWT <your_token>" http://127.0.0.1:8000/virtual/
  • 刷新Token

如果JWT_ALLOW_REFRESH为True,可以“刷新”未过期的令牌以获得具有更新到期时间的全新令牌。像如下这样添加一个URL模式:

from rest_framework_jwt.views import refresh_jwt_token
urlpatterns += [
    url(r‘^api-token-refresh/‘, refresh_jwt_token)
]

使用方式就是将现有令牌传递到刷新API,如下所示: {"token": EXISTING_TOKEN}。请注意,只有非过期的令牌才有效。另外,响应JSON看起来与正常获取令牌端点{"token": NEW_TOKEN}相同。

$ curl -X POST -H "Content-Type: application/json" -d ‘{"token":"<EXISTING_TOKEN>"}‘ http://localhost:8000/api-token-refresh/

可以重复使用令牌刷新(token1 -> token2 -> token3),但此令牌链存储原始令牌(使用用户名/密码凭据获取)的时间。作为orig_iat,你只能将刷新令牌保留至JWT_REFRESH_EXPIRATION_DELTA。

刷新token以获得新的token的作用在于,持续保持活跃用户登录状态。比如通过用户密码获得的token有效时间为1小时,那么也就意味着1小时后此token失效,用户必须得重新登录,这对于活跃用户来说其实是多余的。如果这个用户在这1小时内都在浏览网站,我们不应该让用户重新登录,就是在token没有失效之前调用刷新接口为用户获得新的token。

  • 认证Token

在一些微服务架构中,身份验证由单个服务处理。此服务负责其他服务委派确认用户已登录此身份验证服务的责任。这通常意味着其他服务将从用户接收JWT传递给身份验证服务,并在将受保护资源返回给用户之前等待JWT有效的确认。添加以下URL模式:

from rest_framework_jwt.views import verify_jwt_token
urlpatterns += [
    url(r‘^api-token-verify/‘, verify_jwt_token)
]

将Token传递给验证API,如果令牌有效,则返回令牌,返回状态码为200。否则,它将返回400 Bad Request以及识别令牌无效的错误。

  • 手动创建Token

有时候你可能希望手动生成令牌,例如在创建帐户后立即将令牌返回给用户。或者,你需要返回的信息不止是Token,可能还有用户权限相关值。你可以这样做:

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过期时间,以下是所有可用设置的默认值。在settings.py文件中设置。

JWT_AUTH = {
    ‘JWT_ENCODE_HANDLER‘:
    ‘rest_framework_jwt.utils.jwt_encode_handler‘,

    ‘JWT_DECODE_HANDLER‘:
    ‘rest_framework_jwt.utils.jwt_decode_handler‘,

    ‘JWT_PAYLOAD_HANDLER‘:
    ‘rest_framework_jwt.utils.jwt_payload_handler‘,

    ‘JWT_PAYLOAD_GET_USER_ID_HANDLER‘:
    ‘rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler‘,

    ‘JWT_RESPONSE_PAYLOAD_HANDLER‘:
    ‘rest_framework_jwt.utils.jwt_response_payload_handler‘,

    // 这是用于签署JWT的密钥,确保这是安全的,不共享不公开的
    ‘JWT_SECRET_KEY‘: settings.SECRET_KEY,
    ‘JWT_GET_USER_SECRET_KEY‘: None,
    ‘JWT_PUBLIC_KEY‘: None,
    ‘JWT_PRIVATE_KEY‘: None,
    ‘JWT_ALGORITHM‘: ‘HS256‘,
    // 如果秘钥是错误的,它会引发一个jwt.DecodeError
    ‘JWT_VERIFY‘: True,
    ‘JWT_VERIFY_EXPIRATION‘: True,
    ‘JWT_LEEWAY‘: 0,
    // Token过期时间设置
    ‘JWT_EXPIRATION_DELTA‘: datetime.timedelta(seconds=300),
    ‘JWT_AUDIENCE‘: None,
    ‘JWT_ISSUER‘: None,

    // 是否开启允许Token刷新服务,及限制Token刷新间隔时间,从原始Token获取开始计算
    ‘JWT_ALLOW_REFRESH‘: False,
    ‘JWT_REFRESH_EXPIRATION_DELTA‘: datetime.timedelta(days=7),

    // 定义与令牌一起发送的Authorization标头值前缀
    ‘JWT_AUTH_HEADER_PREFIX‘: ‘JWT‘,
    ‘JWT_AUTH_COOKIE‘: None,

一般除了过期时间外,其他配置参数很少改变。具体参数意义当用到时可以查询官网。

参考:http://www.ruanyifeng.com/blog/2018/0

7/json_web_token-tutorial.html

参考:https://juejin.im/entry/5979a9355188253de4272ff4

参考:http://www.ywnds.com/?p=14967

原文地址:https://www.cnblogs.com/freely/p/10328011.html

时间: 2024-11-05 23:24:34

Django REST framework 之JWT认证的相关文章

Django rest framework 使用自定义认证方式

Django rest framework 使用自定义认证方式 Django使用自定义认证方式 介绍了 "Django使用自定义认证方式",这一篇说说怎样在前一篇的基础上提供rest api. 修改settings.py中INSTALLED_APPS,添加 'login' app. 给login app增加serializers.py文件 #coding:utf-8 from django.contrib.auth.models import User from rest_framew

Django REST framework 之 API认证

RESTful API 认证 和 Web 应用不同,RESTful APIs 通常是无状态的, 也就意味着不应使用 sessions 或 cookies, 因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过 sessions 或 cookies 维护, 常用的做法是每个请求都发送一个秘密的 access token 来认证用户, 由于 access token 可以唯一识别和认证用户, API 请求应通过 HTTPS 来防止 man-in-the-middle(MitM)中间人攻击. 通

Django REST framework 的TokenAuth认证及外键Serializer基本实现

一,Models.py中,ForeignKey记得要有related_name属性,已实现关联对象反向引用. app_name = models.ForeignKey("cmdb.App",related_name='deploy_app', verbose_name="App") 二,Settings.py文件中,加入对Django REST framework的基本设置. REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSE

Django REST framework JWT

Django REST framework JWT 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证.我们不再使用Session认证机制,而使用Json Web Token认证机制. Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景.JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便

django项目之配置jwt的token认证

1.简介jwt                                                                                                              Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景.JWT的声明一般被用来在身份提供者和服务提供者间传递被

Django Restful Framework【第三篇】认证、权限、限制访问频率

一.认证 认证请求头 views.py #!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.permissions import

源码剖析Django REST framework的认证方式及自定义认证

源码剖析Django REST framework的认证方式 由Django的CBV模式流程,可以知道在url匹配完成后,会执行自定义的类中的as_view方法. 如果自定义的类中没有定义as_view方法,根据面向对象中类的继承可以知道,则会执行其父类View中的as_view方法 在Django的View的as_view方法中,又会调用dispatch方法. 现在来看看Django restframework的认证流程 Django restframework是基于Django的框架,所以基

django rest framework 用户认证

BaseAuthentication 类: django rest framework 通过 BaseAuthentication 实现认证功能 无论是自定义的认证类还是 rest framework 自带的认证类都应该继承 BaseAuthentication BaseAuthentication 中有两个方法 authenticate 和 authenticate_header, 其中 authenticate 方法必须实现 如果用户需要自定义认证方法则继承 BaseAuthenticati

Django rest framework源码分析(一) 认证

一.基础 最近正好有机会去写一些可视化的东西,就想着前后端分离,想使用django rest framework写一些,顺便复习一下django rest framework的知识,只是顺便哦,好吧.我承认我是故意的,因为我始终觉得,如果好的技术服务于企业,顺便的提高一下自己.大家都很开心不是不.再次强调一下,真的只是顺便. 安装吧 pip install djangorestframework 1.2.需要先了解的一些知识 理解下面两个知识点非常重要,django-rest-framework