Shiro 在跨域请求中会遇到的 302 重定向问题

CORS 真的太严格了,半清不清的跨域、预检请求、拦截器搞得前后端都身心俱疲。

背景

这边要开发一套带有权限认证的平台,懒得自己写拦截器,于是还是打算利用 Shiro 安全框架,由于采用的还是 Cookie-Session 那老一套,并没有封装成 token 暂时也不用考虑集群多实例共享 session 的问题,所以其实前端的每次请求报文都是需要携带 cookie 的,cookie 里面的 jsessionid 就是验证对应服务端中的 session-data 能否匹配。但是这次联调处理并不顺利,还是在开发阶段就问题频出。

在 CORS 协议中,前端如果需要每次携带 cookie,就得把 withCredentials 设置成 true。所以在开发阶段我将配置类的 Access-Control-Allow-Origin 设置成 *Access-Control-Allow-Credentials 设置成 true

但是在遇到 PUT 方法的接口(非预检请求)时,由于 options 类型的预检请求不带 cookie 所以被拦截在外。

非简单请求

请求分为非简单请求和简单请求,其中预检请求就是导致我们联调发生的问题的原因。

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是 PUTDELETE,或者 Content-Type 字段的类型是 application/json

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

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

这里引用阮一峰的网络日志的一篇名为《跨域资源共享 CORS 详解》解释。当请求方法是 PUTDELETE,或者 Content-Type 字段的类型是 application/json 就会触发询问服务器的回应,要求服务器确认可以这样请求。

之前的配置类由于服务端未放行,导致触发错误。

解决方法

我们将需要预检请求的方法 "OPTIONS".equals(request.getMethod()) 放行,同时把 Access-Control-Allow-HeadersAccess-Control-Allow-Origin 设置成请求的 HTTP 头信息,这样可以保证都放行。实际的配置就是如下:

/**
 * @ClassName CorsFilterConfiguration
 * @Description 跨域资源共享
 * @Author MatthewHan
 * @Date 2019/10/15 10:48
 * @Version 1.0
 **/
@WebFilter(filterName = "CorsFilterConfig ")
@Order(-100)
@Component
@ServletComponentScan
public class CorsFilterConfig implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(CorsFilterConfig.class);

    private static String[] allowDomains = {"http://localhost:8080", "http://localhost:80", "http://ip:8080", "http://ip:80",
            "http://ip:8080", "http://ip:80"};


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST,GET,PATCH,DELETE,PUT,OPTIONS");
        /*
         * 该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是1小时(3600秒),
         * 即允许缓存该条回应3600秒(即1小时),在此期间,不用发出另一条预检请求。
         */
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        response.setHeader("Content-Type","application/json;charset=UTF-8");
        // prefight请求
        if ("OPTIONS".equals(request.getMethod())) {
            response.setStatus( 200 );
            return;
        }
        chain.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }
}
  • Shiro
    18 引用 • 29 回帖
  • CORS
    9 引用 • 53 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    762 引用 • 1360 回帖 • 697 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    2667 引用 • 7964 回帖 • 779 关注
回帖
请输入回帖内容...