解決跨域請求,NG返回403(403并不一定是NG問題)
先說問題怎么解決的
1.ng返回403的情況也就那么幾種,百度一下都能找到,但是ng返回403不一定是ng的問題。
2.最終發(fā)現(xiàn)是在網(wǎng)關(guān)跨域配置中,沒有加上請求方的域名。
3.想探索的可以看看文章
背景
和合作方對接,我們這邊的app開發(fā)完放在合作方的服務(wù)器上,再通過NG,請求我這邊的后臺。
NG配置跨域等信息后(這個很容易找,百度隨便都能找到),發(fā)現(xiàn)測試環(huán)境一切正常,但是到了生產(chǎn),NG一直返回403。
請求都沒通過,網(wǎng)關(guān)zuul沒有日志。雙方開始排查NG,百度了無數(shù)次。
最后都配置與測試環(huán)境一直,但是生產(chǎn)一直不通。
返回403,加上zuul沒日志,一直定位在NG跨域的問題上。
解決
最后發(fā)現(xiàn)是zuul項(xiàng)目的問題,SpringBoot跨域問題。
嘗試排查是否zuul的問題,對比了測試環(huán)境和生產(chǎn)的。
測試環(huán)境zuul的跨域配置CorsConfiguration類屬性allowedOrigins賦值了個*,生產(chǎn)是針對地址進(jìn)行配置的。
其實(shí)就是SpringBoot的跨域配置,源碼CorsConfiguration的allowedOrigins屬性沒有加上第三方的域名地址。
導(dǎo)致直接被拒絕了。加上合作方地址,問題解決。
源碼解析
在配置跨域問題時,我們需要對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;
}
進(jìn)入checkOrigin(config, requestOrigin);校驗(yàn)請求來源
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;
}
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring?NamedContextFactory在Fegin配置及使用詳解
在我們?nèi)粘m?xiàng)目中,使用FeignClient實(shí)現(xiàn)各系統(tǒng)接口調(diào)用變得更加簡單,?在各個系統(tǒng)集成過程中,難免會遇到某些系統(tǒng)的Client需要特殊的配置、返回讀取等需求。Feign使用NamedContextFactory來為每個Client模塊構(gòu)造單獨(dú)的上下文(ApplicationContext)2023-11-11
Java消息隊(duì)列RabbitMQ之消息回調(diào)詳解
這篇文章主要介紹了Java消息隊(duì)列RabbitMQ之消息回調(diào)詳解,消息回調(diào),其實(shí)就是消息確認(rèn)(生產(chǎn)者推送消息成功,消費(fèi)者接收消息成功) , 對于程序來說,發(fā)送者沒法確認(rèn)是否發(fā)送成功,需要的朋友可以參考下2023-07-07
Java的MyBatis+Spring框架中使用數(shù)據(jù)訪問對象DAO模式的方法
Data Access Object數(shù)據(jù)訪問對象模式在Java操作數(shù)據(jù)庫部分的程序設(shè)計(jì)中經(jīng)常被使用到,這里我們就來看一下Java的MyBatis+Spring框架中使用數(shù)據(jù)訪問對象DAO模式的方法:2016-06-06
Spring Boot @Async 異步任務(wù)執(zhí)行方法
本篇文章主要介紹了Spring Boot @Async 異步任務(wù)執(zhí)行方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
SpringBoot中添加監(jiān)聽器及創(chuàng)建線程的代碼示例
這篇文章主要介紹了SpringBoot中如何添加監(jiān)聽器及創(chuàng)建線程,文中有詳細(xì)的代碼示例,具有一定的參考價值,需要的朋友可以參考下2023-06-06
SpringBoot?Java通過API的方式調(diào)用騰訊智能體(騰訊元寶)代碼示例
這篇文章主要介紹了SpringBoot?Java通過API的方式調(diào)用騰訊智能體(騰訊元寶)的相關(guān)資料,詳細(xì)說明參數(shù)獲取及動態(tài)處理方法,提供結(jié)果字符串轉(zhuǎn)數(shù)組技巧,需要的朋友可以參考下2025-06-06
Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實(shí)踐
本文主要介紹了Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
java實(shí)現(xiàn)ArrayList根據(jù)存儲對象排序功能示例
這篇文章主要介紹了java實(shí)現(xiàn)ArrayList根據(jù)存儲對象排序功能,結(jié)合實(shí)例形式分析了java針對ArrayList的相關(guān)運(yùn)算、排序操作技巧,需要的朋友可以參考下2018-01-01

