跨域过滤器要在userFilter之前执行 ,不然会执行到!

复杂请求

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求。

HEAD

GET

POST

HTTP的头信息不超出以下几种字段:

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type: 只限于三个值 application/x-www-form-urlencoded multipart/form-data text/plain

凡是不同时满足上面两个条件,就属于非简单请求。

预检请求

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

过滤器

由于项目中的 shiro 使用了 UserFilter, 下面是其代码

public class UserFilter extends AccessControlFilter {

    public UserFilter() {

    }


    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

        if (this.isLoginRequest(request, response)) {

            return true;

        } else {

            Subject subject = this.getSubject(request, response);

            return subject.getPrincipal() != null;

        }

    }


    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        this.saveRequestAndRedirectToLogin(request, response);

        return false;

    }

}

可以看出过滤器在过滤时上面的判断是用来判断是否为登录请求的,否则就去寻找登录凭证。而在 OPTIONS 请求中,是没有携带上 token 信息的,下面是当时情况下请求的 header:

=== MimeHeaders ===

host = 192.168.7.139:4000

connection = keep-alive

accept = */*

access-control-request-method = POST

access-control-request-headers = content-type,x-admin-token

origin = http://192.168.7.117:8080

sec-fetch-mode = cors

referer = http://192.168.7.117:8080/

user-agent = Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36

accept-encoding = gzip, deflate

accept-language = zh-CN,zh;q=0.9


可以看到 token 是被带在了 access-control-request-headers 中,这样 shiro 是找不到登录凭证的,请求自然就被拒绝了

问题解决

解决办法就是重写 UserFilter(具体看项目用的是哪个过滤器) 的 isAccessAllowed 方法,代码如下:

public class StatelessAuthcFilter extends UserFilter {


    @Override

    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        HttpServletRequest httpRequest = WebUtils.toHttp(request);

        HttpServletResponse httpResponse = WebUtils.toHttp(response);

        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {

            httpResponse.setHeader("Access-control-Allow-Origin", httpRequest.getHeader("Origin"));

            httpResponse.setHeader("Access-Control-Allow-Methods", httpRequest.getMethod());

            httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));

            httpResponse.setStatus(HttpStatus.OK.value());

            return false;

        }

        return super.preHandle(request, response);

    }

}