由于项目需要springboot项目接入了shiro进行登录验证。做法是在在每一个接口的heards部分增加token,后台拿到token后进行验证。在postman测试没问题。但是,前端却一直报options500错误。
原来,是浏览器的同源策略引起的。
什么是同源策略?
源(origin)就是协议、域名和端口号。同源策略是浏览器的一种安全功能,不同源(协议或域名或端口不同)之间不能相互读写。
跨域(摘自http访问控制)
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。
在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求,以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method
首部字段告知服务器实际请求所使用的 HTTP 方法;Access-Control-Request-Headers
首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。
这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
shiro实现跨域
在shiro中要实现跨域,重写BasicHttpAuthenticationFilter的preHandle返回正确的请求
public class MyHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
... ...
/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin")); //标识允许哪个域到请求,直接修改成请求头的域
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");//标识允许的请求方法
// 响应首部 Access-Control-Allow-Headers 用于 preflight request (预检请求)中,列出了将会在正式请求的 Access-Control-Expose-Headers 字段中出现的首部信息。修改为请求首部
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
//给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}
使用MyHttpAuthenticationFilter
@Configuration
public class ShiroConfig {
//@Qualifier("securityManager") 解决 :The dependencies of some of the beans in the application context form a cycle:
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//排除swagger相关页
//...
//排除静态资源文件
//...
//自定义过滤器
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("jwt",new JwtFilter());
shiroFilterFactoryBean.setFilters(filterMap);
// 过滤链定义,从上向下顺序执行,一般将/**放在最为下边
filterChainDefinitionMap.put("/**", "jwt");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}