Flask强制登录 token 无感知更新 token禁用

强制登录实现思路分析

1.强制登录需求
在开发中, 部分接口是要求用户登录才能访问的, 所以有强制登录的需求. 当用户登录的情况下, 可以正常访问特定的接口, 但当用户没有登录的情况下, 访问特定的接口, 则要求用户进行登录.

2.强制登录思路
1.定义装饰器脚本, 脚本内定义login_required装饰器
2.传入装饰器中的函数就是请求要访问的视图, 所以在视图执行前, 进行判断, 如果用户登录则放行, 执行视图; 如果用户未登录则返回401状态码与'Invalid token'信息
3.如何判断用户是否登录呢? 之前的请求钩子已经将用户的id放进了g对象中, 只需要判断g对象中是否存在user_id即可.

强制登录装饰器的实现

# 4.代码实现
from flask import g
from functools import wraps
def login_required(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if g.user_id is not None:
            return func(*args, **kwargs)
        else:
            return {'message': 'Invalid token'}, 401
    return wrapper
# 可以使用测试蓝图来测试装饰器

更新Token

1.Token失效如何处理

# Token的失效
我们给Token设置了过期时间, 那过期时间多久合适呢? 同学们可能想到一种情况"15天免登录", 是不是我们给Token设置15天的有效期就行了呢?
    当然不是, 给Token设置15天的有效期会使得Token暴露时间过长, 降低安全性. 所以我们约定只给Token两个小时的有效时间.但问题又来了. 两个小时后Token失效了咋办呢? 从新登录? 如果你是用户, 可能在这种情况下会感觉体验非常的差.
    所以需要解决的事情就是: 刷新token(无感知)

2.无感知更新Token思路

# 更新Token思路
1.在给用户返回token时, 我们给用户设置了两小时有效期, 同时我们可以给用户返回一个额外的refresh_token, 这个token设置的有效期根据业务需求而定.
2.用户携带正常的token请求接口, 当token失效后, 变返回401状态码, 告诉前端用户token失效了, 前端携带者refresh_token请求后端的put接口进行Token的刷新, 生成新的token返回给用户
3.用户接收到后, 再次请求接口使用新生成的token, 这一些列的操作需要前端和后端配合完成, 做到用户无感知的状态下已经变更了原有的token值.

更新Token接口实现(代码)

# 接口实现
# (1).改写_generate_token方法
def _generate_token(self, user_id, refresh=True):
    """
    生成token
    :param user_id:
    :return:
    """
    # 获取盐
    secret = current_app.config.get('JWT_SECRET')
    # 定义过期时间
    expiry = datetime.utcnow() + timedelta(hours=2)
    # 生成Token
    token = 'Bearer ' + generate_jwt({'user_id': user_id}, expiry, secret)
    if refresh:
        expiry = datetime.utcnow() + timedelta(days=15)
        # is_refresh作为更新token的信号
        refresh_token = 'Bearer' + generate_jwt({'user_id': user_id, 'is_refresh': True}, expiry, secret)
    else:
        refresh_token = None
    return token, refresh_token

# (2).修改用户认证中的token生成代码与返回值代码
token, refresh_token = self._generate_token(user_id)
return {'message': 'OK', 'data':{'token': token, 'refresh_token': refresh_toke}}
 

# (3).改写验签代码: 将更新token信号放在g对象中
def jwt_authentication():
    g.user_id = None
    g.is_refresh = False
    # 获取请求头中的token
    token = request.headers.get('Authorization')
    print(token)
    if token is not None and token.startswith('Bearer '):
        token = token[7:]

        # 验证token
        payload = verify_jwt(token)
        print('payload', payload)

        if payload is not None:
            # 保存到g对象中
            g.user_id = payload.get('user_id')
            g.is_refresh = payload.get('is_refresh', False)

 (4).实现put刷新接口

def put(self):
    """
    刷新token
    :return:
    """
    if g.user_id is not None and g.is_refresh is True:
        token, refresh_token = self._generate_token(g.user_id, refresh=False)
        return {'message': 'ok', 'data': {'token': token}}
    else:
        return {'message': 'Invalid refresh token'}, 403

JWT禁用问题

1.需求分析
用户登录后, Token两小时有效, 但在登录后的10分钟内, 用户修改了密码.这个Token依然可以实现请求. 怎么办呢?

2.应用场景

(1).用户修改密码, 需要颁发新的Token, 禁用原有Token
(2).后台封禁用户

3.问题解决方案
1.在redis中使用set类型保存新生成的token值, 格式为: "user:user_id:token".
2.客户端使用token进行请求时, 如果token验证通过, 则从Redis中查找该用户的token值是否存在
3.第一种情况: Redis中该用户的token值不存在, 则直接放行, 处理业务逻辑
4.第二种情况: Redis中该用户的token存在, 但与当前请求使用的token一致, 则说明用户使用的是新的token, 同样可以放行, 处理业务.
5.第三种情况: Redis中存在, 但当前用户请求携带的token与Redis中的token不一致, 则返回403状态码, 不再处理业务逻辑.

4.代码实现

# 4.代码实现
# 在Redis中使用set类型保存新token
key = 'user:{}:token'.format(user_id)
pl = redis_client.pipeline()
pl.sadd(key, new_token)
pl.expire(key, token有效期)
pl.execute()

# token检验操作
key = 'user:{}:token'.format(user_id)
valid_tokens = redis_client.smembers(key, token)
if valid_tokens and token not in valid_tokens:
  return {'message': 'Invalid token'.}, 403

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_ang__ang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值