springboot(shiro)+vue(axios)跨域问题记录

mac2024-03-29  34

1. 通用设置

一旦前后端分离,就涉及到跨域问题(域名、端口、协议(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 })

2. 携带cookie的跨域

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}

3. shiro + vue

上述配置在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

最新回复(0)