Interceptor + CrossOrigin

Author Avatar
Akun 11月 07,2021

拥有拦截器时的跨域问题

Shit! How annoying trouble! Fk Interceptor and CrossOrigin.

最近期末了嘛,准备做一个前后端分离的项目交上去,事实上,一切顺利,除了最后的项目部署

项目环境

  • 上半年快速入手了Springboot,考虑到这是一个比较庞大的系统,因此快速开发Springboot的结果就是对原理的不甚理解,所以这个学期做了SpringMVC的项目
  • 本项目采取前后端分离的架构,前台部署在小伙伴的服务器,后台部署在我的服务器,因此,需要解决跨域问题
  • 由于做的是一个社区项目,BS架构嘛,B端肯定有注册登录等等一系列操作,常用的Session自然是用不了,所以改用JWT(Json Web Token)技术了,通过使用拦截器,对于所有接口进行拦截验证,并放行其中不需要权限的端口

经过

  • 昨天就做好跨域了,并上传到了服务器后,便开始看比赛
  • 就在昨晚EDG拿到冠军,重返LPL巅峰,准备迎接他最忠诚信徒时,也是正当我准备打开一把匹配,与EDG同享荣光之时,噩耗突然传来——小伙伴告诉我跨域有问题
  • 在上传图片等等一系列操作里,跨域请求报错
  • 跨域请求报错?那必定是我全局跨域没做好,我急忙补救一番,将XML配置换成注解配置,喊小伙伴再次测试,结果还是不行
  • 这时已经接近凌晨三点,熬不住后我觉得睡觉,让时间解决一切烦恼
  • 第二天测试后,发现有一部分接口是可以正常跨域的,这很不正常,因为我设置的是全局跨域
  • 再次一番测试后,发现可以正常跨域都是没有被我的JWT拦截器拦截的接口,问题已经浮出水面

原因

跨域

  • 本人对于前端了,对于发包了,并不太了解,自然,对于跨域也是不太了解的
  • 经过网上冲浪补充学习之后,才知道——
    • 由于同源策略,跨域请求中,会出现Options请求(CORS预检请求
    • 不会触发CORS预检请求的请求,被称为简单请求
    • 相反则被称为复杂请求

简单请求和复杂请求

  • 请求方式是HEADGETPOST,并且http头信息不超过Accept
    Accept-Language
    Content-Language
    Last-Event-IDContent-Type时,该请求为简单请求
  • 否则该请求为复杂请求

产生预检请求的原因

  • 我们的项目将Token存放在了请求头,并且所有的接口均为GET、POST请求,因此,被JWT拦截器拦截下的接口,均会因为跨域而发送CORS预检请求
  • 并且CORS预检请求会因为拦截器(interceptor)而导致无法到达具体接口,接口从而无法响应设置的CrossOrigin

措施

设置跨域(SpringMVC)

  • 全局跨域(在spring配置文件中)

    <mvc:cors>
        <mvc:mapping path="/**" />
    </mvc:cors>
    
  • 细粒度的跨域配置(配置某个类下的所有接口跨域)

    @Controller
    @CrossOrigin(maxAge = 3600)
    @RequestMapping("/master")
    public class MasterController{
    }
    
  • 更细粒度的跨域配置(配置某个接口的跨域)

    @ResponseBody
    @CrossOrigin(maxAge = 3600)
    @RequestMapping("addArticle")
    public JsonMessage addArticle(Article article){
    }
    

拦截器导致跨域问题

  • 在自定义拦截器的preHandle方法内加入以下代码,即可解决

    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Allow-Headers", "x-requested-width");
    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
    

跨域测试方法

  • 通过浏览器,随便打开一个网页,然后打开开发者选项,在其中的Console页面中,直接输入以下Js代码进行测试

    var token= ""
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://localhost:8080/master/login');
    xhr.setRequestHeader("authentication",token);
    xhr.send(null);
    xhr.onload = function(e) {
        var xhr = e.target;
        console.log(xhr.responseText);
    }
    /* 
     * 如果不需要token,直接删去第1行和第4行
     * 其中
     *   第3行别傻芙芙的照抄我的接口
     *   第4行记得k/v对照
    */