跨域问题的解决
使用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)
}
})