文章目錄
- 1.前言
- 2.問題復現
- 3.解決方法
- 3.1 方式一:后端修改CorsFilter源碼
- 3.2 方式二:前端禁用或移除瀏覽器referrer-policy引用者策略
- 4.總結
1.前言
????緣由請參看下面這篇文章:sa-token前后端分離解決跨域的正確姿勢
https://mp.weixin.qq.com/s/96WbWL28T5_-xzyCfJ7Stg
https://blog.csdn.net/qq_34905631/article/details/140233780?spm=1001.2014.3001.5501
????這篇文章雖然經過n多次嘗試找到了正確的姿勢,但是問題的根本原因沒有找到,然后我就經過一些思考和探索,我想這個跨域能不能在本地模擬出來,然后起就去找了項目前端人員老王,然后確實模擬出本地跨域,在項目中將之前sa-token前后端分離解決跨域的正確姿勢文章中的SimpleCORSFilter注釋之后進行了問題復現。
2.問題復現
?????spring5.2.15.RELEASE官方mvc的跨域配置如下:
https://docs.spring.io/spring-framework/docs/5.2.15.RELEASE/spring-framework-reference/web.html#mvc-cors-intro
????按照上面鏈接官方的一種方式,然后新增了一個CustomCorsFilter類如下:
package xxxx.config;import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomCorsFilter extends CorsFilter {public CustomCorsFilter() {super(corsConfigurationSource());}private static CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.addAllowedOrigin("*"); // 允許訪問的域名,例如 http://localhost:8080configuration.addAllowedHeader("*"); // 允許所有請求頭configuration.addAllowedMethod("*"); // 允許所有請求方法configuration.setAllowCredentials(true);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}}
????該CustomCorsFilter的配置寫法我搞的一個項目中配置驗證過是有效的,但是在最近做的一個項目中使用這個CustomCorsFilter類配置到項目中解決跨域缺沒有用,難道是這種配置失效了嗎?在上面sa-token前后端分離解決跨域的正確姿勢文章中配置了CustomCorsFilter類驗證確實是失效了,這個問題真的是讓人百思不得其解,當我在項目中重新配置好CustomCorsFilter類之后(SimpleCORSFilter需要注釋,因為這個SimpleCORSFilter進過驗證時可以行的),緊接著把項目本地跑起來之后,前端模擬跨域請求,我在CorsFilter類的如下代碼打上斷點:
@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);boolean isValid = this.processor.processRequest(corsConfiguration, request, response);//關鍵就是這里,在這里打上斷點if (!isValid || CorsUtils.isPreFlightRequest(request)) {return;}filterChain.doFilter(request, response);}
????CorsUtils.isPreFlightRequest方法上也打上斷點:
public static boolean isPreFlightRequest(HttpServletRequest request) {return (HttpMethod.OPTIONS.matches(request.getMethod()) &&request.getHeader(HttpHeaders.ORIGIN) != null &&request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);}
????然后斷點截圖如下:
????登錄接口是一個http加域名的復雜跨域請求會發送一個預檢測請求(OPTIONS請求)
????在集成了sa-token的項目中OncePerRequestFilter的doFilterInternal方法打上斷點,發現有如下過濾器:
????可以看到上面我們自定一的CustomCorsFilter是在最先執行的,sa-token相關的兩個filter:saPathCheckFilterForServlet和SaServletFilter都是后面才執行的,所以之前的其他猜想都得到了很好的證實,當斷點走到下面這里CorsFilter的doFilterInternal方法中:
//關鍵就是這里,在這里打上斷點if (!isValid || CorsUtils.isPreFlightRequest(request)) {return;}
????問題就出在如下代碼:
CorsUtils.isPreFlightRequest(request)
????這行代碼返回了true,就return了,后面的所有過濾器都沒有執行,所以請求不到接口,瀏覽器還是在報跨域的問題,然后我就調試了CorsUtils.isPreFlightRequest(request)這行代碼,發現是復雜請求跨域發了預檢測請求(OPTIONS請求)之后Orgin會被設置了一個莫名奇妙的是:
http://localhost:3000
????這個就很奇怪了,然后我就找了前端同事老王幫看了下,這個值是哪里設置的,然后發現如下:
????前端沒有設置Orgin,只是設置了如下參數:
//設置axios跨域訪問
axios.defaults.withcredentials = true // 設置cross跨域 并設置訪問權限 允許跨域攜帶cookie信息axios.defaults.crossDomain=true //設置axios跨域的配置
????上面的圖片只有一個Referer的值跟Orgin的值不謀而合,難道這真的只是一個巧合,于是我參看了如下文章:
https://blog.csdn.net/qq_55316925/article/details/128571809
????給我很多的思路,那我想了一下,居然這樣,后端配置的CustomCorsFilter是生效了的,那可不可以改CorsFilter源碼,于是就嘗試了下面兩種方式,進過我和前端老王的驗證,這兩種方式都是可行的,任選一種都是可以解決cors跨域問題。
3.解決方法
3.1 方式一:后端修改CorsFilter源碼
????后端修改CorsFilter源碼
????在項目下src.main.java下新增一個org.springframework.web.filter的包
????然后將CorsFilter的源碼拷貝出來,放到上面新建的這個org.springframework.web.filter的包下,就可以修改CorsFilter的源碼了,
/** Copyright 2002-2019 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.web.filter;import java.io.IOException;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.util.Assert;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.CorsProcessor;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.DefaultCorsProcessor;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;/*** {@link javax.servlet.Filter} that handles CORS preflight requests and intercepts* CORS simple and actual requests thanks to a {@link CorsProcessor} implementation* ({@link DefaultCorsProcessor} by default) in order to add the relevant CORS* response headers (like {@code Access-Control-Allow-Origin}) using the provided* {@link CorsConfigurationSource} (for example an {@link UrlBasedCorsConfigurationSource}* instance.** <p>This is an alternative to Spring MVC Java config and XML namespace CORS configuration,* useful for applications depending only on spring-web (not on spring-webmvc) or for* security constraints requiring CORS checks to be performed at {@link javax.servlet.Filter}* level.** <p>This filter could be used in conjunction with {@link DelegatingFilterProxy} in order* to help with its initialization.** @author Sebastien Deleuze* @since 4.2* @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>*/
public class CorsFilter extends OncePerRequestFilter {private final CorsConfigurationSource configSource;private CorsProcessor processor = new DefaultCorsProcessor();/*** Constructor accepting a {@link CorsConfigurationSource} used by the filter* to find the {@link CorsConfiguration} to use for each incoming request.* @see UrlBasedCorsConfigurationSource*/public CorsFilter(CorsConfigurationSource configSource) {Assert.notNull(configSource, "CorsConfigurationSource must not be null");this.configSource = configSource;}/*** Configure a custom {@link CorsProcessor} to use to apply the matched* {@link CorsConfiguration} for a request.* <p>By default {@link DefaultCorsProcessor} is used.*/public void setCorsProcessor(CorsProcessor processor) {Assert.notNull(processor, "CorsProcessor must not be null");this.processor = processor;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);boolean isValid = this.processor.processRequest(corsConfiguration, request, response);//修改的源碼是將CorsUtils.isPreFlightRequest(request)這行代碼移除,就是因為復雜請求跨域發了預檢測請求,瀏覽器的referrer-policy引用者策略會攜帶一個值,后端處理之后會將這個值賦值給請求頭的Orgin屬性上,移除這行代碼之后就可以正常訪問到登錄接口了,前端也沒有報跨域了。if (!isValid) {return;}filterChain.doFilter(request, response);}}
3.2 方式二:前端禁用或移除瀏覽器referrer-policy引用者策略
????前端新增如下代碼 :禁用或者是移除referrer-policy`這個引用者策略
https://blog.csdn.net/qq_49810363/article/details/111036180
????新增代碼如下:
<meta name="referrer" content="never">
????瀏覽器的referrer-policy引用者策略,也是為了安全考慮,我使用的瀏覽器是chrome瀏覽器
4.總結
????經過不斷地嘗試和摸索之后,對這個在項目中集成了sa-token后部署出現cors跨域的問題,總的來說是cors跨域協議的規范不允許這種操作,在加上瀏覽器和后端的解決庫都不允許不遵循cors跨域協議的規范的請求操作,所以可以使用sa-token前后端分離解決跨域的正確姿勢文章里面的方法,配置一個SimpleCORSFilter,對參數都寫具體,處理預檢測請求返回200狀態碼,或者使用本篇文章的這兩種方式的任意一種都可以解決cors跨域的問題,本文的解決方法是我經過親自打斷點復現跨域請求之后debug出來的解決方法,如果你相信可以按照本文的方法,本地復現跨域請求然后dubug打斷點,看一下是不是corsFilter的CorsUtils.isPreFlightRequest(request)這行代碼的問題,總的來說,這個cors的跨域問題是瀏覽器要背的一個大鍋,簡單請求corsFilter是不會失效的,復雜請求發了預檢測請求(OPTIONS請求),由于瀏覽器的referrer-policy引用者策略會攜帶一個值,后端處理之后會將這個值賦值給請求頭的Orgin屬性上,導致corsFilter的CorsUtils.isPreFlightRequest(request)這行代碼返回true之后就return了,導致后面的一系列的Filter的doFilter沒有執行到,也沒有訪問到后端的接口,而前端瀏覽器頁面還在報跨域的問題,這個問題說實話是真的坑啊。https的方式跨域不行,因為https是監聽443端口,是需要證書的,這個我猜測是要配置證書才可以的,本文洗白了集成sa-token之后會導致corsFilter配置失效的罪名啊,網上千篇一律的文章都沒有本文的這種情況下的解決方法的,應該來說還是首創吧,創作不易,請尊重作者原創,請不要抄襲當做原創,請轉載添加上原創出處,本次分享到此結束,希望我的分享對你有所啟發和幫助,請一鍵三連,么么噠!