概念:
1.principals:身份,用户的唯一标识属性,如账号名称,邮箱,手机号等.
2.credentials: 凭证,即只有绑定对应的用户知道的安全保密值,如密码,短信验证码,数字签证书等
常见的principals和credentials就是账号/密码
1.刷新token拦截器
package com.hmdp.interceptor;
import com.hmdp.constant.RedisConstant;
import com.hmdp.dto.UserDTO;
import com.hmdp.utils.JacksonUtil;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
/**
* User: ldj
* Date: 2023/4/1
* Time: 15:55
* Description: 刷新token拦截器
*/
@Slf4j
@Component
public class RefreshTokenInterceptor implements HandlerInterceptor {
public static final String AUTHORIZE_TOKEN = "authorization";
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//尝试从请求头/路径参数/Cookie获取token
String token = request.getHeader(AUTHORIZE_TOKEN);
if (StringUtils.isBlank(token)) {
token = request.getParameter(AUTHORIZE_TOKEN);
}
if (StringUtils.isBlank(token)) {
Cookie[] cookies = request.getCookies();
if (cookies.length > 0) {
for (Cookie cookie : cookies) {
if (AUTHORIZE_TOKEN.equalsIgnoreCase(cookie.getName())) {
token = cookie.getValue();
}
}
}
}
if(StringUtils.isBlank(token)){
return true;
}
//过期时间是30分钟
String redisKey = RedisConstant.REDIS_TOKEN_PREFIX + token;
BoundValueOperations<String, String> operations = stringRedisTemplate.boundValueOps(redisKey);
String userJson = operations.get();
if (StringUtils.isBlank(userJson)) {
return true;
}
//保存到ThreadLocal
UserDTO userDTO = JacksonUtil.readValue(userJson, UserDTO.class);
log.info("[userDTO]:{}", userDTO);
UserHolder.saveUser(userDTO);
//刷新token的有效期
operations.expire(RedisConstant.REDIS_TOKEN_TTL, TimeUnit.MINUTES);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
UserHolder.removeUser();
}
}
2.登录拦截器
package com.hmdp.interceptor;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* User: ldj
* Date: 2023/4/1
* Time: 1:12
* Description: 基于Redis登录拦截器
*/
@Slf4j
@Component
@RefreshScope
public class LoginInterceptor implements HandlerInterceptor {
@Value("#{'${spring.loginInterceptor.excludePathPatterns}'.split(',')}")
private List<String> excludePathPatterns;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
AntPathMatcher antPathMatcher = new AntPathMatcher();
//放行不需要登录的请求
if (excludePathPatterns.size() > 0) {
for (String excludePathPattern : excludePathPatterns) {
if (antPathMatcher.match(excludePathPattern, uri)) {
return true;
}
}
}
//判断ThreadLocal中是否有用户数据
if (UserHolder.getUser() == null) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
return true;
}
}
WebConfig
package com.hmdp.config;
import com.hmdp.interceptor.LoginInterceptor;
import com.hmdp.interceptor.RefreshTokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* User: ldj
* Date: 2023/4/1
* Time: 1:29
* Description: No Description
*/
@RefreshScope
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Autowired
private RefreshTokenInterceptor refreshTokenInterceptor;
//order越小优先级越高,不设置顺序,默认按插入顺序执行
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(refreshTokenInterceptor).addPathPatterns("/**").order(0);
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").order(1);
}
}
工具类列举其中之一
package com.hmdp.utils;
import com.hmdp.dto.UserDTO;
public class UserHolder {
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
public static void saveUser(UserDTO user){
tl.set(user);
}
public static UserDTO getUser(){
return tl.get();
}
public static void removeUser(){
tl.remove();
}
}
常量类
package com.hmdp.constant;
/**
* User: ldj
* Date: 2023/4/1
* Time: 3:18
* Description: No Description
*/
public class RedisConstant {
public static final Integer REDIS_CODE_TTL = 60;
public static final Integer REDIS_TOKEN_TTL = 30;
public static final String REDIS_CODE_PREFIX = "login:code";
public static final String REDIS_TOKEN_PREFIX = "login:token";
}
application.properties
spring.loginInterceptor.excludePathPatterns=/user/code,/user/login,/shop-type/**,/shop/**,/blog/hot