背景
最近公司开始推行前后端分离的架构,于是不可避免的引入了跨域的问题,跨域的概念可以参考大佬的博客,这里就不再赘述了。
作为Java最流行框架之一的Spring其实已经帮我们写好了很多代码,我们只需要简单配置一下即可,当然下面会提到还是有一些不如人意的地方。PS:本文没有使用SpringBoot
SpringMVC跨域(cors)配置
全局配置
在SpringMVC的配置文件中添加如下配置即可
<mvc:cors>
<mvc:mapping path="/**" allow-credentials="true" allowed-methods="*" allowed-headers="*" allowed-origins="*"/>
</mvc:cors>
单个controller配置
注解可以写在类上也可以写在方法上
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://domain2.com")
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
加上权限控制后踩坑
原因
一般我们使用的都是全局的配置,而SpringMVC把跨域处理的拦截器放到了最后一个,那么我们权限过滤的拦截器就是排在了跨域处理之前,如果一个请求是无权限的,那么被拦截返回之后由于没有跨域处理,在前端展示的就是跨域失败的提示。同样的,你所有的拦截器的拦截返回都是跨域失败的提示,这显然是不合理的。
解决
方法一
SpringMVC还支持基于filter的跨域处理,由于filter的位置是在interceptor之前的,所以可以完美解决上述问题。官方给出的一个例子如下:
@Configuration
public class MyConfiguration {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
方法二
方法一有个比较坑的地方就是FilterRegistrationBean这个类只有SpringBoot中才有,那我们项目没有用SpringBoot咋办,下边是常规配置的方法:
<!-- 这个配置需要放到Spring的配置文件中,不能放到SpringMVC的配置文件,因为SpringMVC的加载是基于Servlet,它是晚于Filter的 -->
<bean id="corsFilter" class="org.springframework.web.filter.CorsFilter">
<constructor-arg name="configSource">
<bean class="org.springframework.web.cors.UrlBasedCorsConfigurationSource">
<property name="corsConfigurations">
<map>
<entry key="/**">
<bean class="org.springframework.web.cors.CorsConfiguration">
<property name="allowCredentials" value="true"/>
<property name="allowedMethods">
<list>
<value>GET</value>
<value>POST</value>
<value>HEAD</value>
</list>
</property>
<property name="allowedHeaders" value="*"/>
<property name="allowedOrigins" value="*"/>
</bean>
</entry>
</map>
</property>
</bean>
</constructor-arg>
</bean>
由于CorsFilter跟通常的Filter不一样,Spring对其做了很多改造,所以加载的方式要使用DelegatingFilterProxy,通过Spring的方式把它放到容器中
<!-- web.xml -->
<filter>
<filter-name>myCorsFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>corsFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myCorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
方法三
除了使用Spring提供的方法,你也可以自己编写跨域的处理,因为跨域的处理其实就是在响应头里加一些东西
public class CorsInterceptor extends HandlerInterceptorAdapter {
//@Setter是lombok的注解,等价于setter方法,下同
@Setter
private String allowCredentials;
@Setter
private String allowedMethods;
@Setter
private String allowedHeaders;
@Setter
private String allowedOrigins;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("Adding Access Control Response Headers");
if (CorsUtils.isCorsRequest(request)) {
ServletServerHttpRequest wrapRequest = new ServletServerHttpRequest(request);
if(CorsConfiguration.ALL.equals(allowedOrigins)) {
response.setHeader("Access-Control-Allow-Origin", wrapRequest.getHeaders().getOrigin());
} else {
response.setHeader("Access-Control-Allow-Origin", allowedOrigins);
}
response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
response.setHeader("Access-Control-Allow-Methods", allowedMethods);
response.setHeader("Access-Control-Allow-Headers", allowedHeaders);
}
return true;
}
}
SpringMVC配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/*.do"/>
<bean class="com.fingard.gardpay.websys.web.interceptor.CorsInterceptor">
<property name="allowCredentials" value="true"/>
<property name="allowedHeaders" value="*"/>
<property name="allowedMethods" value="GET,POST,HEAD,OPTIONS"/>
<property name="allowedOrigins" value="*"/>
</bean>
</mvc:interceptor>
</mvc:interceptors>