Vue + Axios 需要Api能够跨域调用,网上也是很多跨域的解决方法,比如重写 WebMvcConfigurationSupport 的addCrosMappings 方法,如:
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
.allowedHeaders("X-Access-Token");
log.debug("允许跨域配置成功!");
}
这种方法正常能够跨域,但是遇到进行 Token 认证被拦截器或者过滤器处理成401并返回后,跨域也就失败,axios 会读不到error.response,而无法进行401处理。@CrossOrigin更是不能解决,况且每个 Controller加也不方便。
我的解决方法简单介绍如下:
1、定义拦截器CrossInterceptor,并且能够通过配置修改允许跨域域名
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
@Log4j2
@Configuration
@ConfigurationProperties(prefix = "spring.cross")
public class CrossInterceptor implements HandlerInterceptor {
private static List allowHosts;
//配置允许跨域的域名
public void setAllow(String allow){
if(allow != null){
allowHosts = Arrays.asList(allow.split(","));
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String origin = request.getHeader(HttpHeaders.ORIGIN);
if (origin != null) {
if(allowHosts.contains(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Access-Token");
response.setHeader("Access-Control-Max-Age", "3600");
} else {
log.warn("跨域失败:" + origin);
}
}
return true;
}
}
2、WebMvcConfigurationSupport中注册拦截器,注意跨域拦截器必须要在Token认证、权限认证等拦截器之前注册
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Log4j2
@Configuration
class WebMvcConfiguration extends WebMvcConfigurationSupport {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CrossInterceptor()).addPathPatterns("/**");
log.debug("跨域拦截器注册成功!");
registry.addInterceptor(new OptionsInterceptor()).addPathPatterns("/**");
log.debug("Options请求拦截器注册成功!");
}
// 不在这里实现跨域,这里实现跨域会导致拦截器return false的情况下跨域失败,改为拦截器实现
/*@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
.allowedHeaders("X-Access-Token");
log.debug("允许跨域配置成功!");
}
*/
}
3、配置yml
spring:
cross:
allow: http://localhost:8080,http://localhost:8081
4、Axios 处理401等返回结果
function beforThen (instance) {
instance.interceptors.response.use(function (response) {
let { data } = response;
return data;
}, function (error) {
if (error.response && error.response.status === 401) {
window.location.href = '/#/login';
} else {
Message.error(error.message);
return Promise.reject(error.message);
}
});
}
5、Axios请求时,偶尔会遇到一个 api请求两次的情况,其实第一次是 Options 请求,我们也可以用拦截器同意处理 Options请求
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class OptionsInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(request.getMethod().equals("OPTIONS")){
response.setStatus(HttpServletResponse.SC_OK);
return false;
}
return true;
}
}
该拦截器也是要注册在跨域之后,保证 Options请求能够跨域成功