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); } }
