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