springboot后端跨域addCorsMappings与拦截器冲突导致跨域失效
问题出现在一个简单的登录功能,本想随便应付一下课设,使用下传统的session保存用户信息即可,没想到遇到了这一个坑,甚是欢喜。首先介绍下大概背景,系统是前后端分离的项目,经典的Vue + SpringBoot组合。有过前后端分离开发经验的人都很清楚,需要解决跨域问题。我就直接在后端进行的跨域处理。直接添加下面的配置类:@Configurationpublic class MyWebConfig
问题出现在一个简单的登录功能,本想随便应付一下课设,使用下传统的session保存用户信息即可,没想到遇到了这一个坑,甚是欢喜。
首先介绍下大概背景,系统是前后端分离的项目,经典的Vue + SpringBoot
组合。有过前后端分离开发经验的人都很清楚,需要解决跨域问题。我就直接在后端进行的跨域处理。直接添加下面的配置类:
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
//配置解决跨域问题
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","POST","HEAD","PUT","DELETE","OPTIONS")
.allowCredentials(true)
.allowedHeaders("*")
.maxAge(3600);
}
}
ok,Vue通过axios可以成功获取到后端响应的json数据。然后我就以为解决了跨域问题了,直到系统完成的最后,最后想要再添加一个登录功能。原本对于前后端分离的项目来说,一般首选使用的是token(Shiro+JWT)作为登录的验证信息,不过由于是课设,我想简单偷懒一下,使用成session的方式,在后端服务器上保存用户信息,响应给前端JSESSIONID这一cookie,每次请求都携带上这个cookie。
我们都知道跨域问题之一就是cookie不能跨域,所以才需要进行跨域配置,上面的配置确实可以让cookie实现跨域,这没问题。问题就在于,我添加了一个拦截器,拦截系统主要信息的请求路径,判断用户是否登录了,如果没有则返回没有登录的信息,交给前端进行处理。
首先在上面MyWebConfig
配置类中添加下面配置:
//添加自定义的拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //设置拦截路径(所有)
.excludePathPatterns("/user/login") //排除被拦截的部分请求
.excludePathPatterns("/user/unauthenticated");
}
未登录的controller处理如下:
@GetMapping("/user/unauthenticated")
public ResultTemplate<User> unauthenticated(){
ResultTemplate<User> res = new ResultTemplate<>();
res.isSuccess(false)
.setCode(StatusCode.UNLOGIN.getCode())
.setMessage("未登录,请先进行登录");
return res;
}
自定义拦截器如下:
public class LoginInterceptor extends HandlerInterceptorAdapter {
//预处理拦截器,用于进入所有后台页面的拦截,判断是否登录
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//未登录(这里简化直接判断了,直接这样处理不太合理)
if(request.getSession().getAttribute("user") == null){
//重定向到未登录的响应信息
response.sendRedirect("/user/unauthenticated");
return false;
}
return true;
}
}
就是这一拦截器,把我获取系统其他资源的请求路径都给拦截了,调试时还说session获取不到,都是null。也就是说请求跨域失效了。
(其实这是两个问题:跨域问题是前端报的cros请求跨域失败的错误,而获取到session为null是因为前端没有带上JSESSIONID这一cookie值导致获取不到同一个session)。
调试和查找资料的过程不多说,最终才发现是一个很小的坑:
就是请求处理的顺序问题,请求打到后端时,是先被拦截器拦截处理的,也就是我们配置的addCrosMappings并没有起到作用,请求直接被拦截器拦截了,而由于此时请求还没有配置跨域信息,所以就出现了跨域问题
怎么解决?很简单,在这个登录拦截器之前再加一个拦截器,把请求拦截下来配置好跨域信息后放行即可,spring也为我们提供了这样一个拦截器(过滤器CorsFilter
)
@Configuration
public class MyCorsConfig {
private CorsConfiguration corsConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); //允许所有域名访问
corsConfiguration.addAllowedHeader("*"); //允许所有请求头
corsConfiguration.addAllowedMethod("*"); //允许所有的请求类型
corsConfiguration.setMaxAge(3600L);
corsConfiguration.setAllowCredentials(true); //允许请求携带验证信息(cookie)
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
//存储request与跨域配置信息的容器,基于url的映射
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig());
return new CorsFilter(source);
}
}
自此,后端成功解决跨域问题了。
至于前端,还有一个小问题,就是需要对每一个axios请求,都需要携带上JSESSIONID这个cookie,否则session还是为null。查了好多,都说在axios请求加上下面参数就可以解决了:
xhrFields: {
withCredentials: true
},
那我也不可能每一个方法都这么加吧,前端菜鸡不懂,最后才知道这种处理:在main.js中对axios进行全局配置
axios.defaults.withCredentials=true
更多推荐
所有评论(0)