redis 缓存jwt令牌设置更新时间 BUG修复

大家好,今天我又又又来了,hhhhh。

上文中 我们永redis缓存了token 但是我们发现了 一个bug ,redis中缓存的token 是单用户才能实现的。

就是 我 redis中存储的键 名 为token 值 是jwt令牌 ,但是如果 用户a 登录 之后 创建一个 键 为token的 键值对,如果用户b登录,创建的的键名也是 token ,这样用户b的 jwt 会覆盖 用户a的,就会导致 用户a 的token 会失效呀,这真是一个大大的bug , 按照我目前的水平,我想到了一下 的解决方案 ,既然 token 的键会覆盖,那么我们给 token的键 加上一个唯一标识不就好了

解决前的代码

java 复制代码
package com.example.getway.globalfilter;

import cn.hutool.core.collection.CollUtil;
import com.example.getway.Untils.JwtUntils;
import com.example.getway.commen.MessageConstant;
import com.example.getway.pojo.JwtProperties;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
@Slf4j
@Component
public class TokenGlobalFilter implements GlobalFilter, Ordered {
    private  final JwtProperties jwtProperties;
    private  final   RedisTemplate redisTemplate;
    // 这个过滤器 请求的转发
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //我们只要是非登录请求全都要检验jwt 然后进行 用户信息的传递
       //获取request对象
        ServerHttpRequest request = exchange.getRequest();
        RequestPath requestPath = request.getPath();
        if(requestPath.toString().contains("/userLogin")){
            return  chain.filter(exchange);
        }
        //获取请求头的 token
//        String redisToken =null;
//        List<String> authorization = request.getHeaders().get("authorization");
//        if (!CollUtil.isEmpty(authorization)) {
//            redisToken = authorization.get(0);
//        }

        String redisToken = (String)redisTemplate.opsForValue().get(MessageConstant.TOKEN);
        log.info("token:{}",redisToken);
        if (redisToken != null ) {
            //进行jwt的解析
            try {
                Claims claims = JwtUntils.parseJwt(redisToken, jwtProperties.getSecretkey());
                //每次 访问其他资源的时候 都把token更新
                redisTemplate.expire(MessageConstant.TOKEN, 1000, TimeUnit.DAYS);
                String loginId = claims.get(MessageConstant.LOGIN_ID).toString();
                log.info("网关层当前用户的id:{}", Long.valueOf(loginId));
                //证明 token有效 传递用户信息
                ServerWebExchange loginId1 = exchange.mutate()
                        .request(b -> b.header("loginId", loginId))
                        .build();
                return chain.filter(loginId1);
            } catch (Exception e) {
                log.info("{}",e.getMessage());
                //出现异常返回一个异常响应
                ServerHttpResponse response = exchange.getResponse();
                response.setRawStatusCode(401);
                return  response.setComplete();
            }
        }
        log.info("token错误");
       return  exchange.getResponse().setComplete();
    }
//过滤器链中的优先级 数值越低 优先级就越高
    @Override
    public int getOrder() {
        return 0;
    }
}

解决前的登陆代码

java 复制代码
package com.example.logindemo.cotroller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.logindemo.Untils.JwtUntils;
import com.example.logindemo.commen.MessageConstant;
import com.example.logindemo.pojo.JwtProperties;
import com.example.logindemo.pojo.po.UserLogin;
import com.example.logindemo.result.Result;
import com.example.logindemo.service.UserLoginService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
@RequiredArgsConstructor
public class LoginController {
    private  final UserLoginService userLoginService;
    private final JwtProperties jwtProperties;
    private  final RedisTemplate redisTemplate;
    @PostMapping("/userLogin")
    public Result userLogin( String username,  String password) {
          password=DigestUtils.md5DigestAsHex(password.getBytes());
        LambdaQueryWrapper<UserLogin> userLoginLambdaQueryWrapper = new LambdaQueryWrapper<UserLogin>()
                .eq(UserLogin::getUsername, username);
        UserLogin userLogin = userLoginService.getOne(userLoginLambdaQueryWrapper);
        if(userLogin==null && userLogin.getUsername().isEmpty()){
            return Result.error("查询不到用户");
        }
        if (username.equals(userLogin.getUsername()) && password.equals(userLogin.getPassword())) {
         //需要一个map集合  传什么 解析出来什么   一般传的是登录用户的id  我们传1
            HashMap<String, Object> map = new HashMap<>();
            map.put(MessageConstant.LOGIN_ID, userLogin.getId());
            String token = JwtUntils.CreateJwt(map, jwtProperties.getSecretkey());
            //设置redis缓存为1000天
            redisTemplate.opsForValue().set(MessageConstant.TOKEN,token,1000,TimeUnit.DAYS);
            return Result.success(token);
        }
        return Result.error("未知错误");
    }

}

解决后 的登录代码 这里只放修改部分

解决后的 网关层过滤代码

现在 我们手动在数据库添加 一个用户

我们apifox进行登录接口的依次登录 ,然后观察 redis中的缓存数据

发现token 2 存在 我们也就设置成功了

相关推荐
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
Code apprenticeship5 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站5 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶5 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
fpcc7 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
Ewen Seong7 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
安全二次方security²7 小时前
SMMU软件指南SMMU编程之虚拟机结构和缓存
缓存·cache·smmu·arm安全架构·系统mmu·虚拟机结构·vms
黄名富9 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
G_whang10 小时前
centos7下docker 容器实现redis主从同步
redis·docker·容器