기술나눔

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 요청)이 전송됩니다.

코르스 디버깅 그림 2

sa-token과 통합된 프로젝트에서 OncePerRequestFilter의 doFilterInternal 메서드에 중단점을 설정하고 다음 필터를 찾았습니다.

코르스 디버깅 그림 2

위에서 커스터마이즈한 CustomCorsFilter가 먼저 실행되고 sa-token과 관련된 두 개의 필터 saPathCheckFilterForServlet 및 SaServletFilter가 나중에 실행되는 것을 볼 수 있으므로 이전의 다른 추측도 잘 확인되었습니다. 중단점은 아래 CorsFilter의 doFilterInternal 메서드로 이동합니다. :

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

문제는 다음 코드에 있습니다.

CorsUtils.isPreFlightRequest(request)
  • 1

이 코드 줄은 true를 반환하므로 모든 후속 필터가 실행되지 않아 요청이 인터페이스에 도달할 수 없습니다. 그런 다음 CorsUtils.isPreFlightRequest(request) 코드 줄을 디버깅했습니다. . 복잡한 요청이 도메인 전체에 걸쳐 사전 감지 요청(OPTIONS 요청)을 보낸 후 Origin이 설명할 수 없는 방식으로 설정되는 것으로 나타났습니다.

http://localhost:3000
  • 1

이것은 매우 이상해서 프런트엔드 동료인 Lao Wang에게 이 값이 어디에 설정되어 있는지 확인해달라고 요청한 결과 다음과 같은 내용을 발견했습니다.

코르스 디버깅 그림 3

프런트엔드는 Origin을 설정하지 않고 다음 매개변수만 설정합니다.

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

위 그림에는 Origin 값과 일치하는 Referer 값이 하나만 있는데, 이게 정말 우연일까 싶어서 다음 글을 읽어봤습니다.

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

많은 생각을 하게 되어서 곰곰이 생각해보니 백엔드에 설정한 CustomCorsFilter가 효과적이었던 것 같습니다. CorsFilter 소스코드를 변경할 수 있을까요? 그래서 제가 확인한 다음 두 가지 방법을 시도해 보았습니다. 프런트엔드 Lao Wang에서는 두 가지 방법 모두 실행 가능하며 어느 쪽이든 크로스 도메인 문제를 해결할 수 있습니다.

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

코르스 디버깅 그림 4

브라우저의 리퍼러 정책 리퍼러 정책도 보안상의 이유로 사용하는 브라우저입니다.

4. 요약

끊임없는 시도와 탐색 끝에 sa-token을 프로젝트에 통합한 후 cors 크로스 도메인 배포 문제가 발생했습니다. 일반적으로 cors 크로스 도메인 프로토콜의 사양은 이 작업을 허용하지 않습니다. -end 솔루션 라이브러리는 cors 크로스 도메인 프로토콜의 사양을 따르지 않는 요청 작업을 허용하지 않으므로 sa-token을 사용하여 프런트 엔드 및 백엔드 솔루션을 크로스 도메인 솔루션의 올바른 자세로 분리할 수 있습니다. SimpleCORSFilter를 구성하고 특정 매개변수를 작성하고 사전 감지 요청을 처리하면 200 상태 코드가 반환됩니다. 또는 이 문서의 두 가지 방법 중 하나를 사용하여 cors 교차 도메인 문제를 해결할 수 있습니다. 중단점에서 도메인 간 요청을 재현한 후 디버깅합니다. 해결 방법, 이 문서의 방법을 따를 수 있다고 생각되면 로컬에서 도메인 간 요청을 재현한 다음 중단점을 디버깅하여 문제가 있는지 확인하세요. corsFilter의 CorsUtils.isPreFlightRequest(request) 라인은 일반적으로 corsFilter에 대한 간단한 요청은 사전 감지 요청(OPTIONS 요청)을 보내는 데 큰 문제입니다. 브라우저의 리퍼러 정책 리퍼러 정책은 백엔드 처리 후 값을 전달하므로 이 값은 요청 헤더의 Origin 속성에 할당되어 corsFilter의 CorsUtils.isPreFlightRequest(request) 코드 줄이 true를 반환하게 됩니다. 결과적으로 후속 필터 시리즈의 doFilter가 실행되지 않고 백엔드 인터페이스에 액세스할 수 없으며 프런트엔드 브라우저 페이지에서 여전히 도메인 간 문제를 보고합니다. , 이 문제는 정말 짜증나네요. https 방법은 포트 443을 수신하고 인증서가 필요하기 때문에 도메인 간 작동하지 않습니다. 이 문서에서는 sa-token을 통합하면 corsFilter 구성이 유효하지 않게 된다는 비난을 해소합니다. 이 글의 어떤 글도 이 상황에 대한 해결책을 갖고 있지 않습니다. 창작이 쉽지는 않습니다. 원본을 다시 인쇄하여 추가해 주세요. 제 공유가 여러분에게 영감을 주고 도움이 되기를 바랍니다. 연결하려면 클릭하세요.