Django REST framework 之JWT认证

Json Web Token

一、JWT简介

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

  • 简洁(Compact)前端

    能够经过URL, POST 参数或者在 HTTP header 发送,由于数据量小,传输速度快web

  • 自包含(Self-contained)算法

    负载中包含了全部用户所须要的信息,避免了屡次查询数据库shell

二、JWT 组成

  • Header 头部

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

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

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

Base64URL 算法json

 这个算法跟 Base64 算法基本相似,是一种编码,也就是说,它是能够被翻译回原来的样子来的。它并非一种加密过程后端

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

  • 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附带上的签名是不同的。若是要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不同的。

三、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的接收方是不是本身(可选)。

四、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 '{"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

相关文章
相关标签/搜索