Shiro Learning Sharing (3) - Pit encountered in solving cross-domain problems

Solving cross-domain problems

Using springboot to integrate the shiro framework, the method of springboot to solve cross-domain is also to configure CORS everywhere on the Internet to solve cross-domain problems.


Problems that arise:

When the shiro framework is used and the shiro login verification filter is turned on, that is filterChainDefinitionMap.put("/**","user");, the representative has to log in to access it, but after some tests, it is found that when the ajax request is a complex request , the cookie cannot be carried and transmitted to the server, resulting in been inaccessible.
After searching on the Internet for a long time, most netizens said that if you want to bring a cookie in a complex request, you allowedOriginscannot configure it as "*", but if you want to access the address, although it can be achieved by configuring the filter, even if it is local html, it still cannot be accessed. A cross-domain error is reported, and although html placed on different ports can be accessed, it still cannot be passed as a cookie. . .
In the end, it was found that the reason why the cookie could not be passed was because of Shiro's permission control. Since the complex request had to be passed twice, the first verification request ( OPTIONS ) did not have a cookie, so the verification failed, and the next real request could not continue, because The above request was rejected. . .


Solutions:

1. You can customize shiro's UserFilter to allow OPTIONS requests to pass unconditionally (shiro's own filter can be rewritten through inheritance).
Examples are as follows:

/**
 * 重写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. Since the blogger's project is separated from the front and back ends, Shiro's permission control on url is optional, so the blogger simply filterChainDefinitionMap.put("/**","user");removes it so that everyone can access it, but if you use shiro's other url permissions Control, such as identity control and shiro's permission control annotation, if you are not logged in, an exception will be thrown UnauthenticatedException. At this time, you can use the global exception control to catch the exception and send the error information back to the front end, although this is not a very solution. Grace. . . (There are also many tutorials on the Internet about global exception handling, you can also read this )


Finally, attach the code that springboot found on the Internet to solve cross-domain problems

/**
 * 解决跨域问题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: The blogger found that after the sprinboot configuration, the front end can perform cross-domain without special configuration. I don't know if it is a browser problem or not, but for the sake of insurance, it crossDomain:true,xhrFields: { withCredentials: true },is better to add it to the ajax request. If you want to bring cookies across domains, you must add the above two sentences.
Sample request:

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

})

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325356616&siteId=291194637