Compartir tecnología

Integre sa-token, separe la implementación de front-end y back-end y configure corsFliter para resolver la causa real de la falla entre dominios.

2024-07-12

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

1. Introducción

Consulte el siguiente artículo para conocer el motivo: La forma correcta de resolver problemas entre dominios separando el front-end y el back-end de 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

Aunque este artículo encontró la postura correcta después de muchos intentos, no se encontró la causa raíz del problema. Luego, después de pensar y explorar un poco, me pregunté si este dominio cruzado se podría simular localmente y luego comencé a buscarlo. Wang, el personal de front-end del proyecto, simuló el dominio cruzado local en el proyecto, separó el front-end y el back-end del token sa anterior para resolver la postura correcta del dominio cruzado y reprodujo el problema. después de comentar sobre SimpleCORSFilter en el artículo.

2. Recurrencia del problema

La configuración entre dominios del mvc oficial de spring5.2.15.RELEASE es la siguiente:

https://docs.spring.io/spring-framework/docs/5.2.15.RELEASE/spring-framework-reference/web.html#mvc-cors-intro
  • 1

Siga el método oficial vinculado anteriormente y luego agregue una clase CustomCorsFilter de la siguiente manera:

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

Se ha verificado que la configuración de CustomCorsFilter es válida en un proyecto en el que trabajé. Sin embargo, en un proyecto reciente, es inútil usar esta configuración de clase CustomCorsFilter para resolver problemas entre dominios. En el artículo anterior sobre la forma correcta de resolver problemas entre dominios separando el front-end y el back-end de sa-token, la verificación de la clase CustomCorsFilter no era válida. Este problema fue realmente desconcertante cuando reconfigure la clase CustomCorsFilter en el proyecto. (Es necesario comentar SimpleCORSFilter, porque este SimpleCORSFilter se puede usar después de la verificación). Después de ejecutar el proyecto localmente, la interfaz simula solicitudes entre dominios en el siguiente código de la clase 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

También coloque un punto de interrupción en el método 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

Entonces la captura de pantalla del punto de interrupción es la siguiente:

La interfaz de inicio de sesión es una solicitud compleja entre dominios con http más un nombre de dominio, y se enviará una solicitud de detección previa (solicitud de OPCIONES).

imagen de depuración de cors 2

En el proyecto integrado con sa-token, establecí un punto de interrupción en el método doFilterInternal de OncePerRequestFilter y encontré los siguientes filtros:

imagen de depuración de cors 2

Se puede ver que el CustomCorsFilter que personalizamos anteriormente se ejecuta primero, y los dos filtros relacionados con sa-token: saPathCheckFilterForServlet y SaServletFilter se ejecutan más tarde, por lo que otras conjeturas anteriores han sido bien confirmadas. El punto de interrupción va al método doFilterInternal de CorsFilter a continuación. :

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

El problema radica en el siguiente código:

CorsUtils.isPreFlightRequest(request)
  • 1

Esta línea de código devolvió verdadero, por lo que no se ejecutaron todos los filtros posteriores, por lo que la solicitud no pudo llegar a la interfaz. El navegador todavía informaba problemas entre dominios. Luego depuré la línea de código CorsUtils.isPreFlightRequest (solicitud). , se descubrió que después de que una solicitud compleja enviara una solicitud de detección previa (solicitud de OPCIONES) entre dominios, el Origen se establecería de una manera inexplicable:

http://localhost:3000
  • 1

Esto era muy extraño, así que le pedí a mi colega de front-end, Lao Wangbang, que comprobara dónde estaba establecido este valor y encontré lo siguiente:

imagen de depuración de cors 3

La interfaz no establece el origen, solo establece los siguientes parámetros:

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

En la imagen de arriba, solo hay un valor de Referencia que coincide con el valor de Origen. ¿Es esto realmente solo una coincidencia? Entonces leí el siguiente artículo:

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

Me dio muchas ideas, así que lo pensé y resultó que el CustomCorsFilter configurado en el backend era efectivo. ¿Puedo cambiar el código fuente de CorsFilter? Entonces probé los dos métodos siguientes, que fueron verificados por mí y En el front-end Lao Wang, ambos métodos son factibles y cualquiera de ellos puede resolver el problema de dominio cruzado de cors.

3.Solución

3.1 Método 1: modificar el código fuente de CorsFilter en el backend

Modifique el código fuente de CorsFilter en el backend

Agregue un nuevo paquete org.springframework.web.filter en src.main.java en el proyecto

Luego copie el código fuente de CorsFilter y colóquelo en el paquete org.springframework.web.filter recién creado arriba, y luego podrá modificar el código fuente de 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 Método 2: deshabilitar o eliminar la política de referencia del navegador en la interfaz

Agregue el siguiente código al front-end: deshabilite o elimine la política de referencia referrer-policy`

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

El nuevo código es el siguiente:

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

imagen de depuración de cors 4

La política de referencia del navegador también es por razones de seguridad. El navegador que uso es el navegador Chrome.

4. Resumen

Después de constantes intentos y tanteos, el problema de la implementación entre dominios de cors ocurrió después de integrar sa-token en el proyecto. En términos generales, las especificaciones del protocolo entre dominios de cors no permiten esta operación. La biblioteca de soluciones -end no permite operaciones de solicitud que no sigan las especificaciones del protocolo de dominio cruzado cors, por lo que puede usar sa-token para separar las soluciones de front-end y back-end en la postura correcta de las soluciones de dominio cruzado Configure un SimpleCORSFilter, escriba parámetros específicos y procese La solicitud de detección previa devuelve un código de estado 200, o puede utilizar cualquiera de los dos métodos de este artículo para resolver el problema entre dominios de cors. Lo depuro después de reproducir la solicitud entre dominios en el punto de interrupción. Solución: si cree que puede seguir el método de este artículo, reproduzca la solicitud entre dominios localmente y luego depure el punto de interrupción para ver si es un problema. la línea CorsUtils.isPreFlightRequest (solicitud) de corsFilter En general, este cors El problema entre dominios es un gran problema para los navegadores. Una solicitud simple a corsFilter no fallará. Una solicitud compleja envía una solicitud de detección previa (solicitud de OPCIONES). Dado que la política de referencia de la política de referencia del navegador llevará un valor, después del procesamiento de back-end, este valor se asignará al atributo Origen del encabezado de la solicitud, lo que hará que la línea de código CorsUtils.isPreFlightRequest (solicitud) de corsFilter devuelva verdadero. y luego regresar, como resultado, el doFilter de la serie posterior de filtros no se ejecuta y no hay acceso a la interfaz de back-end y la página del navegador de front-end todavía informa problemas entre dominios. , este problema es realmente molesto. El método https no funciona entre dominios, porque https escucha el puerto 443 y requiere un certificado. Supongo que esto requiere configurar el certificado. Este artículo aclara la acusación de que la integración de sa-token hará que la configuración de corsFilter deje de ser válida. Es lo mismo en Internet. Ninguno de los artículos de este artículo tiene una solución para esta situación. Cabe decir que es la primera creación que no es fácil. Por favor, respete la originalidad del autor. original Vuelva a imprimir y agregue la fuente original. Este intercambio termina aquí. Espero que mi intercambio pueda inspirarlo y ayudarlo. Haga clic tres veces para conectarse.