web应用中,保存状态的几种方式-token-cookie-session-jwt

web应用中,保存状态的几种方式-token-cookie-session

无论我们选择何种语法进行开发,都会遇到一个问题,如登录之后进行状态保持,特定视图比如后台核心数据需要校验权限,等等一系列涉及账号安全问题都需要我们进行-签名验签,保持账号信息的可靠性。下面列举下我知道的几种状态保持方式。

第一种: 自定义token

用户在登录或者注册成功之后,给当前用户生成由自己的加密方式生成的token,保存到客户端上,客户端再次访问的时候,带上这个token,我们拿到这个token后再使用密钥生成now_token,和接收到的token进行对比,对比成功,即可访问正常视图,否则重定向到登录页面:

  1. 在我的子应用中创建一个utils模块,里面定义了两个方法,一个用来随机生成token,一个用来校验token;
from django.conf import settings

def get_token(id, salt=settings.SECRET_KEY):
    import hashlib
    md = hashlib.md5()
    md.update(bytes(str(id), encoding='utf-8'))
    md.update(bytes(salt, encoding='utf-8'))
    return md.hexdigest() + '|' + str(id)


def check_token(token, salt=settings.SECRET_KEY): 
    ll = token.split('|')
    import hashlib
    md = hashlib.md5()
    md.update(bytes(ll[-1], encoding='utf-8'))
    md.update(bytes(salt, encoding='utf-8'))
    if ll[0] == md.hexdigest():
        return True
    else:
        return False

这里我使用了md5加密,以用户id和 自己维持的密钥进行加盐,生成token,自己的id称之为源数据,我这里使用的·settings.SECRET_KEY·称之为盐,盐可自定义,因为我们在创建django项目时会生成个独立的密钥-每个django项目的密钥都不同的SECRET_KEY。
2. 当用户填写好账号密码进行登录时,在相应视图补充token逻辑;

    def post(self, request):
        # 认证账户
        # print(request.data)

        username = request.data.get("username")
        password = request.data.get("password")
        if not all([username, password]):
            return Response({"errmsg": "参数不全"}, status=400)
        user = auth.authenticate(username=username, password=password)
        # print(user.username)
        if user is None:
            return Response({"errmsg":"账户或者密码错误"}, status=400)
        token = utils.get_token(user.pk)
        # return Response({"msg":"success","username":user.username}, status=200)
        return JsonResponse({"msg":"success","username":user.username, "token":token}, status=200)

前端接收到这个token,保存至本地。
在这里插入图片描述
这里说一点 我们浏览器的保存机制 localstoragesessionStoragelocalstorage会保存到当前浏览器,用户关闭浏览器下次打开数据依旧存在, 而sessionStorage是临时存储,当关闭当前页面或者浏览器,数据消失。
在这里插入图片描述
这里对我token进行说明一点,加密方式选择的是md5,用我当前用户的id先进行加密,再在这个基础上加盐再进行加密,返回给我们的十六进制的token,返回的时候我拼接了个 "|id"在后面,这样后端接受到这个token就可以根据这个id和盐生成出来token 进行对比。

第二种 django restframework自带的Token类生成的token

  1. 首先看 自带的Token的导包顺序: from rest_framework.authtoken.models import Token
  2. 再看相应源码实现
class Token(models.Model):
    """
    The default authorization token model.
    """
    key = models.CharField(_("Key"), max_length=40, primary_key=True)
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL, related_name='auth_token',
        on_delete=models.CASCADE, verbose_name=_("User")
    )
    created = models.DateTimeField(_("Created"), auto_now_add=True)

    class Meta:
        # Work around for a bug in Django:
        # https://code.djangoproject.com/ticket/19422
        #
        # Also see corresponding ticket:
        # https://github.com/encode/django-rest-framework/issues/705
        abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS
        verbose_name = _("Token")
        verbose_name_plural = _("Tokens")

    def save(self, *args, **kwargs):
        if not self.key:
            self.key = self.generate_key()
        return super().save(*args, **kwargs)

    def generate_key(self):
        return binascii.hexlify(os.urandom(20)).decode()

    def __str__(self):
        return self.key

首先可以明确的是 这个Token类也是一个模型类,有三个字段,key,user,created分别是token值,关联用户对象和创建时间
3. 既然是模型类,那么我们在创建这个对象的语法就很简单了

token = Token.objects.create(user=user)
  1. 接下来是具体的实现步骤
  2. 先在settings配置文件中注册token
    在这里插入图片描述
  3. 再进行数据库迁移, python manage.py makemigrations python manage.py makemigrate 会生成一张表
    在这里插入图片描述
  4. 我们将账号密码传到后端,后端进行校验成功后,便可生成token,返回给前端,前端再使用storage
    保存起来即可。
    后端代码如下:
def post(self, request):
        # 认证账户
        # print(request.data)

        username = request.data.get("username")
        password = request.data.get("password")
        if not all([username, password]):
            return Response({"errmsg": "参数不全"}, status=400)
        user = auth.authenticate(username=username, password=password)
        # 校验通过, 删除原来的token
        old_token = Token.objects.filter(user=user)
        old_token.delete()
        # print(user.username)
        token = Token.objects.create(user=user)
        print(token)
        if user is None:
            return Response({"errmsg":"账户或者密码错误"}, status=400)
        # return Response({"msg":"success","username":user.username}, status=200)
        return JsonResponse({"msg":"success","username":user.username, "token":token.key}, status=200)

前端代码如下:

on_submit: function(){
            axios.post('/users/login/', {
                    username: this.username,
                    password: this.password,
                }, {
                    responseType: 'json',
                })
                .then(response => {
                    // 使用浏览器本地存储保存token
                    sessionStorage.clear();
                    localStorage.username = response.data.username;
                    localStorage.token = response.data.token;
                    // 扩展全局
                    $.ajaxSetup({
                        headers : {
                            'Authorization' : "Token " + response.data.token
                        }
                    });
                    this.get_user_info();
                    // alert("登录成功");
                })
                .catch(error => {
                    alert(error);
                    alert("请登陆:")
                })

        },

最后在具体需要登录之后才能访问的视图加上权限验证,如下:

class QueryShow(APIView):
    from rest_framework.authentication import TokenAuthentication
    authentication_classes = [TokenAuthentication, ]
    def get(self, request):
        if request.user.is_authenticated:
            print("已认证")
        else:
            print("未认证")
        goods_unpay = OrderInfo.objects.filter(user_id=1,status=1)
        data = {"goods_unpay":goods_unpay}
        return render(request, 'order_success.html', context=data)

在进入这个视图之前,会进行权限验证,这里我们指定了Token验证,然后我们前端访问的时候带上了这个token 'Authorization' : "Token " + response.data.token ,验证通过就表示是登录用户。

第三种-JWT

JWT由三部分组成,分别是header(头部), playload(负载), signature(签名),签名需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分signature形式如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

headers说明的是使用类型和加密方式,playload说明的是公共信息如用户名,id等。
具体使用步骤如下:
(1). 安装扩展 pip install djangorestframework-jwt
(2). 在settings文件中指定配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 这里指定使用jwt验证,当用户访问具体视图进行权限验证,就会默认的使用jwt进行验证
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 这里指定jwt-token的过期时间
}

(3). 使用jwt扩展生成token的步骤如下:

Django REST framework JWT 扩展的说明文档中提供了手动签发JWT的方法

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)

(4). 用户登录,验证后生成token,返回给前端前端保存,步骤如下:
后端:

def post(self, request):
        # 认证账户
        # print(request.data)

        username = request.data.get("username")
        password = request.data.get("password")
        if not all([username, password]):
            return Response({"errmsg": "参数不全"}, status=400)
        user = auth.authenticate(username=username, password=password)
        token = user.genetate_jwt_token()
        print(token)
        if user is None:
            return Response({"errmsg":"账户或者密码错误"}, status=400)
        # return Response({"msg":"success","username":user.username}, status=200)
        return JsonResponse({"msg":"success","username":user.username, "token":token}, status=200)
##### user.genetate_jwt_token() 这是我定义在User模型类中的方法
def genetate_jwt_token(self):
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(self)
        token = jwt_encode_handler(payload)
        return token

前端

.then(response => {
                    // 使用浏览器本地存储保存token
                    sessionStorage.clear();
                    localStorage.username = response.data.username;
                    localStorage.token = response.data.token;
                    this.token = response.data.token;
                    this.get_user_info();

然后访问需要登录之后的视图 带上JWT token 如下:

axios.get('/order/query/', {
                headers: {
                    'Authorization': 'JWT ' + this.token
                },
                responseType: 'json'
            })
                .then(response => {
                    // 使用浏览器本地存储保存token
                    // sessionStorage.clear();
                    // localStorage.username = response.data.username;
                    // localStorage.token = response.data.token;
                    // 扩展全局
                    console.log("登陆成功");
                })
                .catch(error => {
                    alert(error);
                    alert("请登陆:")
                });

这样 前端访问到具体视图 就会采用刚在配置文件中指定的JSONWebTokenAuthentication验证。验证通过就是登录状态的用户啦。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值