大家好,今天我又又又来了,hhhhh。
上文中 我们永redis缓存了token 但是我们发现了 一个bug ,redis中缓存的token 是单用户才能实现的。
就是 我 redis中存储的键 名 为token 值 是jwt令牌 ,但是如果 用户a 登录 之后 创建一个 键 为token的 键值对,如果用户b登录,创建的的键名也是 token ,这样用户b的 jwt 会覆盖 用户a的,就会导致 用户a 的token 会失效呀,这真是一个大大的bug , 按照我目前的水平,我想到了一下 的解决方案 ,既然 token 的键会覆盖,那么我们给 token的键 加上一个唯一标识不就好了
解决前的代码
- 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
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
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;
- }
- }
解决前的登陆代码
- 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
userLoginLambdaQueryWrapper = new LambdaQueryWrapper() - .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
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 存在 我们也就设置成功了