一旦前后端分离,就涉及到跨域问题(域名、端口、协议(http或https等)其一不一样就是跨域)。
在涉及到跨域问题时,浏览器发送请求前,会优先发送一个OPTION请求。
所以,对于springboot项目,只需要自定义一下web过滤器即可。
/**
* 解决跨域问题
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://xx.xx.xx.xx:port")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("Accept", "Content-Type", "Origin", "Authorization", "X-Auth-Token")
.exposedHeaders("X-Auth-Token", "Authorization")
.allowedMethods("POST", "GET", "DELETE", "PUT", "OPTIONS");
}
}
说明:
allowedOrigins
:一般情况下配置"*"
和配合"ip:port"
都行,但是在某些情况下,出现后者可以,前者不行的问题。故先建议配成具体的ip(*后续再试即可)。allowCredentials
:普通跨域,这个值无所谓,true或false都可以。(但是有shiro认证,必须为true,下文再说)allowedHeaders、allowedMethods
:允许OPTION
即可。let instance = axios.create({
baseURL: host,
timeout: 120000
})
shiro
用户认证在浏览器cookie
中存在JSESSIONID
信息,发送请求时,会携带信息。因此,需要前端和后端,都需要设置允许Credentials。
加一条设置,如:
let instance = axios.create({
baseURL: host,
timeout: 120000
})
instance.defaults.withCredentials = true
也只需要在前述的WebConfig类中修改一条配置:
WebConfig
...
.allowCredentials(true)
...
至此,shiro + axios跨域初步解决。
初步解决的问题有哪些呢?shiro认证拦截已经生效,未登录情况下,访问被拦截。登录之后,不会被拦截。如:
/login
接口:
@GetMapping("/login")
public RootRespBody<Boolean> login() {
Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()){
return RootRespBody.failure(RootRespBody.Status.ACCESS_DENIED, "please login", false);
}
return RootRespBody.success(true);
}
登录后访问直接成功:
{"status":200,"message":"SUCCESS","data":true}
上述配置在shiro + vue
的项目中并不能正常使用,原因是跨域时,请求会发送两个,一个OPTION, 一个正常的请求。
通常是OPTIONS Reqeust正常返回status 200,然后才发起正式的GET/POST等访问,但因为Shiro配置了URL过滤, 对于OPTIONS Request也进行了拦截,OPTION是没有携带cookie信息的,所以无法继续访问,后续实际的请求也就不会发送。
上述截图只发送了OPTION请求,由于该请求没通过,没有发送真正的需要的请求。
解决方法:shiro配置,允许OPTION
不经过过滤器。
public class MyAuthenticationFilter extends FormAuthenticationFilter {
/**
* @desc 跨域时,先发送option请求,option是不携带cookie信息的,但是也被shiro拦截了导致不通过,无法发送正常请求
* 因此需要将OPTION放行。
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (request instanceof HttpServletRequest) {
if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
return true;
}
}
return super.isAccessAllowed(request, response, mappedValue);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws Exception {
WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
在shiro 配置中手动加上:
//以下两行配合MyAuthenticationFilter过滤器使用
Map<String, Filter> filterMap = shiroFilterFactoryBean.getFilters();
filterMap.put("authc", new MyAuthenticationFilter());
至此,完成!
其他:后续可以考虑重定向的优化。
参考:
https://www.jianshu.com/p/e56362315581