状态保持与身份认证

1. 关系

1.1 缓存

Cookie和Session
提示:
Cookie和Session是Django程序中用来缓存数据的
状态保持和Cookie、Session的关系
提示:
* 容易出现的误解:状态保持就是Cookie和Session,Cookie和Session就是状态保持。
* 状态保持和Cookie、Session是两种不同的概念,需要区分开,不能混淆。
状态保持

* 用于记录当前用户的状态,比如,用户登录后记录登录的状态。

Cookie、Session:

* Cookie、Session仅仅是Django提供的缓存机制而已,用于缓存数据的。
* 比如:
    * Cookie和Session缓存购物车数据
    * Cookie和Session缓存用户登录状态 (状态保持)

状态保持和Cookie、Session的关系

* Cookie和Session仅仅是状态保持的一种实现方式而已
* 而能够实现状态保持的方式不仅仅只有Cookie和Session,比如JWT也可以实现状态保持

1.2 应用场景

1.1 登录接口实现的业务逻辑

在整个登录接口实现中,除了检查用户名和密码以外,我们需要通过某种方式来记住用户身份(状态保持),还有用户认证。
1.2 用户下次访问不用登录

例如,在我们登录boss直聘网站后,我们可以不用再次登录就可以直接使用网站,并且网站上有我们的信息

2. cookie

2.1 简介

需求:

  • HTTP协议本身是无状态的,即服务器无法判断用户身份。

问题:
服务器默认是无法识别当前登录用户是谁的,即无法记住登录状态

解决:

  • Cookie
  • 我们可以使用Cookie机制记住用户的登录状态

1. 定义

  • Cookie,最早是网景公司的前雇员 Lou Montulli 在 1993 年 3 月发明的。
  • Cookie,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。
  • cookie 是一个实际存在的东西,是http协议中定义header中的字段,实际上是一小段存储在用户浏览器中的文本信息

2. 具体位置

  • 在浏览器左上角的感叹号或者锁
  • 在检查模式的Application中的Storage里,其中LocalStorage存储永久cookie,SessionStorage存储临时cookie

2.2 机制

  • 客户端向服务器发起请求,如果服务器需要记录该用户状态,就通过响应向客户端浏览器颁发一个Cookie。
    • 该Cookie中需要包含用户的唯一标识信息
  • 客户端浏览器会把Cookie保存起来。
  • 当浏览器再次请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。
  • 服务器检查该Cookie,以此来辨认用户状态。

2.3 特点

  • Cookie,以键值对Key-Value形式进行信息的存储
  • Cookie,基于域名安全,不同域名的Cookie是不能互相访问的
    • 例如: 保存在www.baidu.com域名下面的Cookie是不能被其他域名访问的
  • Cookie,它会自动跟随当前的请求传递到对应域名所在的服务器中。

2.4 操作

1. 设置Cookie

  • 可以通过**HttpResponse()**对象中的set_cookie()方法来设置Cookie。
 # 创建响应对象
response = HttpResponse()
 # 使用响应对象设置Cookie
response.set_cookie(key, value, max_age=cookie有效期)
  • 响应对象:常见的构造响应对象的方式
    • HttpResponse():响应多种数据类型
    • JsonResponse():响应JSON
    • redirect():重定向
    • render():渲染并响应HTML模板
  • max_age:单位为秒,默认为None
    • 如果是临时Cookie,可将max_age设置为None
    • 如果是有具体有效期的Cookie,可将max_age设置为具体的秒数
class BooksView(View):
    """测试模板
    http://127.0.0.1:8000/books/
    """

    def get(self, request):
        # 查询所有图书信息
        books = BookInfo.objects.all()

        # 构造上下文
        context = {
            'books': books
        }
        # 使用上下文渲染'book.html',并返回给客户端
        response = render(request, 'books.html', context)

        # 设置Cookie
        response.set_cookie('name', 'itcast', max_age=None)

        # 响应结果,并写Cookie到浏览器
        return response

2. 读取Cookie

  • 可以通过HttpRequest对象的COOKIES属性来读取本次请求携带的cookie值。
class TestCookieView(View):
    """测试Cookie
    http://127.0.0.1:8000/cookies/
    """

    def get(self, request):
        # 读取Cookie
        name = request.COOKIES.get('name')
        print(name)

        return http.HttpResponse('测试Cookie')

3. session

3.1 简介

需求:
Cookie中存储敏感信息是否安全?
结论:

如果存储前做加密处理,就会是安全的,但是加密后的敏感信息依然会暴露在浏览器中

思考:
有没有一种方式可以将某些敏感数据存储的更加安全些,即加密又不会暴露出去?
解决:

  • Session

1. 定义

  • 原来session是会话的英文,是一个抽象概念,是开发者为了实现中断和继续等操作,将usergent和server之间一对一的交互,抽象为“会话状态”。
  • 现在的session是为了绕开cookie本身和后端存储实现的一种更高级的会话状态实现,概念上cookie和session可以认为是同一层次的概念,也可以是不同层次的概念,具体到实现上session存在是因为sessionID的存在,通常要借助cookie实现,但这并非必要,只能说是通用性较好的一种实现方案
  • 也可以cookie看做一对键值对,cookie是session的key,有cookie里的sessionID才能取到用户信息
  • Session,是一种会话控制方式。由服务端创建,并且保存在服务端的数据存储形式。
  • Session,内部也是以key-value 键值对的形式存储数据。
  • Session,有些内容会被加密,所以可以存储敏感信息。
  • 处理Session数据的子应用:
    在这里插入图片描述
    处理Session的中间件:
    在这里插入图片描述
    PS:如需禁用Session,将上图中的session中间件注释掉即可。

3.2 机制

  • 客户端向服务器发起请求,如果服务器需要记录该用户状态,就可以通过Session在服务端将该用户的唯一标识信息存储起来。
    • session_key:一个随机的唯一的不重复的字符串
    • session_data:用户的唯一标识信息(密文)
  • 然后,服务端会向客户端浏览器颁发一个Cookie。
    • 该Cookie中包含了Session存储数据时使用的那个session_key
      该Cookie的具体形式为:'sessionid': 'session_key'
  • 当浏览器再次请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。
  • 服务器提取该Cookie中的session_key,再使用它提取session_data
  • 最后使用session_data来辨认用户状态

3.3 特点

  • 存储敏感、重要的信息
    • 因为session数据是存储在服务端的,不会直接暴露给用户
  • 相比较Cookie可以存储更多的内容
    • 不同的浏览器,对Cookie都有不同的数量和大小的限制
  • 依赖于Cookie
    • 因为session_key需要存储在Cookie中
  • Session共享
    • 利用独立部署的session服务器(集群)统一管理Session,服务器每次读写Session时,都访问Session服务器。

3.4 cookie和session关系

cookie与session

  • 注意:
    • 但是两个独立的东西,即sessionID虽然存储在cookie中,但绝不依赖cookie
  • 综合考虑
    • 把重要信息存储在session,用户名和sessionID可以放在cookie中
    • 利用时效机制,给cookie设置有效期,比如十四天,然后让用户十四天再登录
    • 可以设置sesssionID传入方式,若有其他方式传入sessinID,cookie完全可以禁用

3.5 操作

1. 设置Session

可以通过 HttpRequest() 对象中的session属性来设置Session。

request.session['key'] = value
class BooksView(View):
    """测试模板
    http://127.0.0.1:8000/books/
    """

    def get(self, request):
        # 查询所有图书信息
        books = BookInfo.objects.all()

        # 构造上下文
        context = {
            'books': books
        }
        # 使用上下文渲染'book.html',并返回给客户端
        response = render(request, 'books.html', context)

        # 设置Cookie
        response.set_cookie('name', 'itcast', max_age=3600)

        # 设置Session
        request.session['name'] = 'itcast'

        # 响应结果,并写Cookie到浏览器
        return response

在这里插入图片描述

2. 读取Session

根据键读取值

request.session.get('key', 默认值)
class TestSessionView(View):
    """测试Session
    http://127.0.0.1:8000/session/
    """

    def get(self, request):
        # 读取Session
        name = request.session.get('name')
        print(name)

        return http.HttpResponse('测试Session')

3. 操作Session的其他方式

清除所有Session,在存储中删除值部分。

request.session.clear()

清除session数据,在存储中删除session的整条数据。

request.session.flush()

删除session中的指定键及值,在存储中只删除某个键及对应的值。

del request.session['key']

设置session的有效期

request.session.set_expiry(value)
  • 如果value是一个整数,session将在value秒没有活动后过期。
  • 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。
  • 如果value为None,那么session有效期将采用系统默认值,默认为两周。
    • 可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值。

3.6 Session数据存储的位置

1. 默认的存储位置

Session数据默认存储的位置是在settings.pyDATABASES配置项指定的SQL数据库中

  • 因为Session引擎默认的配置为:
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
  • 数据库中的表如图所示
    在这里插入图片描述

  • 表结构如下
    在这里插入图片描述

由表结构可知,操作Session包括三个数据:键,值,过期时间。

2. 本地缓存

存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。

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

3. 混合存储

优先从本机内存中存取,如果没有则从数据库中存取。

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

4. Redis

在Redis中保存Session,需要引入第三方扩展,我们可以使用django-redis来解决。

1)安装扩展

pip install django-redis

2)配置

在settings.py文件中做如下设置

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"

3. JWT

3.1 JWT简介

1. Json Web Token(JWT)

  • JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在两个组织之间传递安全可靠的信息。

官方定义:JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties


JWT
JWT是一个有着简单的统一表达形式的字符串:
JWT

2. JWT组成/token格式

  • JWT组成,也可以说是token格式,是有header,payload,signature三部分以点分割的字符串
  • jwt技术特性
  1. 服务器签发,颁发(交)给客户端保存;
  2. 令牌(token)中记录了用户身份信息;
  3. 令牌(token)可以被验证出真伪(有效性可以被校验出来);

单点登录

客户端在某一台服务器(授权中心)完成登录授权,拿到token之后,可以凭借token(令牌)在任意其他服务器中完成身份验证;

2.1 头部(Header)

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。

  • JSON内容要经Base64 编码生成字符串成为Header。
  • 头部(header):typ(类型说明)和alg(加密方法)

作用:转化:

  1. 把header字典转化为json
header = json.dumps(header)
  1. 再把json格式的头信息通过base64比爱你吗得出最终的头信息字符串
herder = base64.b64(header.encode()).decode()
print("header:", header)
2.2 载荷(PayLoad)

payload的五个字段都是由JWT的标准所定义的。
1. iss: 该JWT的签发者
2. sub: 该JWT所面向的用户
3. aud: 接收该JWT的一方
4. exp(expires): 什么时候过期,这里是一个Unix时间戳
5. iat(issued at): 在什么时候签发的

  • JSON内容要经Base64 编码生成字符串成为PayLoad。

作用:存储用户信息

payload = json.dumps(payload)
payload = base64.b64encode.encode().decode()
print("payload:", payload)
2.3 签名(signature)
  • 这个部分header与payload通过header中声明的加密方式,使用密钥secret进行加密,生成签名。 JWS的主要目的是保证了数据在传输过程中不被修改,验证数据的完整性。但由于仅采用Base64对消息内容编码,因此不保证数据的不可泄露性。所以不适合用于传输敏感数据。

作用:校验token真伪

使用哈希算法对信息进行摘要生成签名,目的是保证信息的可靠性;

原理:只要信息没有改动,那么原信息和签名是匹配的

3、token签发

签发

import hmac, hashlib  # hmac:Python的一个用于hash(签名,散列)运算的模块 hashlib:专门实现具体的算法

# 1、构建哈希对象
# key: 加密使用的秘钥--生成签名
# msg: header和payload信息
# digestmod: 算法
key = "eerewrwerwrwewrwegegsf"
SECRET_KEY = ''
message = header + '.' + payload
h_obj = hmac.new(key = SECRET_KEY.encode(), msg=message.encode(), djgestmod=hashlib.sha256)
# 2、找对象里的方法,生成签名
signature = b_obj.hexdigest()
print("signature:", signature)
=======================
JWT_TOKEN = header + '.' + signature
print("token",JWT_TOKEN)
=======================
4、token校验

核心原理:重新对header和payload按照签发时相同的秘钥和算法加密

# 模拟前端传参
token_from_browser = JWT_TOKEN # 有效
token_from_browser = 'fdds' + JWT_TOKEN # 篡改

# 模拟校验原理
# 原理:只要信息没有改动,那么原信息和签名是匹配的
# 只要信息没有被篡改,那么根据相同而秘钥和算法,生成的第三部分签名和原来的签名一定一致

# 1、获取信息(header和payload)
old_header = token_from_browser.split('.',[0])
old_payload = token_from_brower.split('.',[1])
old_signature = token_from_brower.split('.'[2])
# 2、把信息按照相同的秘钥和算法,重新生成新的签名
message = old_header + '.' +old_payload
new_h_obj = hmac.new(key=SECRET.KEY.encode(), msg=message.encode(), digestmod=hashlib.sha256)
new_signature = new_h_obj.hexdigest()
print("新的签名:", new_signature)
# 3、比对新旧签名是否一致:一致则信息没有被篡改,否则信息就被篡改了
if old_signature = new_signature:
	print("验证成功")
	# 提取用户数据
	user_json = base64.b64decode(old_payload.encode().decode())
	user_dict = json.loads(user_json)
	print("解析出来的用户身份信息:", user_dict)
else:
	print("验证失败")

4. PythonLib

4.1 JWT的Python库

4.2 安装

  $ pip install pyjwt

4.3 用例

  >>> import jwt

  >>> encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
  >>> encoded_jwt
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'

  >>> jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
  {'some': 'payload'}
import jwt
from flask import current_app


def generate_jwt(payload, expiry, secret=None):
    """
    生成jwt
    :param payload: dict 载荷
    :param expiry: datetime 有效期
    :param secret: 密钥
    :return: jwt
    """
    _payload = {'exp': expiry}
    _payload.update(payload)

    if not secret:
        secret = current_app.config['JWT_SECRET']

    token = jwt.encode(_payload, secret, algorithm='HS256')
    return token.decode()


def verify_jwt(token, secret=None):
    """
    检验jwt
    :param token: jwt
    :param secret: 密钥
    :return: dict: payload
    """
    if not secret:
        secret = current_app.config['JWT_SECRET']

    try:
        payload = jwt.decode(token, secret, algorithm=['HS256'])
    except jwt.PyJWTError:
        payload = None

    return payload

5. 实施方案

5.1 需求

  • 设置有效期,但有效期不宜过长,需要刷新。如何解决刷新问题?
    1. 手机号+验证码(或帐号+密码)验证后颁发接口调用token与refresh_token(刷新token)
    2. Token 有效期为2小时,在调用接口时携带,每2小时刷新一次
    3. 提供refresh_token,refresh_token 有效期14天
    4. 在接口调用token过期后凭借refresh_token 获取新token
    5. 未携带token 、错误的token或接口调用token过期,返回401状态码
    6. refresh_token 过期返回403状态码,前端在使用refresh_token请求新token时遇到403状态码则进入用户登录界面从新认证。
    7. token的携带方式是在请求头中使用如下格式:
  Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
  • 注意:Bearer前缀与token中间有一个空格

5.2 实现

1. 注册或登录获取token

# toutiao/resources/user/passport.pty
class AuthorizationResource(Resource):
    """
    认证
    """
    def _generate_tokens(self, user_id, with_refresh_token=True):
        """
        生成token 和refresh_token
        :param user_id: 用户id
        :return: token, refresh_token
        """
         # 颁发JWT
        now = datetime.utcnow()
        expiry = now + timedelta(hours=current_app.config['JWT_EXPIRY_HOURS'])
        token = generate_jwt({'user_id': user_id, 'refresh': False}, expiry)
        refresh_token = None
        if with_refresh_token:
            refresh_expiry = now + timedelta(days=current_app.config['JWT_REFRESH_DAYS'])
            refresh_token = generate_jwt({'user_id': user_id, 'refresh': True}, refresh_expiry)
        return token, refresh_token

    def post(self):
        """
        登录创建token
        """
        json_parser = RequestParser()
        json_parser.add_argument('mobile', type=parser.mobile, required=True, location='json')
        json_parser.add_argument('code', type=parser.regex(r'^\d{6}$'), required=True, location='json')
        args = json_parser.parse_args()
        mobile = args.mobile
        code = args.code

        # 从redis中获取验证码
        key = 'app:code:{}'.format(mobile)
        try:
            real_code = current_app.redis_master.get(key)
        except ConnectionError as e:
            current_app.logger.error(e)
            real_code = current_app.redis_slave.get(key)

        try:
            current_app.redis_master.delete(key)
        except ConnectionError as e:
            current_app.logger.error(e)

        if not real_code or real_code.decode() != code:
            return {'message': 'Invalid code.'}, 400

        # 查询或保存用户
        user = User.query.filter_by(mobile=mobile).first()

        if user is None:
            # 用户不存在,注册用户
            user_id = current_app.id_worker.get_id()
            user = User(id=user_id, mobile=mobile, name=mobile, last_login=datetime.now())
            db.session.add(user)
            profile = UserProfile(id=user.id)
            db.session.add(profile)
            db.session.commit()
        else:
            if user.status == User.STATUS.DISABLE:
                return {'message': 'Invalid user.'}, 403

        token, refresh_token = self._generate_tokens(user.id)

        return {'token': token, 'refresh_token': refresh_token}, 201

2. 请求钩子

# common/utils/middlewares.py
from flask import request, g
from .jwt_util import verify_jwt

def jwt_authentication():
    """
    根据jwt验证用户身份
    """
    g.user_id = None
    g.is_refresh_token = False
    authorization = request.headers.get('Authorization')
    if authorization and authorization.startswith('Bearer '):
        token = authorization.strip()[7:]
        payload = verify_jwt(token)
        if payload:
            g.user_id = payload.get('user_id')
            g.is_refresh_token = payload.get('refresh')

3. 强制登录装饰器

# common/utils/decorators.py
def login_required(func):
    """
    用户必须登录装饰器
    使用方法:放在method_decorators中
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        if not g.user_id:
            return {'message': 'User must be authorized.'}, 401
        elif g.is_refresh_token:
            return {'message': 'Do not use refresh token.'}, 403
        else:
            return func(*args, **kwargs)

    return wrapper

4. 更新token接口

# toutiao/resources/user/passport.py
class AuthorizationResource(Resource):
    """
    认证
    """

    ...

    # 补充put方式 更新token接口
    def put(self):
        """
        刷新token
        """
        user_id = g.user_id
        if user_id and g.is_refresh_token:
            token, refresh_token = self._generate_tokens(user_id, with_refresh_token=False)
            return {'token': token}, 201
        else:
            return {'message': 'Wrong refresh token.'}, 403

6. 禁用问题

6.1 需求

  • token颁发给用户后,在有效期内服务端都会认可,但是如果在token的有效期内需要让token失效,该怎么办?
  • 此问题的应用场景:
    • 用户修改密码,需要颁发新的token,禁用还在有效期内的老token
    • 后台封禁用户
  • 伪代码思路如下:
    在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进行请求时,如果验证token通过,则从redis中判断是否存在该用户的user:{}:token记录:
    • 若不存在记录,放行,进入视图进行业务处理
    • 若存在,则对比本次请求的token是否在redis保存的set中:
      • 若存在,则放行
      • 若不在set的数值中,则返回403状态码,不再处理业务逻辑
key = 'user:{}:token'.format(user_id)
valid_tokens = redis_client.smembers(key)

# 若上传的token不在白名单列表valid_tokens内,返回403要求重新登录
if valid_tokens and token not in valid_tokens:
  return {'message': 'Invalid token'.}, 403

说明:
1. redis记录设置有效期的时长是一个token的有效期,保证旧token过期后,redis的记录也能自动清除,不占用空间。
2. 使用set保存新token的原因是,考虑到用户可能在旧token的有效期内,在其他多个设备进行了登录,需要生成多个新token,这些新token都要保存下来,既保证新token都能正常登录,又能保证旧token被禁用

6.2 具体代码实现

1. 修改密码接口

class ModifyPasswordResource(Resource):

    # 修改密码要求用户必须登录
    method_decorators = [login_required]

    def post(self):
        """修改密码逻辑"""

        # 1.获取用户id
        user_id = g.user_id

        # 2.保存到redis中的key
        key = 'user:{}:token'.format(user_id)

        # 3.获取管道对象
        pl = current_app.redis_master.pipeline()

        # 4.先删除已有的白名单
        if pl.exists(key):
            pl.delete(key)

        # 5.生成新的token值
        new_token, refresh_token = self._generate_tokens(user_id)

        # 6.sadd 往集合中添加成员--添加修改密码后的新的token值
        pl.sadd(key, new_token)

        # 7.设置2小时有效期
        pl.expire(key, 7200)

        # 8.设置修改密码的标志位,同时设置2小时有效期
        modify_key = "modify:{}".format(user_id)
        pl.setex(modify_key, 7200, "modify")

        # 9.执行管道
        pl.execute()

        # 10.修改密码成功,并返回新的token值
        return {"message": "修改密码成功", "valid_token": new_token}

2. 在middleware 的def jwt_authorization():方法中补充验证白名单的逻辑

def jwt_authorization():
    """
    每次请求之前进行jwt验证
    :return:
    """

    # 请求头Header携带
    # {Authorization: "Bearer jwt_token"}
    # 默认值
    g.user_id = None
    g.is_refresh = False

    # 1.提取请求头中的token数据
    header_token = request.headers.get("Authorization")

    # 2.截取真正的jwt-token值
    if header_token is not None and header_token.startswith("Bearer "):
        jwt_token = header_token[7:]

        # 3.jwttoken的校验 --- 如果token没有值 或者过期了 自动会抛出401异常
        payload = verify_jwt(token=jwt_token)

        # 4.获取载荷中的用户信息,使用g对象存储用户信息
        if payload is not None:
            g.user_id = payload.get("user_id")
            g.is_refresh = payload.get("is_refresh", False)



   # ========================【白名单验证逻辑】================================
    if g.user_id:

        # 根据key获取是否进行过密码修改的标志位
        modify_key = "modify:{}".format(g.user_id)
        modify = current_app.redis_master.get(modify_key)

        # 如果是修改过密码,同时又不是刷新token,需要进行白名单认证
        if modify == 'modify' and g.is_refresh is False:
            # 白名单认证
            ret = white_list()
            # 如果认证结果为False 代表token没有在白名单内部
            if ret is False:
                return "token is not in white list", 403

3. 判断用户上传的token是否在白名单列表中

def white_list():
    """
    判断用户上传的token是否在白名单列表中
    如果在:正常访问
    如果不在:拦截不让访问视图函数,需要使用修改密码后生成的最新的token才能访问
    :return: BooL值 False代表token没有在白名单内部
    """
    # 用户id
    user_id = g.user_id
    key = 'user:{}:token'.format(user_id)
    # 获取白名单内部的token
    # smembers 获取集合中的成员
    valid_tokens = current_app.redis_master.smembers(key)

    # set类型数据 装的bytes类型数据
    print(type(valid_tokens))

    # 把set、转换成python的列表
    valid_tokens = list(valid_tokens)

    # 提取前端发送的token
    header_token = request.headers.get("Authorization")
    token = header_token[7:]

    # 判断前端发送的token值是否在白名单列表中,如果不在返回403 重新登录
    if valid_tokens and token not in valid_tokens:
        return False
    else:
        return True

6.3 思路流程图

JWT禁用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值