Spring Boot拦截器实现并和swagger集成后使用拦截器的配置问题
1. 定义拦截器 LoginInterceptor
LoginInterceptor.java是整个登录认证模块中的核心类之一,它实现了HandlerInterceptor
类,由它来拦截并过滤到来的每一个请求;它的三个方法能分别作用于每个请求的不同生命周期,你可以根据自己的需要来加入相应的处理逻辑
package com.demo.common.interceptor; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import lombok.extern.slf4j.Slf4j; import org.springframework.web.method.HandlerMethod; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; import java.util.List; import java.util.Map; /** * @ProjectName: demo * @Package: com.demo.common.interceptor * @ClassName: LoginInterceptor * @Description: 登录请求拦截器 * @Author: * @Date: * @Version: 1.0 */ @Slf4j public class LoginInterceptor implements HandlerInterceptor { /** * 在请求被处理之前调用 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestMethod = request.getMethod(); if (requestMethod.contains("OPTIONS") || requestMethod.contains("options")) { return true; } //token 校验 String token = request.getHeader("token"); if (StringUtil.isEmpty(token)) { token = request.getHeader("token-inc"); } //获取请求页面id String pageId = ""; Map<String, String[]> paramMap = request.getParameterMap(); if (CollectionUtils.isNotEmpty(paramMap)) { if (paramMap.containsKey("page_id")) { pageId = paramMap.get("page_id")[0]; } } //验证token是否有效 boolean checkToken = this.checkToken(token, pageId); if (!checkToken) { //未登录就跳转到登录页面 //response.sendRedirect(LOGIN_HOST + "login"); Result<String> resultObject = Result.fail("10001", "登录超时,请刷新页面重新登录"); PrintWriter writer = response.getWriter(); writer.write(JSON.toJSONString(resultObject)); writer.flush(); writer.close(); return false; } //参数日志打印 if (handler instanceof HandlerMethod) { this.saveRequestLog(request); } return true; } /** * 在请求被处理后,视图渲染之前调用 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { //Collection<String> headerNames = response.getHeaderNames(); //log.info("[postHandle]{}", JsonUtils.toJson(headerNames)); } /** * 在整个请求结束后调用 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { UserInfoHolder.removeUserInfo(); } /** * 请求校验token 塞入ThreadLocal * * @param token * @return */ private boolean checkToken(String token, String pageId) { //不需要验证,直接通过,使用于直接请求接口(测试用) if ("false".equals(ENABLE_TOKEN) || NOT_VAILE_TOKEN.equals(token)) { return true; } if (StringUtil.isEmpty(token)) { return false; } //请求获取登录用户信息及权限 HttpBO<UserInfoBO> httpBO = this.httpUserInfo(token, pageId); if (httpBO == null || httpBO.getCode() == null || httpBO.getCode() != 0 || httpBO.getData() == null) { return false; } this.saveLoginContext(httpBO.getData()); return true; } private HttpBO<UserInfoBO> httpUserInfo(String token, String pageId) { HttpBO<UserInfoBO> httpBO = new HttpBO<>(); try { LoginQuery loginQuery = new LoginQuery(); loginQuery.setCookieStr(token); loginQuery.setPageId(pageId); String url = API_HOST + USER_URL; String jsonUserInfoBo = JSON.toJSONString(loginQuery); log.info("发送请求获取权限参数信息 param:{},url:{}", jsonUserInfoBo, url); String response = HttpClientPostUtil.sendPostRequest(url, jsonUserInfoBo); log.info("发送请求获取权限返回信息 response={}", response); Type type = new TypeReference<HttpBO<UserInfoBO>>() { }.getType(); httpBO = JSON.parseObject(response, type); //httpBO = JSON.parseObject(response, new HttpBO<UserInfoBO>().getClass()); log.info("发送请求获取权限信息 封装 httpBO:{}", JSON.toJSONString(httpBO)); } catch (Exception ex) { log.error("发送请求获取权限失败 error:{}", ex.getMessage()); } return httpBO; } private void saveLoginContext(UserInfoBO userInfoBO) { //List<ButtonBO> buttonBOList = userInfoBO.getUserBtns(); //if (CollectionUtils.isEmpty(buttonBOList)) { // List<String> code = buttonBOList.stream().map(ButtonBO::getBtnCode).collect(Collectors.toList()); //} UserInfoHolder.setUserInfo(userInfoBO); } private void saveRequestLog(HttpServletRequest request) throws Exception { StringBuilder builder = new StringBuilder(); builder.append("请求入口 "); builder.append("PATH => "); builder.append(request.getRequestURI()); builder.append(",METHOD => "); builder.append(request.getMethod()); builder.append(",PARAM => "); Map<String, String[]> paramMap = request.getParameterMap(); if (CollectionUtils.isNotEmpty(paramMap)) { builder.append(JSON.toJSONString(paramMap)); builder.append(",page_id => "); if (paramMap.containsKey("page_id")) { builder.append(paramMap.get("page_id")[0]); } log.info(builder.toString()); return; } if (request instanceof MultipartHttpServletRequest) { log.info(builder.toString()); return; } //由于request.getReader流只能操作一次,这里用过一次后,在Control中获取RequestBody中参数就获取不到,所以这里要先注释掉,后续解决 // BufferedReader reader = request.getReader(); // String body = this.read(reader); // if (StringUtil.isNotEmpty(body)) { // body = body.replace("\n", ""); // } // builder.append(body); log.info(builder.toString()); } private String read(Reader reader) throws IOException { StringWriter writer = new StringWriter(); try { this.write(reader, writer, 1024 * 8); return writer.getBuffer().toString(); } finally { writer.close(); } } private long write(Reader reader, Writer writer, int bufferSize) throws IOException { int read; long total = 0; char[] buf = new char[bufferSize]; while ((read = reader.read(buf)) != -1) { writer.write(buf, 0, read); total += read; } return total; } }
2. 注册拦截器配置 LoginConfiguration
LoginConfiguration.java是另一个核心类之一,它继承自WebMvcConfigurer类,负责注册并生效我们自己定义的拦截器配置;在这里要注意定义好拦截路径和排除拦截的路径;
package com.demo.common.configuration; import com.demo.common.interceptor.LoginInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.handler.MappedInterceptor; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @ProjectName: demo * @Package: com.demo.common.configuration * @ClassName: LoginConfiguration * @Description: 负责注册并生效自己定义的拦截器配置 * @Author: * @Date: * @Version: 1.0 */ @Configuration public class LoginConfiguration implements WebMvcConfigurer { @Bean public MappedInterceptor getMappedInterceptor() { //注册拦截器 LoginInterceptor loginInterceptor = new LoginInterceptor(); //拦截路径 ("/**")对所有请求都拦截 String[] includePatterns = new String[]{"/**"}; //排除拦截路径 String[] excludePatterns = new String[]{"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/api", "/api-docs", "/api-docs/**"}; //将数组转化为集合 List<String> listOldExclude = Arrays.asList(excludePatterns); //将自定义的排除拦截路径添加到集合中 List<String> listNewExclude = new ArrayList<>(); listNewExclude.add("/demoJson/getCityList.json"); listNewExclude.add("/demoJson/getStudentList.json"); //定义新集合 List<String> listExclude = new ArrayList<>(); listExclude.addAll(listOldExclude); listExclude.addAll(listNewExclude); //将新集合转化回新数组 String[] newExcludePatterns = listExclude.toArray(new String[listExclude.size()]); return new MappedInterceptor(includePatterns, newExcludePatterns, loginInterceptor); } }