解决问题
import com.tryonce.config.auth.UserInfoHolder;
import com.tryonce.config.exception.AuthException;
import com.tryonce.dto.WebUserBase;
import com.tryonce.type.OsType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
public class GlobalInterceptor implements HandlerInterceptor {
/**
* 业务处理前执行(controller代码执行前)
* 1、ThreadLocal赋值:请求时间戳、获取Header信息等
* 2、打印请求参数信息
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws AuthException {
// 1、设置请求开始时间
log.info("[set-ThreadLocal] GlobalThreadLocal.reqStartTime:{}", Thread.currentThread());
GlobalThreadLocal.reqStartTime.set(System.currentTimeMillis());
// 2、打印请求信息
log.info("Interceptor.preHandle:{}", Thread.currentThread());
log.info("Request URL : {}", request.getRequestURL().toString());
log.info("HTTP Method : {}", request.getMethod());
log.info("Request IP : {}", request.getRemoteAddr());
// 3、从Header获取数据并存到ThreadLocal
WebUserBase webUserBase = WebUserBase.builder()
.os(OsType.android.getType())
.product(request.getHeader("product"))
.androidId(request.getHeader("androidId"))
.deviceId(request.getHeader("deviceId"))
.oaid(request.getHeader("oaid"))
.appFlag(null == request.getHeader("appFlag") ? null : Integer.valueOf(request.getHeader("appFlag")))
.build();
UserInfoHolder.setWebUserBase(webUserBase);
log.info("WebUserBase : {}", UserInfoHolder.getWebuserBase());
return true;
}
/**
* 业务处理后,返回浏览器之前
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
/**
* 返回浏览器时执行
* 1、清除ThreadLocal数据
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 1、移除ThreadLocal - 请求时间
log.info("[remove-ThreadLocal] GlobalThreadLocal.reqStartTime:{}", Thread.currentThread());
GlobalThreadLocal.reqStartTime.remove();
// 2、移除ThreadLocal - 用户信息
log.info("[remove-ThreadLocal] UserInfoHolder {} . ", Thread.currentThread());
UserInfoHolder.remove();
}
}
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.tryonce.config.auth.UserInfo;
import com.tryonce.config.auth.UserInfoHolder;
import com.tryonce.config.exception.AuthException;
import com.tryonce.service.AuthService;
import com.tryonce.tools.IP;
import com.tryonce.vo.ResponseEnum;
import com.tryonce.vo.ResponseVo;
import com.tryonce.vo.auth.AuthLoginDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
public class LoginAuthInterceptor implements HandlerInterceptor {
@Autowired
private AuthService authService;
/**
* 业务处理前执行(controller代码执行前)
* 1、权限校验
* 2、刷新token缓存
* 3、记录日志
* ......
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws AuthException {
// 1、权限校验
String token = request.getHeader("Authorization");
AuthLoginDto authLoginDto = authService.validLoginUser(token);
if (null == authLoginDto) {
log.info("不存在该用户或用户登录已过期,请重新登录!token : {},url : {}", token, request.getRequestURI());
ServletUtil.write(response, JSON.toJSONString(ResponseVo.buildResponse(ResponseEnum.AUTH.getCode(), "不存在该用户或用户登录已过期,请重新登录!", null)), MediaType.APPLICATION_JSON_UTF8_VALUE);
return false;
} else {
// 2、设置用户信息
UserInfoHolder.set(new UserInfo(authLoginDto, IP.get(request), UserInfoHolder.getWebuserBase()));
}
// 3、若 sa-token 未登录,则进行登录
if (!StpUtil.isLogin()) {
StpUtil.login(UserInfoHolder.getUserCode());
}
log.info("[set-ThreadLocal] UserInfoHolder:{} - ", Thread.currentThread(), UserInfoHolder.get());
return true;
}
/**
* 业务处理后,返回浏览器之前
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
/**
* 返回浏览器时执行(全局拦截器已清除,故此处不做处理)
* 1、清除threadLocal
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private LoginAuthInterceptor loginAuthInterceptor;
@Autowired
private GlobalInterceptor globalInterceptor;
/**
* 无需权限拦截的请求
*/
private static List<String> EXCLUDE_PATH_AUTH = Arrays.asList(
// 登录、退出
"/auth/login", "/auth/logout",
// swagger2
"/doc.html", "/v2/api-docs", "/v2/api-docs-ext", "/swagger-resources", "/swagger-ui.html", "/configuration/ui", "/configuration/security",
// 静态资源
"/*.css", "/*.js", "/*.png", "/*.jpg", "/*.jpeg", "/webjars/**", "/favicon.ico", "/error", "/static/**"
);
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/**
* 添加拦截器
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
log.info("********************* 加入拦截器 LoginAuthInterceptor ******************");
// 注册全局拦截器
registry.addInterceptor(globalInterceptor).addPathPatterns("/**");
// 注册权限拦截器
registry.addInterceptor(loginAuthInterceptor).addPathPatterns("/**").excludePathPatterns(EXCLUDE_PATH_AUTH);
// 注册注解拦截器,并排除不需要注解鉴权的接口地址 (与登录拦截器无关) - Sa-Token
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**").excludePathPatterns(EXCLUDE_PATH_AUTH);
super.addInterceptors(registry);
}
@Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
// 如果为true 会识别 xx.* 后缀的内容
configurer.setUseSuffixPatternMatch(false);
super.configurePathMatch(configurer);
}
/**
* 数据转换
* 1、解决Long类型返回前端精度丢失
* 2、统一对日期做日期格式化
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
// 时间格式化
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// Long转json精度丢失的配置
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(0, jackson2HttpMessageConverter);
}
}
import com.alibaba.fastjson.JSON;
import com.tryonce.vo.ResponseVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
@Slf4j
public class ResponseControllerAdvice implements ResponseBodyAdvice<ResponseVo> {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
if (returnType.getParameterType() == ResponseVo.class) {
return true;
}
return false;
}
/**
* 返回前端前记录响应信息及耗时
*/
@Override
public ResponseVo beforeBodyWrite(ResponseVo body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
Long startTime = GlobalThreadLocal.reqStartTime.get();
String res = JSON.toJSONString(body);
log.info("[END] uri:[{}] , costTime:[{}] , return:[{}]", request.getURI().toString(), (startTime == null ? 0 : System.currentTimeMillis() - startTime), res.length() > 1000 ? res.substring(0, 1000) + "......" : res);
GlobalThreadLocal.reqStartTime.remove();
return body;
}
}