SpringCloud系列:随笔记录在使用 OAuth2 遇到的巨坑

举报
云享专家 发表于 2019/08/28 14:52:20 2019/08/28
【摘要】 为什么 sensitiveHeaders 中将Authentication 移除,Base Authorization 的信息就可以发送了,参考官网的说明 https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#cookies-

作者:方志朋

方志朋简介:SpringCloud中国社区联合创始人,博客访问量突破一千万,爱好开源,热爱分享,活跃于各大社区,保持着非常强的学习驱动力,终身学习践行者,终身学习受益者。目前就职于国内某家知名互联网保险公司,担任DEVOPS工程师,对微服务领域和持续集成领域研究较深,精通微服务框架SpringCloud

 

本文转载文章,原文链接

https://www.shangyang.me/2017/06/01/spring-cloud-oauth2-zuul-potholes/

前言

根据当前的设计,打算将Spring Boot Authenticate (OAuth2) Server 配置到ZUUL 中,通过ZUUL 实现认证的负载均衡;看似顺理成章的东西,结果在实践过程中,踩到不少坑,也花费不少时间来整理,所以,打算专门写一篇博文来整理自己遇到的坑,以防以后踩到同样的坑,又耗费大量的时间和精力去模式;

环境

§ Authenticate Server: 9999

§ ZUUL Server: 8000

Basic Authenticate 信息丢失

丢失情况一

当前的环境是,不采用Eureka 服务器,直接通过地址转发的方式,将认证链接从ZUUL 导向Authenticate Server

Client -> ZUUL -> Authentication Server 的过程中,在ZUUL -> Authentication Server 的时候丢失;

分析

OAuth2 Client 的身份信息认证是通过HTTP Basic 的方式进行的,也就是在Header 中生成一串由BASE64 编码的 clientid clientsecret 的字符串,类似如下,

1.  POST /uaa/oauth/token HTTP/1.1

2.  Host: localhost:9999

3.  AuthorizationBasicZGVtbzpkZW1v

4.  User-Agent: curl/7.51.0

5.  Accept: */*

6.  Content-Length: 51

7.  Content-Type: application/x-www-form-urlencoded

8.  grant_type=password&username=user&password=password

这一串关键的 Authorization: Basic ZGVtbzpkZW1v 信息,在通过ZUUL 转发给Authenticate Server 的过程中,会丢失;结果导致在通过ZUUL 转发认证请求后得到不能认证的错误返回 Response;如下所述;

1.  $ curl -XPOST -u demo:demo localhost:8000/uaa/ -d grant_type=password -d username=user -d password=password

2.  {"timestamp":1496055288220,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/uaa/oauth/token/"}

从抓包的结果中可以明显看到Base Authorization 信息丢失;这直接导致OAuth 认证失败,因为Client 认证信息丢失;

1.  POST /uaa/oauth/token/ HTTP/1.1

2.  user-agent: curl/7.51.0

3.  accept: */*

4.  content-type: application/x-www-form-urlencoded

5.  Content-Length: 51

6.  Host: localhost:9999

7.  Connection: Keep-Alive

8.    

9.  grant_type=password&username=user&password=password

解决办法

Google 上找到几篇文章

第一种说法,给server 节点增加属性 use-forward-headers: true,验证失败,参考 https://docs.stormpath.com/java/spring-cloud-zuul/quickstart.html

1.  server:

2.    port: 8000

3.    use-forward-headers: true

第二种说法,包括官文,说是可以通过设置ZUUL sensitiveHeaders 属性的三个值,可以解决,如下配置,

1.  zuul.sensitiveHeaders: Cookie,Set-Cookie,Authorization

验证还是失败;在转发的过程中,还是会丢弃Base Authorization 的值;这个让我很奇怪了,明明是为Header 设置了Authorization 信息,为什么不转发呢?

结果,在 https://github.com/Netflix/zuul/issues/218 找到了解决办法,原来在配置 sensitiveHeaders 的时候,不能添加Authorization,需要配置成如下的方式,

1.  zuul.sensitiveHeaders: Cookie,Set-Cookie

这样,Base Authorization 转发了,

1.  POST /uaa/oauth/token/ HTTP/1.1

2.  authorization: BasicZGVtbzpkZW1v

3.  user-agent: curl/7.51.0

4.  accept: */*

5.  content-type: application/x-www-form-urlencoded

6.  x-forwarded-host: localhost

7.  x-forwarded-proto: http

8.  x-forwarded-prefix: /uaa

9.  x-forwarded-port: 8000

10.  x-forwarded-for: 0:0:0:0:0:0:0:1

11.  Accept-Encoding: gzip

12.  Content-Length: 51

13.  Host: localhost:9999

14.  Connection: Keep-Alive

15.   

16.  grant_type=password&username=user&password=password

总结下

为什么 sensitiveHeaders 中将Authentication 移除,Base Authorization 的信息就可以发送了,参考官网的说明 https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#cookies-and-sensitive-headers 里面明确说明的是,sensitiveHeaders 是指http header 中的敏感信息,既然是敏感信息,默认情况下,ZUUL 是不转发的;而如果不显示配置sensitiveHeaders,那么默认情况下,配置的就是zuul.sensitiveHeaders: Cookie,Set-Cookie,Authorization也就是说,默认情况下,cookie 和相关的 Authorization 都不会进行转发,这就导致了我之前遇到的问题;所以呢,我们必须显示的进行配置,将 Authorization sensitiveHeaders 配置中去掉,保证Authorization 是可以被转发的;当然,如果将来需要通过Spring Session 统一所有服务器的Http Session,那么 sessionid 是必须通过cookie 进行传输的,所以,那个时候,ZUUL 还必须转发cookie 的相关信息,到时候,Cookie Set-Cookie 同样需要从 sensitiveHeaders 中移除;

丢失情况二

这种情况是,当客户端Client 拿到了access_token,在执行如下流程

Client -> ZUUL -> Order Service (a resource server) -> Stock Service (a resource server ) 的过程中丢失;

当前的环境是,采用Eureka 服务器,并且使用Feign 作为中间件让集群内部的微服务实现远程调用,通讯;

分析

具体情况是,Client 通过access token 经过ZUUL 访问到Order Service 的被保护资源,但是Order Service 需要通过Feign 去调用另一个微服务Stock Service 去获取Stock 中的product 信息,这是在这一步Order Service -> Stock Service 通过Feign 的调用过程中,access token 丢失了;所以导致,Stock Service 中的资源无法被获取到;错误信息如下,

此部分信息是从Order Service 控制台截取的

屏幕快照 2019-08-28 14.49.27.png

此部分信息是从Order Service 控制台中截取的,可以明显的看到,Order-Service 所有的相关认证信息在Stock Service 上全部丢失的了

屏幕快照 2019-08-28 14.50.42.png

原因

进一步分析得知,是因为Feign 默认下,不转发Authorization 的相关的信息,所以才导致了上述的问题;而且Google 了大量资料,也没看到Feign 是否提供了这样的开关;

一系列关于此问题的讨论,Feign 转发不包含Authorization 信息所引发的血案,

先来看看Github 上对它的讨论(直接把它当做一个Bug)

§ Add support for Feign on OAuth2 protected resources #56

§ Custom Feign RequestInterceptor for Spring OAuth2 #75 这篇文章提出了,可以通过扩展Feign RequestInterceptor 接口来自己添加Authorization 的信息;但是仅仅是思路;

§ Ability to configure feign.RequestInterceptor specific to a given feign client #288 这篇文章提到,我们应该在FeignClientFactoryBean 中为所有的Feign clients 添加Authorization,但也仅仅是想法;

是的,网上的确给出了解决方法,看似好简单,写一个Feign RequestInterceptor 接口实现就搞定,然后我google 了一些看似能够能解决的代码,后来我发现我错了,(水真的很深)

下面这段代码出自 https://gist.githubusercontent.com/joaoevangelista/dc90bcea15da5f554c7c/raw/c4d5801d536410af7d00f464ce24eb9967144ab0/RibbonRestBalanced.java

1.  @Bean

2.     @ConditionalOnMissingBean(RequestInterceptor.class)

3.     @ConditionalOnBean(OAuth2ClientContext.class)

4.     @ConditionalOnClass({RequestInterceptor.classFeign.class})

5.     feign.RequestInterceptorrequestInterceptor(OAuth2ClientContextcontext) {

6.         if(context == nullreturnnull;

7.         returnnewOAuth2FeignRequestInterceptor(context);

8.     }

9.    

10.     publicclassOAuth2FeignRequestInterceptorimplementsRequestInterceptor{

11.   

12.         privatefinalOAuth2ClientContextoAuth2ClientContext;

13.         privatefinalStringtokenTypeName;

14.         privatefinalStringheaderName;

15.         privatefinalLoggerlogger = LoggerFactory.getLogger(OAuth2FeignRequestInterceptor.class);

16.   

17.   

18.         publicOAuth2FeignRequestInterceptor(OAuth2ClientContextoAuth2ClientContext) {

19.             this(oAuth2ClientContext, "Bearer""Authorization");

20.         }

21.   

22.         publicOAuth2FeignRequestInterceptor(OAuth2ClientContextoAuth2ClientContext, StringtokenTypeName, StringheaderName) {

23.             this.oAuth2ClientContext = oAuth2ClientContext;

24.             this.tokenTypeName = tokenTypeName;

25.             this.headerName = headerName;

26.         }

27.   

28.         @Override

29.         publicvoidapply(RequestTemplatetemplate) {

30.             if(oAuth2ClientContext.getAccessTokenRequest().getExistingToken() == null) {

31.                 logger.warn("Cannot obtain existing token for request, if it is a non secured request, ignore.");

32.             } else{

33.                 logger.debug("Constructing Header {} for Token {}", headerName, tokenTypeName);

34.                 template.header(headerName, String.format("%s %s", tokenTypeName, oAuth2ClientContext.getAccessTokenRequest().getExistingToken().toString()));

35.             }

36.   

37.         }

38.     }

这段代码出自 https://github.com/spring-cloud/spring-cloud-netflix/issues/293

1.  publicclassFeignInterceptorimplementsRequestInterceptor{

2.      @Autowired

3.      privateOAuth2ClientContextcontext;

4.    

5.      @Override

6.      publicvoidapply(RequestTemplatetemplate) {

7.          if(context.getAccessToken() != null

8.                  && context.getAccessToken().getValue() != null

9.                  && OAuth2AccessToken.BEARER_TYPE.equalsIgnoreCase(context.getAccessToken().getTokenType()) ){

10.              template.header("Authorization"String.format("%s %s"OAuth2AccessToken.BEARER_TYPE, context.getAccessToken().getValue()));

11.          }

12.      }

13.  }

上面的种种代码我都试过了,但是,终究,还是遇到了和这位仁兄一样的错误,https://github.com/jmnarloch/feign-oauth2-spring-cloud-starter/issues/1 错误信息如下

1.  Nothread-bound request found: Areyou referring to request attributes outside of an actual web request, orprocessing a request outside of the originally receiving thread? Ifyou are actually operating within a web request andstill receive thismessage, your code isprobably running outside of DispatcherServlet/DispatcherPortletInthiscaseuseRequestContextListenerorRequestContextFilterto expose the current request.

那上面这个错误又是一个什么意思呢?

看下这篇文章,https://stackoverflow.com/questions/35265585/trying-to-use-oauth2-token-with-feign-client-and-hystrix 里面的SUGENAN 说到了问题的点,因为Hystrix 是在另外一个线程中执行的,与 Request 不在同一个线程中,所以,上述直接在当前的线程中去获取 OAuth2ClientContext 或者是 SecurityContext 是办不到的,也因此,会出现这个错误;

在来看看这篇文章,Make Spring Security Context Available Inside A Hystrix Command,里面详细的描述了Hystrix Spring Security Context 的问题,原因,以及如何解决;为了避免链接不可用,将这篇文章打印成了PDFOk,这篇文章已经解释到了 Hystrix 为什么不兼容Spring Security Context 的深层次的原因,根本原因是,Hystrix 是在自身的Thread Pool 中执行的,所以,与Request Context 本身就不在同一个线程中,所以,作者做了大量的工作,将Request Context 中的Attributes 赋值到 Hystrix 的线程中,这样才使得Hystrix 能够获取到Request Context 中的东西;该作者写了大量的接口,回调,处理生命周期的调用,但是,问题是,我们是需要在Spring Boot 环境中生效,所以,除了问题的原因分析意外,其方式和方法都不能直接拿来使用;当然,如果读者有时间和耐心深入的去读透 Hystrix Spring Security 的源码,那么解决这个问题自然不在话下;无奈,因为博主时间有限,需要交付一个可用的Spring Cloud 框架,所以,需要一个快速的,能够解决问题的方法;

后来,读到这篇文章,https://stackoverflow.com/questions/34719809/unreachable-security-context-using-feign-requestinterceptor 里面提到了使用 HystrixRequestVariableDefault,通过该类的注释可以知道,这个是Hystrix 为自身执行的线程提供的一个类似于ThreadLocal 的类,但是它与ThreadLocal 的不同之处在于,该Locals 的作用范围提升到了这个User Request Scope 级别,通过其注解可知,它是通过父类线程与子类线程共享的方式来共用该Locals 中的信息;所以,达到了User Request 线程与Hystrix 线程共用 attributes 的目的;由此,可以猜测,Hystrix 的线程是由当前Request 线程所创建的子线程;不过,使用的过程中,需要注意的是,HystrixRequestVariable 必须在每一个请求开始的时候进行初始化;也就是说,我们可以将 Request Context 中的有用信息存储到HystrixRequestVariableDefault中达到与Hystrix Context 共享信息;也就实现了Request Context 中的属性与Hystrix Context 之间共享的目的;

好了,解决思路有了,就差源码了,可惜,作者找遍了能找到的信息,还没有一个最终可直接拿来使用的代码,所以,也只能在不了解源码的基础上,硬着头皮上了,自己撸代码吧~ 请看下一小节;

解决办法其实经过上一小节的原因分析,解决思路已经很清晰了,

首先,写一个 Request Filter,将Spring Security Context 中关键信息传递给Hystrix Context 其次,写一个Feign.RequestInterceptor 的接口实现类,为ReqestTemplate 添加Authorization 属性值;下面贴出我花了整整一天才搞定的完整的代码,

1.  packageorg.shangyang.springcloud;

2.    

3.  importjava.io.IOException;

4.    

5.  importjavax.servlet.Filter;

6.  importjavax.servlet.FilterChain;

7.  importjavax.servlet.FilterConfig;

8.  importjavax.servlet.ServletException;

9.  importjavax.servlet.ServletRequest;

10.  importjavax.servlet.ServletResponse;

11.   

12.  importorg.slf4j.Logger;

13.  importorg.slf4j.LoggerFactory;

14.  importorg.springframework.boot.context.embedded.FilterRegistrationBean;

15.  importorg.springframework.context.annotation.Bean;

16.  importorg.springframework.context.annotation.Configuration;

17.  importorg.springframework.security.core.Authentication;

18.  importorg.springframework.security.core.context.SecurityContext;

19.  importorg.springframework.security.core.context.SecurityContextHolder;

20.  importorg.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;

21.   

22.  importcom.netflix.hystrix.strategy.concurrency.HystrixRequestContext;

23.  importcom.netflix.hystrix.strategy.concurrency.HystrixRequestVariableDefault;

24.   

25.  importfeign.RequestInterceptor;

26.  importfeign.RequestTemplate;

27.   

28.  /**

29.   * 

30.   * 现在遇到的问题是,从Order Service 向Stock Service 转发的时候,Credentials 丢失,原因是Hystrix 不转发;所以,需要补发,该类的逻辑就是取转发相关遗漏的Credentials;在OAuth 认证中是关键;

31.   * 

32.   * Godden Code...

33.   * 

34.   * @author shangyang

35.   *

36.   */

37.  @Configuration

38.  publicclassHystrixCredentialsContext{

39.   

40.    privatestaticfinalLoggerlogger = LoggerFactory.getLogger(HystrixCredentialsContext.class);

41.   

42.      privatestaticfinalHystrixRequestVariableDefault<Authentication> authentication = newHystrixRequestVariableDefault<>();

43.   

44.      publicstaticHystrixRequestVariableDefault<Authentication> getInstance() {

45.          returnauthentication;

46.      }   

47.   

48.    /**

49.     * 下面这段代码是关键,实现@See feign.RequestInterceptor,

50.     * 1. 添加认证所需的oauth token;

51.     * 2. 添加认证所需的user;

52.     * 

53.     * 目前仅实现了oauth toke,将来看情况是否实现user;

54.     * 

55.     * 特别要注意一点,因为HystrixRequestContext 和RequestContext 不在同一个线程中,所以,不能直接在RequestInterceptor 的实现方法中调用RequestContext 中的资源,因为HystrixRequestContext 是在自己

56.     * 的ThreadPool 中执行的;所以,这里搞得比较的麻烦... 不能在{@link RequestInterceptor#apply(RequestTemplate)} 中直接使用RequestContext / SecurityContextHolder,否则取到的资源全部是null;

57.     * 

58.     * @return

59.     */

60.    @Bean

61.    publicRequestInterceptorrequestTokenBearerInterceptor() {

62.   

63.            returnnewRequestInterceptor() {

64.   

65.                @Override

66.                publicvoidapply(RequestTemplaterequestTemplate) {

67.   

68.                  Authenticationauth = HystrixCredentialsContext.getInstance().get();

69.   

70.                  if( auth != null){

71.   

72.                    logger.debug("try to forward the authentication by Hystrix, the Authentication Object: "+ auth );

73.   

74.                    // 记得,因为Feign Interceptor 是通过自有的ThreadPool 中的线程执行的,与当前的Request 线程不是同一个线程,所以这里不能使用debug 模式进行调试;

75.                      requestTemplate.header("Authorization""bearer "+ ( (OAuth2AuthenticationDetails) auth.getDetails()).getTokenValue() );

76.   

77.                  }else{

78.   

79.                    logger.debug("attention, there is no Authentication Object needs to forward");

80.   

81.                  }

82.   

83.            }

84.        };

85.    }

86.   

87.      @Bean

88.      publicFilterRegistrationBeanhystrixFilter() {

89.   

90.          FilterRegistrationBeanr = newFilterRegistrationBean();

91.   

92.          r.setFilter(newFilter(){

93.   

94.        @Override

95.        publicvoidinit(FilterConfigfilterConfig) throwsServletException{

96.   

97.        }

98.   

99.        @Override

100.      publicvoiddoFilter(ServletRequestrequest, ServletResponseresponse, FilterChainchain)

101.          throwsIOExceptionServletException{

102. 

103.        // as the comments described by HystrixRequestContext, for using HystrixRequestVariable should first initialize the context at the beginning of each request

104.        // so made it here... 

105.        HystrixRequestContext.initializeContext();

106. 

107.        SecurityContextsecurityContext = SecurityContextHolder.getContext();

108. 

109.        if( securityContext != null){

110. 

111.          Authenticationauth = (Authentication) securityContext.getAuthentication();     

112. 

113.            HystrixCredentialsContext.getInstance().set(auth);

114. 

115.            logger.debug("try to register the authentication into Hystrix Context, the Authentication Object: "+ auth );

116. 

117.        }

118. 

119.          chain.doFilter(request, response);

120. 

121.      }

122. 

123.      @Override

124.      publicvoiddestroy() {

125. 

126.      }

127. 

128.        });

129. 

130.        // In case you want the filter to apply to specific URL patterns only

131.        r.addUrlPatterns("/*");

132. 

133.        returnr;

134.    } 

135. 

136.}

幽灵般的401: Bad credentials

分析

当前的环境是,不采用Eureka 服务器,直接通过地址转发的方式,将认证链接从ZUUL 导向Authenticate Server

现象分析

原本以为,解决了BASE Authentication 的问题以后,后面就可以迎刃而解了,万万没有想到的是,遇到了下面这个错误返回

1.  {"timestamp":1496061029672,"status":401,"error":"Unauthorized","message":"Bad credentials","path":"/uaa/oauth/token/"}

ZUUL 的配置如下,转发规则很简单,凡是/uaa/** 都会被重定向到http://localhost:9999/uaa/oauth/token

1.  zuul:

2.    ignoredServices: '*'

3.    routes:

4.      auth:

5.        path: /uaa/**

6.        url: http://localhost:9999/uaa/oauth/token

7.        stripPrefix: true

一切看似合情合理,但是,奇怪的问题却接踵而至;

当直接访问 Authenticate Server 的时候,

1.  $ curl demo:demo@localhost:9999/uaa/oauth/token -d grant_type=password -d username=user -d password=password

得到正常结果:

1.  {"access_token":"09abb86d-c307-4683-b00c-0a83860cadd7","token_type":"bearer","refresh_token":"048d31fa-fd55-4be0-9236-6c179d0a3b65","expires_in":42416,"scope":"read write"}

但是一旦通过ZUUL 进行转发( 既是通过 localhost:8000/uaa/ 的访问,将会转发到 localhost:9999/uaa/oauth/token 地址上)

1.  $ curl demo:demo@localhost:8000/uaa/ -d grant_type=password -d username=user -d password=password

就得到验证失败的结果,

1.  {"timestamp":1496060499677,"status":401,"error":"Unauthorized","message":"Bad credentials","path":"/uaa/oauth/token/"}

从返回结果上也可以清晰的看到,获取oauth token 的路径为/uaa/oauth/token/ 也是正确的;这不和使用使用 localhost:9999/uaa/oauth/token 一模一样吗?

代码分析

后续无奈之下,调试代码,

从日志中可以发现,验证client user 的身份信息都是通过如下方法进行的

AbstractUserDetailsAuthenticationProvider.java

1.  publicAuthenticationauthenticate(Authenticationauthentication)

2.        throwsAuthenticationException{

3.           // Determine username

4.     Stringusername = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"

5.           : authentication.getName();

6.    

7.     booleancacheWasUsed = true;

8.     UserDetailsuser = this.userCache.getUserFromCache(username);

9.    

10.     if(user == null) {

11.        cacheWasUsed = false;

12.        ...

13.        user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);

14.        ...         

15.   

16.   

17.     }

18.   

19.     ....

20.   

21.     returncreateSuccessAuthentication(principalToReturn, authentication, user);

22.  }

将其它不相干的代码暂时删除;主要是通过上述代码第13 行,获取用户信息;从该行代码进入

DaoAuthenticationProvider.java

屏幕快照 2019-08-28 14.48.09.png

好了,抛出错误的代码行找到了,那有什么性质呢?

若直接访问,也就是正常的情况下:

验证client 的时候,this.getUserDetailsService() 返回ClientDetailsUserDetailsService 验证user 的时候,this.getUserDetailsService() 返回InMemoryUserDetailsManager

ZUUL 转发,也就是报错的情况下:

验证client 的时候,this.getUserDetailsService() 返回InMemoryUserDetailsManager

这就是问题的原因所在了,正常情况下,应该使用的是 ClientDetailsUserDetailsService,但是这里却使用了 InMemoryUserDetailsManager 所以导致错误的产生;

难道是代码的错误,如果是代码的错误,那这个就是Spring OAuth 的一个Bug,我可没有精力去修改该这个玩意儿呀…. 找到了代码的出处,但迫于没有精力去修改该,只好作罢;下面只好从一些细节现象上入手了,看看两种访问的方式,在哪些细节上不同;

分析日志分析两段Authenticate Server 上的logs,看看一些细微之处到底有什么异同,

屏幕快照 2019-08-28 14.42.32.png

屏幕快照 2019-08-28 14.43.16.png

屏幕快照 2019-08-28 14.43.36.png

可见,两端logs 是何其的相似;ohwait,我发现了一个非常奇怪的地方,通过直接访问的方式,映射的地址是/oauth/token,但是通过ZUUL 转发的方式,映射的地址却是 /oauth/token/;这是不是说明了什么?经过作者的后续验证,的确,这就是问题发生的地方,也就是为什么验证没有通过的根本原因,就是多了一个/,而酿成了我一整个下午不知所措的血案!

解决

既然知道了,是因为映射的时候,多了一个/引起的,那么该如何解决呢?直觉告诉我,一定是一个非常琐碎,且不起眼的地方引发了这场血案~~,那到底是哪里呢?找到了,

如果使用下面这种方式,

1.  $ curl demo:demo@localhost:8000/uaa/ -d grant_type=password -d username=user -d password=password

验证不通过,转发地址会被映射到 /uaa/oauth/token/

如果使用下面这种方式,

1.  $ curl demo:demo@localhost:8000/uaa -d grant_type=password -d username=user -d password=password

便得到了梦寐以求的access token,转发地址将会被映射到/uaa/oauth/token 上,正式因为后缀少了这个该死的/所以一切通过;

1.  {"access_token":"a97141e2-6879-4231-b748-024bc3b9d5b3","token_type":"bearer","refresh_token":"e9d4d03f-c746-48b7-98a5-affe7b9cc195","expires_in":43199,"scope":"read write"}

猜测,难道,在ZUUL 在替换匹配/uaa/ 的时候,只是将/uaa/ 的前面部分/uaa 进行了替换,然后使用url http://localhost:9999/uaa/oauth/token 来进行填充,所以最后多了一个/? 若不是这样,真想不出其它的理由了真不知道这段逻辑是哪位大神写的,该拉出去打板子了….

代码下载

https://github.com/comedsh/springcloud-demo


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。