【技术博客】JWT -- JSON WEB TOKEN

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

从 HTTP 谈起

HTTP 是一个无状态的应用层协议。web

  • 无状态意味着每一个请求都是独立的。
  • 每次请求都不不清楚以前的请求发生了什么。

比如咱们在淘宝上网购:redis

  1. 登陆淘宝
  2. 下单购买
  3. 支付金额

然而,由于 HTTP 是无状态的,咱们在下单购买的时候,服务器此时已经不记得咱们的身份信息了,须要从新进行身份认证。算法

传统跨域登陆认证方式

传统的登陆校验采用的是 cookie-seession 方式,它的流程通常以下:django

  1. 客户端使用用户名和密码向服务器请求登陆。
  2. 服务器验证成功后,在当前对话(session)里面保存相关的数据(用户 UID、登陆时间等)。
  3. 服务器向用户返回一个 session_id,写入用户的 cookie。
  4. 用户以后的每一次请求,都会经过 cookie,将 session_id 提交给服务器。
  5. 服务器收到 session_id 后,查找以前保存的对话数据,获取用户身份,从而判断用户是否已经完成登陆校验。

传统模式的缺点是可扩展性差。对于单机操做影响不大,但若是是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都可以读取 session,而 session 通常是采用文件的方式存储,这会带来文件同步问题和文件读取问题。即使将 session 放入到 redis 中,也没法避免高并发数据的读取问题。json

JWT 的验证方式

JWT 采用的是 token 令牌的方式进行校验:后端

  1. 客户端使用用户名和密码向服务器请求登陆。
  2. 服务器验证成功后,签发一个 token 返回给客户端。
  3. 客户端收到 token 后将其缓存起来,以后的每次请求都会携带该 token。、
  4. 用户以后的每一次请求,都会经过 cookie,将 session_id 提交给服务器。
  5. 服务器收到请求后,验证请求中携带的 token,验证经过后进行业务逻辑处理。

这样作的优势在于:api

  • 服务器不保存任何 session 数据,减小服务器开销,方便扩展。
  • JWT 构成简单,只占用不多的字节,便于传输。
  • JSON 格式通用,便于跨语言使用。

一个 JWT 字符串包括如下三个部分:跨域

  • 头部(header)缓存

    • 标识用于生成签名的算法,例如:

      {
        ‘typ‘: ‘JWT‘,
        ‘alg‘: ‘HS256‘
      }
    • typ 表示 token 的类型,默认为 JWT,通常不改动。

    • alg 表示加密算法,默认 HMAC SHA256。

  • 载荷(payload)

    • 存放有效信息:
      • 官方标准的字段申明
      • 公共的字段声明
      • 私有的字段声明
  • 签证(signature)

    • 用密钥对 header 和 playload 两部分进行签证,防止数据被篡改。

Django REST framework

咱们的项目后端是基于 Django 进行开发的。

通常而言是在 restful 的接口中使用 JWT token,相关的 Django 库是 djangorestframeworkdjangorestframework-jwt

首先须要在 settings 中设置 DEFAULT_AUTHENTICATION_CLASSES 来进行用户认证:

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': (
        'rest_framework.schemas.coreapi.AutoSchema'
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    )
}

接着设置 JWT_AUTH 来进行 token 的刷新:

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    'JWT_ALLOW_REFRESH': True,
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
}

在用户登陆时,生成 token:

serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)

user = self.perform_create(serializer)

re_dict = serializer.data
payload = jwt_payload_handler(user)
re_dict['token'] = jwt_encode_handler(payload)
re_dict['username'] = user.username

headers = self.get_success_headers(serializer.data)
return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

urls 中注册路由:

urlpatterns = [
    path('login/', obtain_jwt_token),
    path('refresh/', refresh_jwt_token),
    path('verify/', verify_jwt_token),
]
相关文章
相关标签/搜索