Spring security Faign和Spring Security 5-客户端凭据

Spring security Faign和Spring Security 5-客户端凭据,spring-security,oauth,spring-cloud-feign,Spring Security,Oauth,Spring Cloud Feign,我试图调用某个后端系统,该系统由来自外部客户端应用程序的客户端\ U凭据授予类型保护 可以使用以下curl结构从后端系统检索访问令牌(仅作为示例): 然后,应在对后端系统的后续业务调用的头中设置此accessToken 所以现在我的问题是,如何使用Feign和springbootsecurity5实现这一点。 经过一些研究,我得出了这个解决方案(不起作用): 在application.yml中定义我的客户端: 创建OAuth2AuthorizedClientManager Bean,以便能够授权

我试图调用某个后端系统,该系统由来自外部客户端应用程序的客户端\ U凭据授予类型保护

可以使用以下curl结构从后端系统检索访问令牌(仅作为示例):

然后,应在对后端系统的后续业务调用的头中设置此accessToken

所以现在我的问题是,如何使用Feign和springbootsecurity5实现这一点。 经过一些研究,我得出了这个解决方案(不起作用):

  • 在application.yml中定义我的客户端:
  • 创建OAuth2AuthorizedClientManager Bean,以便能够授权(或重新授权)OAuth 2.0客户端:
  • @Bean
    公共OAuth2AuthorizedClientManager授权dClientManager(
    ClientRegistrationRepository ClientRegistrationRepository,
    OAuth2AuthorizedClientPosition授权客户(授权客户){
    DefaultOAuth2AuthorizedClientManager授权dClientManager=
    新的DefaultOAuth2AuthorizedClientManager(
    clientRegistrationRepository,授权客户库);
    返回授权客户管理器;
    }
    
  • 创建使用OAuth2AuthorizedClientManager的外部请求侦听器:
  • 公共类OAuthRequestInterceptor实现RequestInterceptor{
    专用OAuth2AuthorizedClient管理器;
    公共OAuthRequestInterceptor(OAuth2AuthorizedClient管理器){
    this.manager=经理;
    }
    @凌驾
    公共作废应用(RequestTemplate RequestTemplate){
    OAuth2AuthorizedClient=this.manager.authorize(OAuth2AuthorizeRequest.withClientRegistrationId(“后端”).principal(createPrincipal()).build());
    字符串accessToken=client.getAccessToken().getTokenValue();
    requestTemplate.header(HttpHeaders.AUTHORIZATION,“Bearer”+accessToken);
    }
    私有身份验证createPrincipal(){
    返回新的身份验证(){
    @凌驾
    
    公共收藏我尝试了you's方法。不幸的是没有成功。但这一个对我有效:。看起来我现在使用了很多折旧,但至少它确实有效。

    我对Faign和OAuth2很有经验,我花了好几个小时才找到如何做到这一点。 首先,假设我的应用程序基于最新的Spring库,因此我使用以下依赖项(Spring cloud starter openfeign的托管版本为3.0.0)

    最后是我的配置bean

    package es.spanishkangaroo.ttanalyzer.config;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
    import org.springframework.security.oauth2.client.OAuth2RestTemplate;
    import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
    
    import feign.RequestInterceptor;
    
    @Configuration
    public class FeignClientConfiguration {
        
        @Bean
        @ConfigurationProperties(prefix = "security.oauth2.client")
        public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
            return new ClientCredentialsResourceDetails();
        }
    
        @Bean
        public RequestInterceptor oauth2FeignRequestInterceptor(){
            return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), clientCredentialsResourceDetails());
        }
    
        @Bean
        public OAuth2RestTemplate clientCredentialsRestTemplate() {
            return new OAuth2RestTemplate(clientCredentialsResourceDetails());
        }
    
    }
    
    因此,外国客户机就像

    package es.spanishkangaroo.ttanalyzer.api;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import es.clovelly.ttanalyzer.model.Trends;
    
    @FeignClient(name = "twitterClient", url = "https://api.twitter.com/1.1/")
    public interface TwitterClient {
     
        @GetMapping("/trends/place.json")
        Trends[] getTrendsById(@RequestParam Long id);
        
    }
    
    正如您可能已经注意到的,代码在客户端调用之前自动获取令牌(承载令牌)

    @Bean
    public OAuth2ClientContext oAuth2ClientContext() {
        DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext();
        context.setAccessToken(bearerToken);
        return context;
    }
    

    要使它与SpringSecurity5一起工作,您需要

    • 工作的Spring安全配置
    • 假拦截器
    • 使用该拦截器的虚假配置
  • 工作弹簧安全配置
  • 在这里,我们将为您的oauth2
    客户端凭据注册一个通用的
    内部api
    客户端。在这里,您可以指定
    客户端机密
    客户端机密
    作用域
    授权类型
    。 所有基本的Spring Security 5功能。这还包括设置一个提供程序(这里我使用一个名为“yourprovider”的自定义OpenID连接提供程序)

    接下来,您需要您的伪配置。这将使用一个
    OAuth2FeignRequestInterceptor

    public class ServiceToServiceFeignConfiguration extends AbstractFeignConfiguration {
    
        @Bean
        public OAuth2FeignRequestInterceptor requestInterceptor() {
            return new OAuth2FeignRequestInterceptor(
                    OAuth2AuthorizeRequest.withClientRegistrationId("internal-api")
                            .principal(new AnonymousAuthenticationToken("feignClient", "feignClient", createAuthorityList("ROLE_ANONYMOUS")))
                            .build());
        }
    }
    
    还有一个请求拦截器,如下所示:

    OAuth2AuthorizedClient Manager是一个可以在配置中配置的bean

    public OAuth2AuthorizedClientManager authorizedClientManager(final ClientRegistrationRepository clientRegistrationRepository, final OAuth2AuthorizedClientService authorizedClientService) {
        return new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
    }
    
    OAuth2AuthorizerRequest
    由上面的外部配置提供。
    OAuth2AuthorizedClient Manager
    可以授权
    oAuth2AuthorizeRequest
    ,获取访问令牌,并将其作为
    授权头提供给基础服务

    public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    
        @Inject
        private OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager;
    
        private OAuth2AuthorizeRequest oAuth2AuthorizeRequest;
    
        OAuth2FeignRequestInterceptor(OAuth2AuthorizeRequest oAuth2AuthorizeRequest) {
            this.oAuth2AuthorizeRequest = oAuth2AuthorizeRequest;
        }
    
        @Override
        public void apply(RequestTemplate template) {
            template.header(AUTHORIZATION,getAuthorizationToken());
        }
    
        private String getAuthorizationToken() {
            final OAuth2AccessToken accessToken = oAuth2AuthorizedClientManager.authorize(oAuth2AuthorizeRequest).getAccessToken();
            return String.format("%s %s", accessToken.getTokenType().getValue(), accessToken.getTokenValue());
        }
    
    }
    

    在切换到Spring Security 5之前,我们使用了Spring Security OAuth2包中的
    ClientCredentialAccessTokenProvider
    。这很有效。感谢您的回答。这当然有效,但它是Spring Security OAuth2的一部分,而Spring Security 5.x已经被弃用了。我真的想让它在Spring Security中发挥作用5.x.
    ClientCredentialsResourceDetails
    不是被弃用了吗?读了这篇文章,我倾向于这样想:我们怎么能检查呢?谢谢你的详细回答。但是,我没有得到一些东西:什么是class
    AbstractFeignConfiguration
    +method
    with clientRegistrationId()
    [它是
    OAuth2AuthorizerRequest.withClientRegistrationId()
    ?];在
    OAuth2FeignRequestInterceptor
    中也是一样的,
    authorizationHeader()
    来自哪里?谢谢。更改代码后,它确实是
    OAuth2AuthorizerRequest.withClientRegistrationId
    (使用静态导入)这里,
    authorizationHeader
    是一个UTILITY方法。为了clarityOK很好,现在删除了它……并且从
    AbstractFeignConfiguration
    继承的没有什么特别的东西?或者它是注释的吗?在我们的例子中,AbstractFeignConfiguration只包含一些解码器/编码器。这里不需要
    package es.spanishkangaroo.ttanalyzer.api;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import es.clovelly.ttanalyzer.model.Trends;
    
    @FeignClient(name = "twitterClient", url = "https://api.twitter.com/1.1/")
    public interface TwitterClient {
     
        @GetMapping("/trends/place.json")
        Trends[] getTrendsById(@RequestParam Long id);
        
    }
    
    @Bean
    public OAuth2ClientContext oAuth2ClientContext() {
        DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext();
        context.setAccessToken(bearerToken);
        return context;
    }
    
    spring:
      security:
        oauth2:
          client:
            registration:
              internal-api:
                provider: yourprovider
                client-id: x
                client-secret: y
                scope:
                  - ROLE_ADMIN
                authorization-grant-type: client_credentials
            provider:
              yourprovider:
                issuer-uri: yourprovider.issuer-uri
          resourceserver:
            jwt:
              issuer-uri: yourprovider.issuer-uri
    
    public class ServiceToServiceFeignConfiguration extends AbstractFeignConfiguration {
    
        @Bean
        public OAuth2FeignRequestInterceptor requestInterceptor() {
            return new OAuth2FeignRequestInterceptor(
                    OAuth2AuthorizeRequest.withClientRegistrationId("internal-api")
                            .principal(new AnonymousAuthenticationToken("feignClient", "feignClient", createAuthorityList("ROLE_ANONYMOUS")))
                            .build());
        }
    }
    
    public OAuth2AuthorizedClientManager authorizedClientManager(final ClientRegistrationRepository clientRegistrationRepository, final OAuth2AuthorizedClientService authorizedClientService) {
        return new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
    }
    
    public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    
        @Inject
        private OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager;
    
        private OAuth2AuthorizeRequest oAuth2AuthorizeRequest;
    
        OAuth2FeignRequestInterceptor(OAuth2AuthorizeRequest oAuth2AuthorizeRequest) {
            this.oAuth2AuthorizeRequest = oAuth2AuthorizeRequest;
        }
    
        @Override
        public void apply(RequestTemplate template) {
            template.header(AUTHORIZATION,getAuthorizationToken());
        }
    
        private String getAuthorizationToken() {
            final OAuth2AccessToken accessToken = oAuth2AuthorizedClientManager.authorize(oAuth2AuthorizeRequest).getAccessToken();
            return String.format("%s %s", accessToken.getTokenType().getValue(), accessToken.getTokenValue());
        }
    
    }