问题出现在一个简单的登录功能,本想随便应付一下课设,使用下传统的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
Logo

前往低代码交流专区

更多推荐