Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 501|回复: 0

springboot+vue2.x 解决session跨域失效问题

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-29 16:52:35 | 显示全部楼层 |阅读模式

    服务端SpringBoot2.x   :localhost:8082

    前端Vue2.x                 :localhost:81

    前后端的端口号不同,为跨域,导致前端访问后端时,每次访问都新生产一个sessionID。解决如下:

     

    后端:

    1.添加过滤器:

    package com.nsoft.gkzp.syscore.config.filter;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Configuration //内置tomcat运行不加它没问题,但后来改为用外置tomcat时,启动后过滤器会失效,后来查明原因需要加上@Configuration才行 @WebFilter(urlPatterns
    = "/*", filterName = "corsFilter") public class CorsFilter implements Filter { final private static Logger logger = LogManager.getLogger(CorsFilter.class); @Override public void destroy() { } /** * 此过滤器只是处理跨域问题 * @param servletRequest * @param servletResponse * @param chain * @throws ServletException * @throws IOException */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String origin = request.getHeader("Origin"); if(origin == null) { origin = request.getHeader("Referer"); } response.setHeader("Access-Control-Allow-Origin", origin);// 允许指定域访问跨域资源(这里不能写*,*代表接受所有域名访问,如写*则下面一行代码无效。谨记) response.setHeader("Access-Control-Allow-Credentials", "true");//true代表允许客户端携带cookie(此时origin值不能为“*”,只能为指定单一域名) response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"); /// 允许浏览器在预检请求成功之后发送的实际请求方法名 response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin, X-Requested-With, Content-Type, Accept,Access-Token");// 允许浏览器发送的请求消息头 //response.setHeader("Access-Control-Max-Age", "86400"); // 浏览器缓存预检请求结果时间,单位:秒 chain.doFilter(request,response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } }

    2. springboot2.配置过滤器时,启动类必须加上@ServletComponentScan才会加载过滤器

    package com.nsoft.gkzp;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.server.ConfigurableWebServerFactory;
    import org.springframework.boot.web.server.ErrorPage;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    /**
     * springboot入口
     * MapperScan("com.nsoft.gkzp.**.dao")为扫描mapper, 所以dao下面的类就不需要添加@mapper注解了
     * ServletComponentScan  添加了过滤器,故这里要添加@ServletComponentScan注解,spring才会扫描到过滤器(eg:com.nsoft.gkzp.syscore.config.filter.CorsFilter)
     */
    @SpringBootApplication
    @ServletComponentScan
    @MapperScan("com.nsoft.gkzp.**.dao")
    public class GzyGkzpApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(GzyGkzpApplication.class, args);
        }
    
    
        /**
         * 在springboot整合vue前端时,vue使用url跳转时报404错误,此处代码解决此问题
         * 参照https://blog.csdn.net/Mr_EvanChen/article/details/83625082
         */
        @Bean
        public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
            return factory -> {
                ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
                factory.addErrorPages(error404Page);
            };
        }
    
    }

    3. spring-session 2.x 中 Cookie里面了SameSite ,他默认值是 Lax 

    SameSite Cookie 是用来防止CSRF攻击,它有两个值:Strict、Lax
    SameSite = Strict:意为严格模式,表明这个cookie在任何情况下都不可能作为第三方cookie;
    SameSite = Lax  :意为宽松模式,在get请求是可以作为第三方cookie,但是不能携带cookie进行跨域post访问(这就很蛋疼了,我们那个校验接口就是POST请求)

    package com.nsoft.gkzp.syscore.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.session.web.http.CookieSerializer;
    import org.springframework.session.web.http.DefaultCookieSerializer;
    
    
    /**
    * https://www.cnblogs.com/hujinshui/p/11025848.html
    * spring-session 2.x 中 Cookie里面引入了SameSite他默认值是 Lax,
    * SameSite Cookie 是用来防止CSRF攻击,它有两个值:Strict、Lax
    * SameSite = Strict:意为严格模式,表明这个cookie在任何情况下都不可能作为第三方cookie;
    * SameSite = Lax:意为宽松模式,在get请求是可以作为第三方cookie,但是不能携带cookie进行跨域post访问
    * 总结:前端请求到后台,每次session都不一样,每次都是新的会话,导致获取不到用户信息
    */
    @Configuration public class SpringSessionConfig {
    public SpringSessionConfig() { }
    @Bean
    public CookieSerializer httpSessionIdResolver() {
      DefaultCookieSerializer cookieSerializer
    = new DefaultCookieSerializer(); // 取消仅限同一站点设置
      cookieSerializer.setSameSite(null); return cookieSerializer;
      }
    }

     

    前端:

    1.在 main.js (前端用axios)

    import axios from 'axios';
    axios.defaults.withCredentials=true;//让ajax携带cookie

     

    用了1天半时间,改了很多次依然不行,后来发现是前端用了 proxy 代理,它本身也是已经处理了跨域问题,网上找的时候发现有的文章也用到这个了。但我这里就是不行。

    我原来的代码:

    1)写的注册页面:

     

     2)全局配置如下:

    main.js

    // xenv 标记当前环境 true:开发环境   false:生产环境
    const xenv = true;
    // 注册全局变量
    Vue.prototype.$global = {
      //contentPath 标记根路径,主要用于axios请求后端数据的url
       contentPath: xenv ? '/api/' : router.options.base  
    };

    (xenv设为true;所以 根路径contentPath的值必为‘/api/’   ,而‘/api/’ 在vue.config.js里配置为代理,如下。)

    vue.config.js

      devServer: {
        open: true,
        host: '0.0.0.0',
        port: 80,
        https: false,
        hotOnly: false,
        before: app => {
        },
        proxy: {
          // 配置跨域
          '/api': {
            target: 'http://127.0.0.1:8082/',
            ws: true,
            changOrigin: true,
            pathRewrite: {
              '^/api': '/'
            }
          }
        }
      },

     

    2.不使用proxy代理,把根目录写死为'http://127.0.0.1:8082/',就成功了,修改如下:

    main.js:

    // xenv 标记当前环境 true:开发环境   false:生产环境
    const xenv = true;
    // 注册全局变量
    Vue.prototype.$global = {
      // contentPath 标记根路径,主要用于axios请求后端数据的url
      // contentPath: xenv ? '/api/' : router.options.base
      contentPath:  'http://127.0.0.1:8082/'
    };

     

    4. 跨域白名单

    (为了安全起见,可在服务端设置可跨域访问的白名单地址)

    1. 自定义了一个配置文件 D:\workspace-gzy-gkzp\src\main\resources\resources\config.properties (这里对于localhost,127.0.0.1两个ip,不要放到正式环境。否则如对方用本地环境,去访问正式的后台,会被允许跨域访问,不安全)

    #允许CORS的IP(即可跨域访问白名单,添加多个用英文逗号隔开coreFile.java)((端口号固定为application.properties配置的server.port))
    system.accessControlAllowOrigin =192.168.1.61,zshj.com.cn
    #测试环境加上localhost,127.0.0.1   system.accessControlAllowOrigin =localhost,127.0.0.1,,zshj.com.cn

    2.读取配置文件类 D:\workspace-gzy-gkzp\src\main\java\com\nsoft\gkzp\syscore\config\MyDefinedUtil.java

    package com.nsoft.gkzp.syscore.config;
    import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; /** * 自定义配置类 获取config.properties相关参数 *( 其他类获取值,请用注解@Autowired 方式 ,否则获取不到值) * @author zdyang * @date 2019.08.30 */ @Configuration //标识这个是一个配置类 @PropertySource(value = "classpath:resources/config.properties") public class MyDefinedUtil { @Value("${system.encoding:UTF-8}")  //冒号后的值为没有配置文件时,制动装载的默认值 //下面的属性不能为static类型,否则获取不到值 public String SYSTEM_ENCODING;  //#System Encoding  //文件管理 @Value("${system.file.folder.img}") public String SYSTEM_FILE_FOLDER_IMG;  //允许跨域白名单 @Value("${system.accessControlAllowOrigin}") public String SYSTEM_ACCESSCONTROLALLOWORIGIN; }

    3.跨域配置类:D:\workspace-gzy-gkzp\src\main\java\com\nsoft\gkzp\syscore\config\filter\CorsFilter.java

    package com.nsoft.gkzp.syscore.config.filter;
    
    import com.nsoft.gkzp.syscore.config.MyDefinedUtil;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Configuration //内置tomcat运行不加它没问题,但后来改为用外置tomcat时,启动后过滤器会失效,后来查明原因需要加上@Configuration才行 @WebFilter(urlPatterns
    = "/*", filterName = "corsFilter") public class CorsFilter implements Filter { final private static Logger logger = LogManager.getLogger(CorsFilter.class); @Autowired MyDefinedUtil myDefinedUtil; @Override public void destroy() { } /** * 此过滤器只是处理跨域问题 * @param servletRequest * @param servletResponse * @param chain * @throws ServletException * @throws IOException */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String origin = request.getHeader("Origin"); if(origin == null) { origin = request.getHeader("Referer"); } //允许跨域白名单 String whiteList=myDefinedUtil.SYSTEM_ACCESSCONTROLALLOWORIGIN; boolean isValid = false; String adressIP = null;//截取地址栏的ip地址 if(origin != null){ try { adressIP = origin.substring(origin.indexOf("://") + 3); int b = adressIP.indexOf(":");//有端口号情况 eg:https://127.0.0.1:8080 if (b > 0) { adressIP = adressIP.substring(0, b); }else{ b = adressIP.indexOf("/");//如果是默认端口号,地址栏不填写端口情况(443 80)eg: https://127.0.0.1 if (b > 0) { adressIP = adressIP.substring(0, b); } } isValid = whiteList.contains(adressIP); //将origin截出ip字符串 }catch (Exception e){ logger.error("白名单校验出错:"+e.getMessage(),e); } } logger.info("跨域验证:origin="+origin+"***adressIP="+adressIP+"***isValid="+isValid);// 如为跨域请求,下面的"Access-Control-Allow-Origin"值置为null,就无法访问了。。。如果为非跨域请求,这个为null不会受影响,依然允许访问 response.setHeader("Access-Control-Allow-Origin", isValid ? origin : "null");// 允许指定域访问跨域资源(这里不能写*,*代表接受所有域名访问,如写*则下面一行代码无效。谨记) response.setHeader("Access-Control-Allow-Credentials", "true");//true代表允许客户端携带cookie(此时origin值不能为“*”,只能为指定单一域名) response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"); /// 允许浏览器在预检请求成功之后发送的实际请求方法名 response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin, X-Requested-With, Content-Type, Accept,Access-Token");// 允许浏览器发送的请求消息头 //response.setHeader("Access-Control-Max-Age", "86400"); // 浏览器缓存预检请求结果时间,单位:秒 //logger.info("****************测试过滤器及日志1111"); chain.doFilter(request,response); //logger.error("****************测试过滤器及日志2222"); } @Override public void init(FilterConfig filterConfig) throws ServletException { } }

     

     

     

    白名单心路历程如下:

    首先参考 https://blog.csdn.net/qq_15054679/article/details/90684703 :

     config.properties 

    #允许CORS的IP(即可跨域访问白名单,添加多个用英文逗号隔开)(本地连接在CorsFilter.java中已设置,就不在这里配置了) 
    system.accessControlAllowOrigin =http://localhost:8082

    CorsFilter.java

    package com.nsoft.gkzp.syscore.config.filter;
    
    import com.nsoft.gkzp.syscore.config.MyDefinedUtil;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Configuration //内置tomcat运行不加它没问题,但后来改为用外置tomcat时,启动后过滤器会失效,后来查明原因需要加上@Configuration才行 @WebFilter(urlPatterns
    = "/*", filterName = "corsFilter") public class CorsFilter implements Filter { final private static Logger logger = LogManager.getLogger(CorsFilter.class); @Autowired MyDefinedUtil myDefinedUtil; @Override public void destroy() { } /** * 此过滤器只是处理跨域问题 * @param servletRequest * @param servletResponse * @param chain * @throws ServletException * @throws IOException */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String origin = request.getHeader("Origin"); if(origin == null) { origin = request.getHeader("Referer"); } //允许跨域白名单 String[] whiteList = (myDefinedUtil.SYSTEM_ACCESSCONTROLALLOWORIGIN).split(",") ; boolean isValid = false; logger.info("origin="+origin); for(String ip : whiteList){ if(origin != null && origin.equals(ip)){ isValid = true; break; } } logger.info("isValid="+isValid);//如为跨域请求,下面的"Access-Control-Allow-Origin"值置为null,就无法访问了。。。如果为非跨域请求,这个为null不会受影响,依然允许访问
            response.setHeader("Access-Control-Allow-Origin", isValid ? origin : "null");// 允许指定域访问跨域资源(这里不能写*,*代表接受所有域名访问,如写*则下面一行代码无效。谨记)
            response.setHeader("Access-Control-Allow-Credentials", "true");//true代表允许客户端携带cookie(此时origin值不能为“*”,只能为指定单一域名)
            response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"); /// 允许浏览器在预检请求成功之后发送的实际请求方法名
            response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin, X-Requested-With, Content-Type, Accept,Access-Token");// 允许浏览器发送的请求消息头
            //response.setHeader("Access-Control-Max-Age", "86400");            // 浏览器缓存预检请求结果时间,单位:秒
    
            chain.doFilter(request,response);
        }
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
    
    }

    上面代码用  origin.equals(ip) 去判定不是很好。

    原因是测试时发现,

    1)origin如果是域名的话(值为http://zhxy.nsoft.com.cn:8082),如果是ip地址的话会有斜杠(值为:http://120.24.253.6:8082/)。这样在config.properties配置文件配置白名单参数system.accessControlAllowOrigin时会有很多,其很麻烦

    2)我把http协议改为https协议时:端口号变了,http也改成https,参数system.accessControlAllowOrigin改的时候也很麻烦。

     

     

     

     

    参照:

    https://www.cnblogs.com/zimublog/p/10786110.html

    https://blog.csdn.net/qq_17555933/article/details/92017890

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-4 09:57 , Processed in 0.062682 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表