DRF框架之用户登录状态保持

 

  本篇主要介绍 用户状态保持的两种的方案 -- session 和 jwt_token,以及这两种方案的实现方式,及优缺点对比。

  引入:HTTP协议是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码进行用户认证,那么下一次请求时,用户还是要再一次进行用户认证才行。

  因为根据HTTP协议的无状态性,在下一次请求时我们并不知道是哪个用户发出的请求,所以为了让我们能识别是哪个用户发送的请求,则必须采取一些特殊的方式,比如 session、jwt_token等。

 

一、session

  根据上面的应用,我可以通过session来保存用户的登录状态,而Django默认提供了session存储机制,且默认启动了session。

  session存储方式:数据库、本地缓存、混合存储三种。通常采用本地缓存:

SESSION_ENGINE='django.contrib.sessions.backends.cache'

  那么我们怎么使用用户登录状态保持呢?

  在用户登录或者注册成功后,设置session。

class UserView(APIView):
    ......
    # 实现用户登录或者注册的业务逻辑
    request.session['user_id'] = user.id
    request.session['is_login'] = True
    
    .....
    # 返回响应

  由于session是保存在服务器中,在设置session时会将 生成随机字符串(sessionID),通过cookie发送给客户端,而服务器端会同sessionID作为键并且将我们设置的进行加密计算形参长字符串作为值存储在服务器端。

  关于session存储机制可参考:https://www.cnblogs.com/lenther2002/p/4822302.html

 缺点:

  1. Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
  2. 扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
  3. CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击

 

二、jwt_token

  基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

  其实现的基本流程如下:

  1. 用户进行登录或者注册成功,服务器返回一个token
  2. 客户端存储token(可以存储在cookie中,也可以存储在本地硬盘)
  3. 每次请求均携带上这个token值
  4. 服务器校验token值(Django扩展提供了jwt_token认证机制),返回数据

 

 1、jwt是什么?

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

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

 

 2、其构成如下:

  1、header -- 头部,主要用来声明类型,这是jwt。并且声明加密算法,通常为HMAC SHA256。

  2、payload -- 载荷,存放标准中注册的声明(签发方,接收方等)、添加用户的相关信息等

  3、signature --  签证,需要base64转换后的header和base64装换后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行利用secret_key进行组合加密,而得到signature。

 注:

  1. 在payload中不要存放敏感信息,因为这部分信息只是使用了base64进行了装换,而不是加密。
  2. secret_key不要暴露,若暴露用户可以根据该header和payload自行生产jwt。

 

 3、它的实现机制如下:

  即 用户每次请求携带该token,Django进行认证时,会取出header 和payloads部分,并结合Django的秘钥secret_key使用header中声明的加密方式进行加密生成一个新的 signature2,对用户携带的token中的第三部分 signature1进行对比,若用户修改了token则生成的signature2与用户携带的signature1不一致,则无法通过验证。

  1、是否有token 决定是否进行jwt认证。

  2、token的正确性决定是否能够通过jwt认证 。

 优点:

  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展

 实现:

  1、安装:

pip install djangorestframework-jwt

  2、配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

#  JWT_EXPIRATION_DELTA 指明token的有效期
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
}

  3、实现

class UserView(APIView):
    .......
    # 实现注册或者登陆的业务逻辑
    
    # 生成记录登录状态的token
        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)

        user.token = token
    
        .......
        # 返回响应

  注:前提:

  这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *

  

  由于我们之前在上面 配置了默认的认证类包含了:

'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    )

  故用户在每次请求时携带 token,Django的认证系统会自动对该token进行认证。若认证通过,则会用户认证完成。

 

   over~~~,简单的介绍了两次常用的用户登录状态保持的方案~~~,关于session介绍的并不全,有机会再补充~~~

 

posted @ 2018-10-24 22:22  Little_five  阅读(4098)  评论(0编辑  收藏  举报