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请求
 * UserFilter会过滤掉所有没有登录cookie的请求
 * PathMatchingFilter则不会
 * @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());
    }

}

  1. 接下来记得将写好的filter配置到shiro的filter链里面,下面为实现的简单代码
@Configuration
public class ShiroConfiguration {
    /**
     * 配置过滤器
     *
     * @param manager 装载shiro的安全管理器
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager manager) {
        // shiro的过滤器
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 自定义拦截器的配置
        Map<String, Filter> filter = new HashMap<>();

        filter.put("custom", new ShiroUserFilter());

        bean.setFilters(filter);
        // 安全管理器
        bean.setSecurityManager(manager);
        // 过滤器链配置
        // 配置访问权限
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 使用该过滤器过滤所有的链接
        filterChainDefinitionMap.put("/**","custom");
        // 登出
        filterChainDefinitionMap.put("/logout", "logout");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }
}

  1. 最后加上在网上找到的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跨域,则必须加上上面两句。
js请求样例:

$.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)  
    }  
      
})

你可能感兴趣的:(shiro,跨域,spring-boot)