• 禁用token及无感知更新token功能实现


    禁用token

    禁用token通俗的讲就是将登录后保存到浏览器上的token退出登录时也保存到redis里
    然后利用钩子函数在每次请求前都判断一次,如果再次请求的token与redis里的token进行对比
    如果两者一直说明token没有更新,很可能是token泄露被人恶意使用,
    所以每次退出都要禁用token
    紧接着强制登录后 实现禁用token
    在项目内蓝图中创建

    @user_dp.before_app_request
    def gz():   #钩子函数 每次请求前都调用
        rep = reqparse.RequestParser()
        rep.add_argument('token', location='headers')
        args = rep.parse_args()
        token = args['token']
        payload = JwtTool().valid(token)  #解密token
    
        ## 放入钩子函数里验证token是否存在
        rds = SmsTool().rds  # 已经封装好的redis数据库信息
        is_exists = rds.exists('token_%s' % token)
        rds.close()
        if is_exists:
            g.uid = -1
        else:
            try:
                g.uid=payload['uid']
            except Exception as a:
                print(a)
                g.uid=0
          
    def login(func):
        def warpper(*args,**kwargs):
            #判断用户是否登录
            uid=g.uid
            if uid==0:
                return jsonify({
                    'code':403,
                    'msg':'用户未登录'
                })
            elif uid==-1:          #验证token是否在redis里面
                return jsonify({
                    'code':400,
                    'msg':'token被禁用'
                })
            return func(*args,**kwargs)
        return warpper
        
    #退出登录 前端退出登录先调用这个 然后再清空token
    class TokenView(Resource):
        @login
        def post(self):
            rep = reqparse.RequestParser()
            rep.add_argument('token', location='headers')
            args = rep.parse_args()
            token = args['token']
            rds=SmsTool().rds    #已经封装好的redis数据库信息
            rds.set('token_%s'%token,'1',ex=3600)
            rds.close()
            return jsonify({
                'code':200,
                'msg':'退出成功'
            })
    
    api.add_resource(TokenView,'/token')  #退出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    无感知更新token

    无感知更新token就是token的有效期不长才安全,就需要频繁的更新,让它自己更新就简单很多
    在登录的蓝图中进行编写

    @user_dp.before_app_request
    def gz():
        rep = reqparse.RequestParser()
        rep.add_argument('token', location='headers')
        args = rep.parse_args()
        token = args['token']
        payload = JwtTool().valid(token)
    
        ## 放入钩子函数里验证token是否存在
        rds = SmsTool().rds  # 已经封装好的redis数据库信息
        is_exists = rds.exists('token_%s' % token)
        rds.close()
        if is_exists:
            g.uid = -1
        else:
            try:
                g.uid=payload['uid']
            except ExpiredSignatureError:
                g.uid=-2    #如果有其他的报错就是-2
            except Exception as a:
                print(a)
                g.uid=0
    def login(func):
        def warpper(*args,**kwargs):
            #判断用户是否登录
            uid=g.uid
            if uid==0:
                return jsonify({
                    'code':403,
                    'msg':'用户未登录'
                })
            elif uid==-1:          #验证token是否在redis里面
                return jsonify({
                    'code':400,
                    'msg':'token被禁用'
                })
            elif uid==-2:     #验证是否已过期
                return jsonify({
                    'code':666,
                    'msg':'token已过期'
                })
            return func(*args,**kwargs)
        return warpper
        
    class LoginView(Resource):  #登录
        def post(self):
            rep=reqparse.RequestParser()
            rep.add_argument('phone')
            rep.add_argument('password')
            args=rep.parse_args()
            user_info=UserModel2.query.filter(UserModel2.mobile==args['phone']).first()
            if not user_info:
                return jsonify({
                    'code':400,
                    'msg':'该用户手机号不存在'
                })
            if user_info.password!=args['password']:
                return jsonify({
                    'code':400,
                    'msg':'密码不正确'
                })
            token=JwtTool().create({    #短token
                'username':user_info.username,
                'uid':user_info.id,
                'exp':int(time.time()+360)  #加上一个有效期360秒
            })
            long_token=JwtTool().create({    #长token
                'username':user_info.username,
                'uid':user_info.id,
                'long':True, #这个标志是用来区分长token的 目的是让token不能进行普通接口的校验 只用于特定接口的校验
                'exp':int(time.time()+15*24*3600)  #加上一个有效期15天
            })
    		return jsonify({
                'code':200,
                'msg':'登陆成功',
                'data':{
                    'username':user_info.username,
                    'token':token,   #短token         #登录的时候两个都保存
                    'long_token':long_token   #长token
                }
            })
     #退出登录 前端退出登录先调用这个 然后再清空token
    class TokenView(Resource):
        def put(self):   #更新token
            #获取参数
            rep = reqparse.RequestParser()
            rep.add_argument('longtoken', location='headers')
            args = rep.parse_args()
    
            #校验参数
            try:
                payload=JwtTool().valid(args['longtoken'])  #封装的解密token
            except Exception as a:
                return jsonify({
                    'code':403,
                    'msg':'用户未登录'
                })
            #逻辑
            user_info = UserModel2.query.get(payload['uid'])
            #再生成token
            token = JwtTool().create({  # 利用封装的生成token函数
                'username': user_info.username,
                'uid': user_info.id,
                'exp': int(time.time() + 360)  # 加上一个有效期360秒
            })
            #返回响应
            return jsonify({
                'code':200,
                'msg':'token更新成功',
                'data':{
                    'token':token
                }
            })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    前端在main.js里进行编写

    //强制登录
    axios.interceptors.response.use(function(resp){
        console.log('resp===',resp)  //resp 就是请求响应回来的数据
        // 未登录用户 直接跳转到登录页面
        if(resp.data.code==403){
            router.push('/login')  //此处无法使用 this.$router
        }
        return resp  //把请求对象返回回去
    },function(error){
        console.log(error)
    })
    
    // 无感知更新token
    axios.interceptors.response.use(function(resp){
        console.log(resp)
        if(resp.data.code==666){
            // 代表token过期了
            //重新请求token
            reloadToken()
        }
        return resp
    },function(error){
        console.log(error)
    })
    //重新加载token
    function reloadToken(){
        axios.put('/user/token',{},{
            'headers':{
                'longtoken':localStorage.getItem('long_token')
            }
        }).then((result) => {
            if (result.data.code==200){
                localStorage.setItem('token',res.data.data.token)
                window.location.reload()  //网页刷新
            }
        }).catch((err) => {
            console.log(err)
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    2023/10/4 -- ARM
    vue3 + vite + ts + setup , 第十七练 vue3 中使用vue-router(一),router跳转传参/嵌套路由/路由重定向/别名
    Java23种设计模式-结构型模式之外观模式
    主流分布式存储技术对比分析:GFS、HDFS、GlusterFS、Ceph、Swift
    容器化技术最佳实践2--制作自己的Docker镜像
    Spring Cloud OpenFeign模版化客户端
    Chapter 11 EM算法
    基于JavaWeb+MySQL的简历信息管理系统
    资深架构大佬用七部分共十三章节,一次讲透 SpringBoot 生态体系~
    Spring Security配置
  • 原文地址:https://blog.csdn.net/qishaoawei/article/details/126129349