shiro学习分享(三)——解决跨域问题时遇到的坑

跨域问题的解决

使用springboot整合了shiro框架,springboot解决跨域的方法也是网上的到处都是的配置CORS解决跨域问题。


出现的问题:

使用了shiro框架,开启了shiro的登陆验证过滤器时,即filterChainDefinitionMap.put("/**","user");,代表要登陆过才能进行访问,但是经过一番测试,发现当ajax请求为复杂请求时,cookie无法被携带传输到服务器的,导致一直无法访问。
在网上找了很久,大部分网友说复杂请求时若要带cookie则allowedOrigins不能配置为“*”,而要是访问的地址,虽然可以通过配置过滤器来实现,当是本地的html还是访问不了,一直报跨域错误,而布置在不同端口的html虽然能够访问,当是cookie依旧传不了。。。
最后发现之所以传不了cookie是因为shiro的权限控制,由于复杂请求要传两次,第一次验证请求(OPTIONS)是没有带cookie的所以验证不通过,导致接下来的真实请求无法继续,因为上面的请求被拒绝了啊。。。


解决思路:

1.可以自定义shiro的UserFilter来让OPTIONS请求无条件通过(shiro的自带的fliter都是可以通过继承来进行重写)
样例如下:

/**
 * 重写shiro的UserFilter,实现通过OPTIONS请求
 * @author MDY
 */
public class ShiroUserFilter extends UserFilter {

    /**
     * 在访问过来的时候检测是否为OPTIONS请求,如果是就直接返回true
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            setHeader(httpRequest,httpResponse);
            return true;
        }
        return super.preHandle(request,response);
    }

    /**
     * 该方法会在验证失败后调用,这里由于是前后端分离,后台不控制页面跳转
     * 因此重写改成传输JSON数据
     */
    @Override
    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        saveRequest(request);
        setHeader((HttpServletRequest) request,(HttpServletResponse) response);
        PrintWriter out = response.getWriter();
        out.println(JSONObject.toJSONString(ResultUtil.error(ExceptionEnum.IS_NOT_LOGIN)));
        out.flush();
        out.close();
    }

    /**
     * 为response设置header,实现跨域
     */
    private void setHeader(HttpServletRequest request,HttpServletResponse response){
        //跨域的header设置
        response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", request.getMethod());
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        //防止乱码,适用于传输JSON数据
        response.setHeader("Content-Type","application/json;charset=UTF-8");
        response.setStatus(HttpStatus.OK.value());
    }

}

2.由于博主的项目是前后端分离的,所以shiro关于url的权限控制可有可无,所以博主干脆把filterChainDefinitionMap.put("/**","user");去掉,这样子所有人都能访问,但是如果你使用了shiro的其它url权限控制,如身份控制之类的以及shiro的权限控制注解,,如果你没有登陆则会抛出UnauthenticatedException异常,这时可以使用全局异常控制捕获该异常,将出错信息传回前端,虽然这样子解决不是很优雅。。。(关于全局异常处理网上也有很多教程,也可以看这篇)


最后附上在网上找到的springboot解决跨域问题的代码

/**
 * 解决跨域问题springboot所需配置
 */
@Configuration
public class CORSConfiguration {
    @Bean
    public WebMvcConfigurer CORSConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins("*")
                        .allowedMethods("*")
                        .allowedHeaders("*")
                        //设置是否允许跨域传cookie
                        .allowCredentials(true)
                        //设置缓存时间,减少重复响应
                        .maxAge(3600);
            }
        };
    }
}

PS:博主发现sprinboot配置之后前端不用特殊的配置就能进行跨域,不知是不是浏览器的问题还是,但为了保险起见,ajax请求时还是加上crossDomain:true,xhrFields: { withCredentials: true },比较好。如果要带上cookie跨域,则必须加上上面两句。
请求样例:

$.ajax({  
    async:true,  
    type:"post",  
    url:"", 
    data:JSON.stringify(params),  
    contentType: "application/json; charset=utf-8",

    crossDomain:true,
    xhrFields: {  withCredentials: true  },

    dataType:"json",  
    success:function(data){  
    console.log(params);
           console.log(data);
    },  
    error:function(data){  
        console.log(data)  
    }  

})

猜你喜欢

转载自blog.csdn.net/madonghyu/article/details/80027387