1. 问题描述

生产和测试环境使用nginx做了反向代理,所以不存在跨域的问题,但是在本地研发环境,由于前后端分离,前端和后端在不同的电脑上开发,存在跨域问题。
前端使用的是vue,后端使用的是springboot。在前后端都做了跨域的设置,前端的设置为:

//前端在vue的main文件全局添加一下代码:
import axios from 'axios';
axios.defaults.withCredentials=true;

后端的设置为:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfigurer {

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        // 允许cookies跨域
        config.setAllowCredentials(true);
        // #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");
        // #允许访问的头信息,*表示全部
        config.addAllowedHeader("*");
        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.setMaxAge(18000L);
        // 允许提交请求的方法,*表示全部允许
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

出现的问题是:登录的时候有一个图片验证码,前端请求验证码图片之后后端会将验证码存在session中,但是在本地开发的时候无论怎样验证码都是错误的,通过打断点发现session中没有拿到验证码,一直是null

//将验证码放入session    javax.servlet.http.HttpSession
httpSession.setAttribute("image-code-" + type, capText);

//从session中取出验证码
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;

@Configuration
public class ImageCodeFilter extends ZuulFilter {
    @Value("xxx")
    private String loginUrl;

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 101;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        String requestUrl = ctx.getRequest().getRequestURI();
        return requestUrl.matches(loginUrl);
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpSession httpSession = ctx.getRequest().getSession();
        for (int i = 0; i < 2; i++) {
            String code = (String) httpSession.getAttribute("image-code-" + i);
            if (code != null) {
                ctx.addZuulRequestHeader("image-code-" + i, code);
            }
        }
        return null;
    }

2.问题排查

刚开始我以为是跨域设置没有生效,但是一想接口可以正常访问,说明跨域的设置是生效的了,session中拿不到存储的验证码,那么就可以定位问题在请求携带的cookie上出了问题。通过浏览器的开发者模式发现请求没有携带cookie,这就很让人疑惑了。
在这里插入图片描述
查看返回头
在这里插入图片描述
每次返回头里都有这个Set-Cookie,这意味着吗,每次都是一个新的cookie,那么问题就很明显了,由于每次请求没有cookie,后端每次都返回一个新的cookie,这个cookie中记录的就是JSESSIONID,对应服务端的session
session拿不到验证码的问题可以简单描述为:由于请求头中没有cookie,所以服务端每次接受前端的请求都会生成一个新的session,自然没有办法从上一个session中拿到验证码,这个可以通过打印两个地方的sessionId来验证。

httpSession.getId()

然后我就搜索了许多解决方案,都不行,一般的解决点都围绕在允许cookie跨域这上面,但是回顾我们的前后端跨域设置,我们都设置了允许cookie跨域,所以问题不在这里。

3. 我的问题解决方案

在再次排查请求信息的时候,我发现了一个浏览器的告警:
在这里插入图片描述
这里提到了一个叫samesite的东西,这个简而言之就是:Chrome 51 开始,浏览器的 Cookie新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。
感兴趣的可以看下这篇博文Cookie 的 SameSite 属性
我们需要设置这个cookie的这个属性来允许浏览器跨域访问可以携带请求头,但是spring对这个的支持是在spring-session 2.x中,而我们的这个项目比较老,版本是springboot 1.x,我尝试引入依赖并进行设置,但是并不起作用。
参考的文章spring设置
现在,问题似乎又到了死胡同,我们再梳理一下,我们目前存在的问题是本地开发前后端联调无法携带cookie跨域,导致联调无法进行,在测试和生产环境由于nginx做了反向代理,解决了跨域的问题,自然也不存在这个问题,所以,我们只要在开发环境解决这个跨域请求携带cookie的问题即可,无需关心测试和生产环境。既然这个samesite是浏览器新版本新增的属性,那么我们关掉它不就可以了?试过之后,可行,成功获取session中的验证码。浏览器设置如下:
在chrome中打开链接: chrome://flags/#site-isolation-trial-opt-out,搜索samesite,禁用前三个选项,然后重启浏览器
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐