【跨域】SpringBoot跨域,拦截器中,第一次获取的请求头为NULL,发送两次请求的处理方式

背景:

在做前后端分离时,牵扯到跨域,但是已经设置了跨域
前端设置了允许携带Cookie
axios.defaults.withCredentials = true;
后端也配置了跨域:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //项目中的所有接口都支持跨域
        registry.addMapping("/**")
                //所有地址都可以访问,也可以配置具体地址
                .allowedOrigins("*")
                //允许的请求方式
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                //是否支持跨域Cookie
                .allowCredentials(true)
                // 跨域允许时间
                .maxAge(3600);
    }
}
浏览器端查看发送的请求,请求头中包含Authorization

【跨域】SpringBoot跨域,拦截器中,第一次获取的请求头为NULL,发送两次请求的处理方式_第1张图片

但是在后端拦截器中获取的参数总是为null【跨域】SpringBoot跨域,拦截器中,第一次获取的请求头为NULL,发送两次请求的处理方式_第2张图片

【跨域】SpringBoot跨域,拦截器中,第一次获取的请求头为NULL,发送两次请求的处理方式_第3张图片

原因:

实际上发送了两次请求,第一次为OPTIONS请求,第二次才GET/POST...请求

  • OPTIONS请求中,不会携带请求头的参数,所以在拦截器上获取请求头为空,自定义的拦截器拦截成功
  • 第一次请求不能通过,就不能获取第二次的请求了GET/POST...
  • 第一次请求不带参数,第二次请求才带参数

解决:

在拦截器中,如果请求为OPTIONS请求,让 OPTIONS 请求返回一个200状态码,表示可以正常访问,然后就会收到真正的GET/POST请求

String method = request.getMethod();	//输出 OPTIONS/GET/POST。。。
//如果是 OPTIONS 请求,让 OPTIONS 请求返回一个200状态码
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
    response.setStatus(HttpServletResponse.SC_OK);
    return false;
}



拦截器完整示例

@Component
public class HttpRequestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //origin:指定可以访问本项目的IP
        String origin = request.getHeader("Origin");
        response.setContentType("application/json;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", origin);
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "1800");
        //  设置  受支持请求标头(自定义  可以访问的请求头  例如:Token)
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization,token,Origin,Content-Type,Accept");
        // 指示的请求的响应是否可以暴露于该页面。当true值返回时它可以被暴露
        response.setHeader("Access-Control-Allow-Credentials", "true");
        //如果是OPTIONS请求,让其响应一个 200状态码,说明可以正常访问
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return false;
        }
        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 {
    }
}

配置拦截器

自定义的拦截器必须要配置,才能使用

@Configuration
public class WebConfig implements WebMvcConfigurer {

	@Autowired
    private HttpRequestInterceptor httpRequestInterceptor;
    
    // 配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //设置跨域得拦截器 ,/** 表示拦截所有请求
        registry.addInterceptor(httpRequestInterceptor).addPathPatterns("/**");
    }
}

你可能感兴趣的:(SpringBoot,跨域)