跨域请求,NG返回403。(403并不一定是NG问题)

文章讲述了在集成合作方服务时遇到的403错误问题,经过排查发现并非NG的问题,而是SpringBoot的Zuul跨域配置不完整。在生产环境中,由于CorsConfiguration的allowedOrigins未包含合作方域名,导致请求被拒绝。修复方法是确保在配置中添加正确的请求源域名。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先说问题怎么解决的:

1.ng返回403的情况也就那么几种,百度一下都能找到,但是ng返回403不一定是ng的问题。
2.最终发现是在网关跨域配置中,没有加上请求方的域名。
3.想探索的可以看看文章

背景

和合作方对接,我们这边的app开发完放在合作方的服务器上,再通过NG,请求我这边的后台。NG配置跨域等信息后(这个很容易找,百度随便都能找到),发现测试环境一切正常,但是到了生产,NG一直返回403。请求都没通过,网关zuul没有日志。双方开始排查NG,百度了无数次。最后都配置与测试环境一直,但是生产一直不通。返回403,加上zuul没日志,一直定位在NG跨域的问题上。

解决:

最后发现是zuul项目的问题,SpringBoot跨域问题。尝试排查是否zuul的问题,对比了测试环境和生产的。测试环境zuul的跨域配置CorsConfiguration类属性allowedOrigins赋值了个*,生产是针对地址进行配置的。其实就是SpringBoot的跨域配置,源码CorsConfiguration的allowedOrigins属性没有加上第三方的域名地址。导致直接被拒绝了。加上合作方地址,问题解决。

源码解析

在配置跨域问题时,我们需要对CorsConfiguration属性赋值

//对CorsConfiguration属性赋值
private CorsConfiguration corsConfig(Map.Entry<String, JawsCorsConfig> entry) {
    CorsConfiguration corsConfiguration = new CorsConfiguration();
    corsConfiguration.setAllowedOrigins(Splitter.on(",").splitToList(entry.getValue().getAllowedOrigins()));
    corsConfiguration.setAllowedHeaders(Splitter.on(",").splitToList(entry.getValue().getAllowedHeaders()));
    corsConfiguration.setAllowedMethods(Splitter.on(",").splitToList(entry.getValue().getAllowedMethods()));
    corsConfiguration.setAllowCredentials(entry.getValue().getAllowCredentials());
    corsConfiguration.setMaxAge(entry.getValue().getMaxAge());
    return corsConfiguration;
}

@Bean
    public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    for (Map.Entry<String, JawsCorsConfig> entry : jawsZuulProperites.getCors().entrySet()) {
        source.registerCorsConfiguration(entry.getValue().getUrls(), corsConfig(entry));
    }
    return new CorsFilter(source);
}

源码中 org.springframework.web.cors.DefaultCorsProcessor#handleInternal 方法

protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
			CorsConfiguration config, boolean preFlightRequest) throws IOException {

		String requestOrigin = request.getHeaders().getOrigin();
		String allowOrigin = checkOrigin(config, requestOrigin);

		HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
		List<HttpMethod> allowMethods = checkMethods(config, requestMethod);

		List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
		List<String> allowHeaders = checkHeaders(config, requestHeaders);

		if (allowOrigin == null || allowMethods == null || (preFlightRequest && allowHeaders == null)) {
			rejectRequest(response);
			return false;
		}

		HttpHeaders responseHeaders = response.getHeaders();
		responseHeaders.setAccessControlAllowOrigin(allowOrigin);
		responseHeaders.add(HttpHeaders.VARY, HttpHeaders.ORIGIN);

		if (preFlightRequest) {
			responseHeaders.setAccessControlAllowMethods(allowMethods);
		}

		if (preFlightRequest && !allowHeaders.isEmpty()) {
			responseHeaders.setAccessControlAllowHeaders(allowHeaders);
		}

		if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
			responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
		}

		if (Boolean.TRUE.equals(config.getAllowCredentials())) {
			responseHeaders.setAccessControlAllowCredentials(true);
		}

		if (preFlightRequest && config.getMaxAge() != null) {
			responseHeaders.setAccessControlMaxAge(config.getMaxAge());
		}

		response.flush();
		return true;
	}
	

进入checkOrigin(config, requestOrigin);校验请求来源


	public static final String ALL = "*";
	/**
	 * Check the origin of the request against the configured allowed origins.
	 * @param requestOrigin the origin to check
	 * @return the origin to use for the response, or {@code null} which
	 * means the request origin is not allowed
	 */
	public String checkOrigin(String requestOrigin) {
		if (!StringUtils.hasText(requestOrigin)) {
			return null;
		}
		if (ObjectUtils.isEmpty(this.allowedOrigins)) {
			return null;
		}

		if (this.allowedOrigins.contains(ALL)) {
			if (this.allowCredentials != Boolean.TRUE) {
				return ALL;
			}
			else {
				return requestOrigin;
			}
		}
//遍历我们赋值的allowedOrigins,判断请求来源是否包含,包含则返回
		for (String allowedOrigin : this.allowedOrigins) {
			if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
				return requestOrigin;
			}
		}
return null;
}
		
在Angular(Ng)项目中配置规则以支持file协议(通常用于本地开发和测试,比如通过`file://`访问静态文件),可以使用`@angular/common/http`模块提供的`HttpClient`服务的拦截器功能。由于浏览器的安全限制,直接从file协议加载内容可能会遇到同源策略问题。以下是设置的基本步骤: 1. 首先,在`app.module.ts`中导入`HttpClientModule`和`JsonpModule`(如果使用的是旧版的`http`库,而不是`HttpClient`): ```typescript import { HttpClientModule } from '@angular/common/http'; // 如果使用HttpClient import { JsonpModule } from '@angular/common/http'; @NgModule({ imports: [HttpClientModule, JsonpModule], // 或者只导入HttpClientModule }) export class AppModule {} ``` 2. 创建一个全局拦截器,如`globalInterceptor.service.ts`: ```typescript import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class GlobalInterceptorService { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const newRequest = req.clone({ setHeaders: {'X-Custom-Header': 'your-header-value'} }); // 添加自定义头 if (isFileProtocol(req.url)) { // 检查是否是file协议 // 如果是,允许请求 return next.handle(newRequest); } return next.handle(req); // 否则,让默认处理继续 } private isFileProtocol(url: string) { return url.startsWith('file:'); } } ``` 3. 在`app.module.ts`中注入并启用拦截器: ```typescript import { HttpClientModule } from '@angular/common/http'; import { GlobalInterceptorService } from './global-interceptor.service'; @NgModule({ providers: [ GlobalInterceptorService, { provide: HTTP_INTERCEPTORS, useClass: GlobalInterceptorService, multi: true }, // 注册拦截器 ], imports: [HttpClientModule], }) export class AppModule {} ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值