springboot+vue2.x 解決session跨域失效問題


服務端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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM