业务逻辑
1:用户登录,使用jwt生成token,
2:后端把这个token返回给前端,同时把这个token作为key存储在redis中,用户对象作为value。并设置一个过期时间。
3:用户以后每次访问都携带这个token,如果这个token在redis中存在就直接通过拦截器,不存在就使用jwt进行解析,如果jwt验证通过就认证通过,(这里有一个问题,就是如果这个用户已经离开了但是没有主动退出登录,导致redis中的token存在,而这个用户的token被别人盗用,那么由于我这个token设置的有效期是1天,即使所以风险还是很小的。那我这个时候又不要把token继续存到redis里面呢?是要存的,因为redis里的token存活时间是5小时,这个时间是在安全性和访问速度是取一个均衡。如果这个用户一天都没有离开token也会过期,那么它就要每次都进行jwt解析就无法利用redis的高效率了。还有就是即使存了,被别人盗用了token,也会在5小时之后过期,还是比较安全的。)如果jwt无法解析,无法通过拦截器。要求用户重新登录。登录后把token重新存入到redis中。
4:用户退出登录的时候把对应redis中的token删除。
部分代码实现
拦截器的配置
package com.dongmu.interceptor;
import com.dongmu.controller.LoginController;
import com.dongmu.dao.UserMapper;
import com.dongmu.pojo.User;
import com.dongmu.util.JwtUtil;
import com.dongmu.util.RedisUtil;
import com.dongmu.util.TokenUtil;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class Interceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(Interceptor.class);
@Autowired
RedisUtil redisUtil;
@Autowired
UserMapper userMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = TokenUtil.getToken(request);
if (token==null){
logger.info("未通过拦截器,身份验证失败了。");
return false;
}else {
if (redisUtil.hget("token",token)!=null){
//redis中存在就直接验证通过
logger.info("通过拦截器,身份验证成功了。");
return true;
}else {
try {
///redis不存在就解析token,并把结果存到redis中
Claims claims = JwtUtil.parseJWT(LoginController.token_password, token);
User user = userMapper.selectByUserName((String) claims.get("username"));
redisUtil.hset("token",token,user,1000*60*60*5);
logger.info("通过拦截器,身份验证成功了。");
return true;
}catch (Exception e){
logger.info("未通过拦截器,身份验证失败了。");
return false;
}
}
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
配置到拦截器中
package com.dongmu.config;
import com.dongmu.interceptor.Interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InteceptorConfig implements WebMvcConfigurer {
@Autowired
Interceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
String path = "/**";
String excludePath="/login";
String swaggerExcludePath[] = {"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"};
// HandlerInterceptor interceptor = new Interceptor();
registry.addInterceptor(interceptor).addPathPatterns(path).excludePathPatterns(excludePath).excludePathPatterns(swaggerExcludePath);
}
}
假如Reids服务关闭了,会存在什么问题?怎么解决?
如果redis服务关闭,就会导致spring一直重连redis,导致原来的服务不可用。
解决办法:
配置连接超时时间,设置短一点,同时包装redis封装工具类查询出现错误就返回null,
查询redis的时候如果返回null再查询数据库。
配置如下:
spring:
redis:
port: 6379
host: 127.0.0.1
database: 0
password:
# 设置最大的连接超时时间
timeout: 20ms
lettuce:
pool:
# 最大连接池的数量,负数表示没有限制,默认为8
max-active: 20
# 连接池最大阻塞等待时间,负数表示没有限制
max-wait: 1ms
# 连接池最大连接数,默认为8
max-idle: 8
# 连接池最小连接数,默认为0
min-idle: 0
添加的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<!-- <version>2.0</version>-->
</dependency>
如果手动设置版本号,可能会出现依赖冲突,所以这里注释了。
如果想要只有一个用户登录怎么办?
那就把redis的key存为userId,第二个登录就会把第一个给替换掉。