學習記錄370@ ssm中 加了攔截器,跨域請求配置就不起作用的解決辦法
阿新 • • 發佈:2020-12-31
注意ssm採用全註解的方式配置的
原攔截器和跨域配置
攔截器類
package com.qianfeng.controller.handler;
import com.qianfeng.common.BusinessException;
import com.qianfeng.common.Constant;
import com.qianfeng.common.ResultEnum;
import com.qianfeng.dao.UserDao;
import com.qianfeng.pojo.vo.UserVo;
import com.qianfeng.utils.CurrentUserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet. http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.List;
@Component//攔截器類,交給spring管理
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
UserDao userDao;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
//判斷之前登入存放的session是否存在
Object attribute = session.getAttribute(Constant.User.LOGIN_KEY);
if (attribute == null) {//未登入
throw new BusinessException(ResultEnum.NO_LOGIN);
}
UserVo userVo = (UserVo) attribute;
//將id設定進threadlocal,後面記錄日誌的AOP要用,也就是操作人
CurrentUserUtil.setId(userVo.getId());
HandlerMethod handler1 = (HandlerMethod) handler;
Method method = handler1.getMethod();
if (method.isAnnotationPresent(NeedPerm.class)) {
// 許可權判斷,沒有許可權就拋給前端一個特定的code,前端自己處理
List<String> permUrls = userDao.findPermsByUserId(userVo.getId());
if (!permUrls.contains(request.getRequestURI())) {
throw new BusinessException(ResultEnum.NO_PERMISION);
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
CurrentUserUtil.remove();
}
}
配置攔截器和跨域允許
package com.qianfeng.config;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.qianfeng.controller.handler.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.*;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
//@Configuration//也可以交予spring管理
//核心@EnableWebMvc,implements WebMvcConfigurer
@EnableWebMvc
@ComponentScan({"com.qianfeng.controller"})
public class SpringmvcConfig implements WebMvcConfigurer {
// 新增攔截器
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 註冊攔截器,新增攔截的路徑,新增放行的路徑
registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
.excludePathPatterns("/login", "/swagger-ui.html", "/webjars/**", "/swagger/**", "/swagger-resources/**");
}
@Override//解決跨域 ip 請求方式
public void addCorsMappings(CorsRegistry registry) {
// 設定允許跨域的路徑
registry.addMapping("/**")
// 設定允許跨域請求的域名
.allowedOrigins("*")
// 是否允許證書 cookie
.allowCredentials(true)
// 設定允許的方法
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 設定允許的header屬性
.allowedHeaders("*")
// 跨域允許時間
.maxAge(3600);
}
}
以上方式有了攔截器後,跨域請求配置就失效了,需要從新進行跨域的配置,可以採用在過濾器中配置跨域,這個在啟動類中配置
package com.qianfeng.init;
import com.qianfeng.config.MainConfig;
import com.qianfeng.config.SpringmvcConfig;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
//啟動伺服器就載入這個類,核心是servletContext,applicationContext
//ServletContext是最大的域物件,隨著伺服器的開啟而開啟,銷燬而銷燬
//用來載入配置檔案,初始化,然後都在spring的管理之下了
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//幫載入父子容器
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
//載入sm配置檔案
applicationContext.register(MainConfig.class);
//載入springmvc配置檔案
// 這裡也可以交給spring管理,但是最好讓spring管理service和mybatis即可,springmvc管理controller層
applicationContext.register(SpringmvcConfig.class);
//加一個編碼過濾 中文亂碼,之前的過濾器是在web.xml中
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
//放入tomcat容器
FilterRegistration.Dynamic characterEncodingFilter1 = servletContext.addFilter("characterEncodingFilter", characterEncodingFilter);
//filter攔截的路徑是/*
characterEncodingFilter1.addMappingForUrlPatterns(null, true, "/*");
// 以下五行程式碼配置跨域過濾器,
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig());
CorsFilter corsFilter = new CorsFilter(source);
FilterRegistration.Dynamic corsFilter1 = servletContext.addFilter("corsFilter", corsFilter);
corsFilter1.addMappingForUrlPatterns(null, true, "/*");
//springmvc能啟動 中央控制 dispatchServlet
ServletRegistration.Dynamic dispatchServlet = servletContext.addServlet("dispatchServlet", new DispatcherServlet(applicationContext));
dispatchServlet.addMapping("/");
dispatchServlet.setLoadOnStartup(1);
}
// 跨域過濾器方法
private CorsConfiguration corsConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
/* * 請求常用的三種配置,*代表允許所有,當時你也可以自定義屬性(比如header只能帶什麼,只能是post方式等等)
*/
corsConfiguration.addAllowedOrigin("*");//域名
corsConfiguration.addAllowedHeader("*");//所有的請求頭
corsConfiguration.addAllowedMethod("*");//post get put delete patch
corsConfiguration.setAllowCredentials(true);//允許cookie放過
corsConfiguration.setMaxAge(3600L);
return corsConfiguration;
}
}
原因
參考:
DispatchServlet.doDispatch()方法是SpringMVC的核心入口方法,經過分析發現所有的攔截器的preHandle()方法的執行都在實際handler的方法(比如某個API對應的業務方法)之前,其中任意攔截器返回false都會跳過後續所有處理過程。而SpringMVC對預檢請求的處理則在PreFlightHandler.handleRequest()中處理,在整個處理鏈條中出於後置位。由於預檢請求中不帶資料,因此先被許可權攔截器攔截。
原來CROS複雜請求時會首先發送一個OPTIONS請求做嗅探,來測試伺服器是否支援本次請求,請求成功後才會傳送真實的請求;而OPTIONS請求不會攜帶資料,導致這個請求被攔截了,直接返回了狀態碼,響應頭中沒攜帶解決跨域問題的頭部資訊,出現了跨域問題。