技術共有

sa-token を統合し、フロントエンドとバックエンドのデプロイメントを分離し、corsFliter を構成してクロスドメイン障害の本当の原因を解決します。

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

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
  • 1
  • 2

この記事では、何度も試した結果、正しい姿勢が見つかりましたが、問題の根本原因は見つかりませんでした。その後、少し考えて調べた後、このクロスドメインをローカルでシミュレートできないかと考え、探し始めました。プロジェクトのフロントエンド スタッフである Wang 氏は、プロジェクト内で実際にローカル クロスドメインをシミュレートし、クロスドメインの正しい姿勢を解決するために以前の 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
  • 1

上記のリンク先の公式メソッドに従って、次のように 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:8080
        configuration.addAllowedHeader("*"); // 允许所有请求头
        configuration.addAllowedMethod("*"); // 允许所有请求方法
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

CustomCorsFilter の構成は、私が取り組んだプロジェクトでは有効であることが確認されていますが、最近のプロジェクトでは、この CustomCorsFilter クラス構成を使用してクロスドメインの問題を解決することは役に立ちません。 sa-token のフロントエンドとバックエンドを分離することでクロスドメインの問題を解決する正しい方法に関する上記の記事では、CustomCorsFilter クラスの検証が実際に無効でした。プロジェクトで CustomCorsFilter クラスを再構成したとき、この問題は非常に不可解でした。 (この SimpleCORSFilter は検証後に使用できるため、SimpleCORSFilter をコメントする必要があります)。プロジェクトをローカルで実行した後、フロントエンドは CorsFilter クラスの次のコードにブレークポイントを置きます。

   @Override
	protected 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);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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);
	}
  • 1
  • 2
  • 3
  • 4
  • 5

ブレークポイントのスクリーンショットは次のとおりです。

ログイン インターフェイスは http とドメイン名を組み合わせた複雑なクロスドメイン リクエストであり、事前検出リクエスト (OPTIONS リクエスト) が送信されます。

cors デバッグ画像 2

sa-token と統合されたプロジェクトで、OncePerRequestFilter の doFilterInternal メソッドにブレークポイントを設定したところ、次のフィルターが見つかりました。

cors デバッグ画像 2

上記でカスタマイズした CustomCorsFilter が最初に実行され、sa-token に関連する 2 つのフィルター (saPathCheckFilterForServlet と SaServletFilter) が後で実行されることがわかります。そのため、他の以前の推測がよく確認されています。ブレークポイントは以下の CorsFilter の doFilterInternal メソッドに移動します。 :

       //关键就是这里,在这里打上断点
		if (!isValid || CorsUtils.isPreFlightRequest(request)) {
			return;
		}
  • 1
  • 2
  • 3
  • 4

問題は次のコードにあります。

CorsUtils.isPreFlightRequest(request)
  • 1

このコード行は true を返したので、後続のフィルターはすべて実行されなかったため、ブラウザーは依然としてクロスドメインの問題を報告していました。次に、コード行 CorsUtils.isPreFlightRequest(request) をデバッグしました。 、複雑なリクエストがドメイン間で事前検出リクエスト (OPTIONS リクエスト) を送信した後、オリジンが不可解な方法で設定されることが判明しました。

http://localhost:3000
  • 1

これは非常に奇妙だったので、フロントエンドの同僚である Lao Wangbang にこの値がどこに設定されているかを確認してもらったところ、次のことがわかりました。

cors デバッグ画像 3

フロントエンドは Origin を設定せず、次のパラメーターのみを設定します。

//设置axios跨域访问
axios.defaults.withcredentials = true // 设置cross跨域 并设置访问权限 允许跨域携带cookie信息axios.defaults.crossDomain=true //设置axios跨域的配置
  • 1
  • 2

上の図では、Origin の値と一致する Referer の値が 1 つだけあります。これは本当に単なる偶然なのでしょうか。そこで、次の記事を読みました。

https://blog.csdn.net/qq_55316925/article/details/128571809
  • 1

色々とアイデアが出たので考えてみたところ、バックエンドに設定したCustomCorsFilterが有効だったので、CorsFilterのソースコードを変更すれば良いのではないかと思い、私が検証した以下の2つの方法を試してみました。フロントエンド Lao Wang の場合、両方の方法が実行可能であり、どちらの方法でも cors クロスドメイン問題を解決できます。

3.解決策

3.1 方法 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;
	}


	@Override
	protected 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);
	}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

3.2 方法 2: フロントエンドでブラウザー リファラー ポリシーを無効にするか削除する

次のコードをフロントエンドに追加します: Referrer-policy` リファラー ポリシーを無効にするか削除します。

https://blog.csdn.net/qq_49810363/article/details/111036180
  • 1

新しいコードは次のとおりです。

<meta name="referrer" content="never">
  • 1

cors デバッグ画像 4

ブラウザのリファラー ポリシー リファラー ポリシーもセキュリティ上の理由からです。 私が使用しているブラウザは Chrome ブラウザです。

4. まとめ

継続的な試行と模索の結果、プロジェクトに sa-token を統合した後、cors クロスドメイン デプロイメントの問題が発生しました。また、一般的に、cors クロスドメイン プロトコルの仕様では、この操作が許可されていません。 -end ソリューション ライブラリでは、cors クロスドメイン プロトコルの仕様に従わないリクエスト操作が許可されないため、sa-token を使用して、フロントエンド ソリューションとバックエンド ソリューションをクロスドメイン ソリューションの正しい姿勢に分離できます。 SimpleCORSFilter を構成し、特定のパラメーターを書き込み、処理します。事前検出リクエストは 200 ステータス コードを返します。または、この記事の 2 つの方法のいずれかを使用して、cors クロスドメインの問題を解決できます。ブレーク ポイントでクロスドメイン リクエストを再現した後にデバッグします。 解決策として、この記事の方法に従うことができると思われる場合は、クロスドメイン リクエストをローカルで再現し、ブレーク ポイントに問題があるかどうかを確認します。 corsFilter の CorsUtils.isPreFlightRequest(request) 行 一般に、この cors クロスドメインの問題はブラウザにとって大きな問題です。corsFilter への単純なリクエストは、事前検出リクエスト (OPTIONS リクエスト) を送信します。ブラウザーの Referrer-policy リファラー ポリシーには値が含まれるため、バックエンド処理の後、この値はリクエスト ヘッダーの Origin 属性に割り当てられ、corsFilter のコード行 CorsUtils.isPreFlightRequest(request) が true を返します。その結果、後続の一連のフィルターの doFilter は実行されず、バックエンド インターフェイスにアクセスできず、正直に言うと、フロントエンド ブラウザー ページは引き続きクロスドメインの問題を報告します。 , この問題は本当に厄介です。 https メソッドはクロスドメインでは機能しません。https はポート 443 をリッスンし、証明書を必要とするためです。この記事では、sa-token を統合すると corsFilter 設定が無効になるという非難を解決します。この記事のどの記事にもこの状況に対する解決策はありません。作成者の独創性を尊重してください。オリジナルです。この共有はここで終わります。3 回クリックしてください。