服務端SpringBoot2.x :localhost:8082html
前端Vue2.x :localhost:81前端
先後端的端口號不一樣,爲跨域,致使前端訪問後端時,每次訪問都新生產一個sessionID。解決以下:vue
後端:java
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 { } }
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); }; } }
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/' };
另:爲了安全起見,可在服務端設置可跨域訪問的白名單地址,參照 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 { } }
參照: