关于springboot集成shiro后遇到的CORS跨域问题

20 篇文章 1 订阅
8 篇文章 0 订阅

废话不多说,先上解决办法,后边再说原理:

自定义MyFormAuthenticationFilter

public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
    /**
     * 在访问controller前判断是否登录,返回json,不进行重定向。
     * @param request
     * @param response
     * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if (isAjax(request)) {
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json");
            //解决一下跨域问题
            httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
            httpServletResponse.setHeader("Access-Control-Max-Age", "0");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpServletResponse.setHeader("XDomainRequestAllowed","1");
            httpServletResponse.getWriter().write(BaseRestfulResult.needLogin().toJsonString());
            httpServletResponse.getWriter().flush();
            httpServletResponse.getWriter().close();
        } else {
            //saveRequestAndRedirectToLogin(request, response);
            /**
             * @Mark 非ajax请求重定向为登录页面
             */
            httpServletResponse.sendRedirect("/login");
        }
        return false;
    }

	//解决OPTIONS请求跨域问题
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (request instanceof HttpServletRequest) {
            if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
                return true;
            }
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }


    private boolean isAjax(ServletRequest request){
        String header = ((HttpServletRequest) request).getHeader("X-Requested-With");
        if("XMLHttpRequest".equalsIgnoreCase(header)){
            return Boolean.TRUE;
       }
       return Boolean.FALSE;
        return true;
    }
}

注意上边的isAccessAllowed()方法和onAccessDenied()方法中的几行httpServletResponse.setHeader(“xx”, “xxx”);,这两个地方是关键。
最后别忘了在config类中注册一下
shiroFilterFactoryBean.getFilters().put(“authc”, new MyFormAuthenticationFilter());
大功告成~

以下是原理

最近在用springboot写前后端分离项目的时候, 用postman测试接口没问题,但丢给前端测试的时候返回了跨域问题:
在这里插入图片描述
这就很奇怪,所有的controller上我都加了@CrossOrigin注解,为什么还会有跨域问题呢?

这里有个需要注意的地方: xx has been blocked by CORS policy
这个CORS是个什么东西呢?简单说是一种资源共享机制。当浏览器发起ajax请求的时候,会先发起一个method为OPTIONS的请求, 这个请求我们可以简单理解为一个探路请求, 该请求不携带信息, 只是为了测试一下目标服务器是否支持跨域,如果支持跨域的话,再发出后续的请求。
在这里插入图片描述
通过chrome控制台可以看出, 当我们尝试调用listByAdmin接口的时候,发出了一个OPTIONS请求,该请求返回200后,浏览器又发起了一个一模一样的请求,后边这个请求才是真正的请求,当然这是正常的情况。
问题就出在这个OPTIONS请求上, 因为我的项目是完全前后端分离的,前端请求的时候会在http header上携带token,后端利用token鉴权。然而OPTIONS请求(‘OPTIONS请求’这个叫法可能不准确,各位理解我说的是啥就行)默认是不携带token信息的,后端没有收到token,导致鉴权失败。

但是鉴权失败为什么报鉴权问题,反而报了个跨域问题呢?
先看一下一开始我是怎么处理鉴权失败的

public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
   @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if (isAjax(request)) {
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json")//返回给前端鉴权失败的信息
            httpServletResponse.getWriter().write(BaseRestfulResult.needLogin().toJsonString());	
            httpServletResponse.getWriter().flush();
            httpServletResponse.getWriter().close();
        } else {
            //saveRequestAndRedirectToLogin(request, response);
            /**
             * @Mark 非ajax请求重定向为登录页面
             */
            httpServletResponse.sendRedirect("/login");
        }
        return false;
    }
    }

首先判断是否ajax请求,如果是的话,将鉴权失败的信息返回给前端。 这个地方没有设置允许跨域的header,所以前端就出现了跨域问题。

解决之后的源码:

public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if (isAjax(request)) {
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json");
            //解决一下跨域问题
            httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
            httpServletResponse.setHeader("Access-Control-Max-Age", "0");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpServletResponse.setHeader("XDomainRequestAllowed","1");
            httpServletResponse.getWriter().write(BaseRestfulResult.needLogin().toJsonString());
            httpServletResponse.getWriter().flush();
            httpServletResponse.getWriter().close();
        } else {
            //saveRequestAndRedirectToLogin(request, response);
            /**
             * @Mark 非ajax请求重定向为登录页面
             */
            httpServletResponse.sendRedirect("/login");
        }
        return false;
    }

当然,最佳的解决方案是针对OPTIONS请求单独处理以下

   @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (request instanceof HttpServletRequest) {
            if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
                return true;
            }
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值