JWT与cookie和token的区别,django中使用

A) cookie如何认证

1. 用户输入用户名与密码,发送给服务器。

2. 服务器验证用户名和密码,正确的就创建一个会话(session),同时会把这个会话的ID保存到客户端浏览器中,因为保存的地方是浏览器的cookie,所以这种认证方式叫做基于cookie的认证方式。

3. 后续的请求中,浏览器会发送会话ID到服务器,服务器上如果能找到对应的ID的会话,那么服务器就会返回需要的数据给浏览器。

4. 当用户退出登录,会话会同时在客户端和服务器端被销毁。

B) cookie认证方式的不足之处

1. 服务器要为每个用户保留session信息,连接用户过多会造成服务器内存压力过大。

2. 适合单一域名,不适合第三方请求。

二. token

A) token的认证过程

1. 用户输入用户名和密码,发送给服务器。

2. 服务器验证用户名和密码,正确的话就返回一个签名过的token(token 可以认为就是个长长的字符串),浏览器客户端拿到这个token。

3. 后续每次请求中,浏览器会把token作为http header发送给服务器,服务器验证签名是否有效,如果有效那么认证就成功,可以返回客户端需要的数据。

4. 一旦用户退出登录,只需要客户端销毁token即可,服务器端不需要任何操作。

B) token认证方式的特点

客户端的token中自己保留有大量信息,服务器没有存储这些信息,而只负责验证,不必进行数据库查询,执行效率大大提高。

三. JWT

A) JWT介绍

1. JWT是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。

2. 优点是在分布式系统中,很好地解决了单点登录问题,很容易解决了session共享的问题。jwt长度较小,且可以使用URL传输(URL safe)。不想cookies只能在web环境起作用。 JWT可以同时使用在web环境和RESTfull的接口。 
   缺点是无法作废已颁布的令牌/不易应对数据过期。

B) JWT组成

JWT包含三个部分: Header头部,Payload负载和Signature签名。由三部分生成token,三部分之间用“.”号做分割。
列如 : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1. Header 
在Header中通常包含了两部分:type:代表token的类型,这里使用的是JWT类型。 alg:使用的Hash算法,例如HMAC SHA256或RSA.
{
    "alg": "HS256",
    "typ": "JWT"
}
这会被经过base64Url编码形成第一部分

2. Payload  
token的第二个部分是荷载信息,它包含一些声明Claim(实体的描述,通常是一个User信息,还包括一些其他的元数据)
声明分三类:
   1)Reserved Claims,这是一套预定义的声明,并不是必须的,这是一套易于使用、操作性强的声明。包括:iss(issuer)、exp(expiration time)、sub(subject)、aud(audience)等
   2)Plubic Claims,
   3)Private Claims,交换信息的双方自定义的声明
{
    "sub": "1234567890",
    "name": "John Doe",
    "admin": true
}
同样经过Base64Url编码后形成第二部分
3.  signature  使用header中指定的算法将编码后的header、编码后的payload、一个secret进行加密。
例如使用的是HMAC SHA256算法,大致流程类似于: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
这个signature字段被用来确认JWT信息的发送者是谁,并保证信息没有被修改

C) 为什么要使用JWT

相比XML格式,JSON更加简洁,编码之后更小,这使得JWT比SAML更加简洁,更加适合在HTML和HTTP环境中传递。 在安全性方面,SWT只能够使用HMAC算法和共享的对称秘钥进行签名,而JWT和SAML token则可以使用X.509认证的公私秘钥对进行签名。与简单的JSON相比,XML和XML数字签名会引入复杂的安全漏洞。 因为JSON可以直接映射为对象,在大多数编程语言中都提供了JSON解析器,而XML则没有这么自然的文档-对象映射关系,这就使得使用JWT比SAML更方便 java json web token工具类

D) JWT的django引入

1、前端请求头带上token:
axios.get(this.host + '/user/', {
                    // 向后端传递JWT token的方法
                    headers: {
                        'Authorization': 'JWT ' + this.token
                    },
                    responseType: 'json',
                })
-------------------------------------------------------------------------------------
2、django中安装jwt
pip install djangorestframework-jwt
3、配置,指明有效期
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
}
-------------------------------------------------------------------------------------
4、使用
序列化器中:
token = serializers.CharField(label='登录状态token', read_only=True)  # 增加token字段
fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token')  # 增加token
create方法中:
 # 补充生成记录登录状态的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
前端js文件中加入保存token方法

// 记录用户的登录状态
                    sessionStorage.clear();
                    localStorage.clear();
                    localStorage.token = response.data.token;

注意:此次操作使用token保存在sessionStorage中,关闭页面也保存,存储在localStorage中。
---------------------------------------------------------------------------------------
5、使用JWT进行django登录
配置路由urlpatterns = [
    url(r'authorizations/', obtain_jwt_token, name='authorizations'),
]
但是默认的返回值仅有token,我们还需在返回值中增加username和user_id。
在users/utils.py 中,创建
def jwt_response_payload_handler(token, user=None, request=None):
    """
    自定义jwt认证成功返回数据
    """
    return {
        'token': token,
        'user_id': user.id,
        'username': user.username
    }
修改配置文件
# JWT
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}
------------------------------------------------------------------------------

6、登录时使用用户名或者手机号同时登录
在users/utils.py中编写:
def get_user_by_account(account):
    """
    根据帐号获取user对象
    :param account: 账号,可以是用户名,也可以是手机号
    :return: User对象 或者 None
    """
    try:
        if re.match('^1[345789]\d{9}$', account):
            # 帐号为手机号
            user = User.objects.get(mobile=account)
        else:
            # 帐号为用户名
            user = User.objects.get(username=account)
    except User.DoesNotExist:
        return None
    else:
        return user


class UsernameMobileAuthBackend(ModelBackend):
    """
    自定义用户名或手机号认证
    """

    def authenticate(self, request, username=None, password=None, **kwargs):
        user = get_user_by_account(username)
        if user is not None and user.check_password(password):
            return user


配置文件加入:
AUTHENTICATION_BACKENDS = [
    'users.utils.UsernameMobileAuthBackend',
]

猜你喜欢

转载自blog.csdn.net/weixin_42670402/article/details/85259268
今日推荐