SpringBoot 2.0.1 Vue Axios 跨域问题及401等异常处理

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<String> 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请求能够跨域成功


猜你喜欢

转载自blog.csdn.net/maoxinrong/article/details/80777829