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

服务端SpringBoot2.x   :localhost:8082html

前端Vue2.x                 :localhost:81前端

先后端的端口号不一样,为跨域,致使前端访问后端时,每次访问都新生产一个sessionID。解决以下:vue

 

后端:java

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;

@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请求)ios

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; } }

 

前端:web

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

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

 

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

我原来的代码:apache

1)写的注册页面:axios

 

 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. 跨域白名单

另:为了安全起见,可在服务端设置可跨域访问的白名单地址,参照 http://www.javashuo.com/article/p-gksecfux-bq.html

我参照写的以下:

1.我以前自定义了一个配置文件 D:\workspace-gzy-gkzp\src\main\resources\resources\config.properties 

#自定义配置文件


#System Encoding
system.encoding=UTF-8

#图片上传下载路径
system.file.folder.img = D:/file/img/


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

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;

@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 {
    }


}

 

 

 

4.后记 (2019.11.05)

上面代码用  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改的时候很麻烦。

修改配置以下:

config.properties  (这里对于localhost,127.0.0.1两个ip,不要放到正式环境。不然如对方用本地环境,去访问正式的后台,会被容许跨域访问,不安全)

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

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;

@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;
//        for(String ip : whiteList){//这里我设置本地访问(localhost,127.0.0.1)自动为白名单里的
//             if(origin != null && origin.equals(ip)){
//                isValid = true;
//                break;
//            }
//        }

        //容许跨域白名单
        String whiteList=myDefinedUtil.SYSTEM_ACCESSCONTROLALLOWORIGIN;
        boolean isValid = false;
         String temp    = null;
        if(origin != null){
            try {
                int a = origin.indexOf("://") + 3; temp = origin.substring(origin.indexOf("://") + 3); int b = temp.indexOf(":"); if (b > 0) { temp = temp.substring(0, b); } isValid = whiteList.contains(temp); //将origin截出ip字符串
            }catch (Exception e){ logger.error("白名单校验出错:"+e.getMessage(),e); }
        }
        logger.info("跨域验证:origin="+origin+"***temp="+temp+"***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 {
    }


}

 

 

 

 

 

 

 

参照:

http://www.javashuo.com/article/p-nnkkqoqa-cg.html

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

相关文章
相关标签/搜索