2

如何使用 Spring Boot 配置多个 OAuth2RestTemplates(通过 OAuth2ProtectedResourceDetails),以便我可以访问多个 API。正如我们所见,它们都配置在同一个租户中,除了范围之外,所有配置都相同。

我相信我确实读过你不能有多个范围,因为每个 JWT 令牌都是资源特定的,但我看不到有多个 RestTemplates 的例子。

谢谢!

security:
  oauth2:
    client:
      client-id: x
      client-secret: y
      user-authorization-uri: z
      access-token-uri: a
      scope: B
      grant-type: client_credentials

    client2:
      client-id: x
      client-secret: y
      user-authorization-uri: z
      access-token-uri: a
      scope: Q
      grant-type: client_credentials
    @Bean(name="ngsWbRestTemplate")
    public OAuth2RestTemplate buildNgsWbRestTemplate(
            OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails
    ){
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oAuth2ProtectedResourceDetails);
        restTemplate.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
        restTemplate.getAccessToken().getValue();
        return restTemplate;
    }

    @Bean(name="OdpRestTemplate")
    public OAuth2RestTemplate buildOdpRestTemplate(
            OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails,
            @Value("#{propertyService.getValue('ODP_BASE_URI')}") String odpBaseUri
    ){
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oAuth2ProtectedResourceDetails);
        restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(odpBaseUri));
        restTemplate.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
        // test access token retrieval
        restTemplate.getAccessToken().getValue();
        return restTemplate;
    }
4

1 回答 1

2

我最近做了一个客户端,它集成了多个使用 OAUth2 协议保护其 API 的提供商的信息。我使用了这个依赖:

<dependency>
  <groupId>org.springframework.security.oauth.boot</groupId>
  <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  <version>2.0.0.RELEASE</version>
</dependency>

在 configuration.yml 文件中,您必须设置客户端所需的所有属性才能从授权服务器获取令牌。

example:
 oauth2:
   okta:
     clientId: XXX-XXX-XXX
     clientSecret: XXX-XXX-XXX
     grantType: client_credentials
     accessTokenUri: https://dev-xxxx.okta.com/oauth2/default/v1/token
     scope: custom_service #Created in okta
   keycloak:
     clientId: app-resource-server
     clientSecret: 60bc378c-5c95-4dee-b525-e71993d1596d
     grantType: password  #For password grant_type
     username: user
     password: user
     accessTokenUri: http://localhost:9001/auth/realms/development/protocol/openid-connect/token
     scope: openid profile email

在主类中,您需要为要发送请求的每个资源服务器创建一个不同的 bean,并在 Authorization 标头中使用相应的 access_token。

@SpringBootApplication
public class DemoApplication {


    /* Inject your client properties into ClientCredentialsResourceDetails object
       if you need to get tokens using client_credentials grant type*/
    @Bean
    @ConfigurationProperties("example.oauth2.okta")
    protected ClientCredentialsResourceDetails oktaOAuth2Details() {
        return new ClientCredentialsResourceDetails();
    }

    /*Inject your client properties into ResourceOwnerPasswordResourceDetails object
      if you need to pass an username and a password*/
    @Bean
    @ConfigurationProperties("example.oauth2.keycloak")
    protected ResourceOwnerPasswordResourceDetails keycloakOAuth2Details() {
        return new ResourceOwnerPasswordResourceDetails();
    }

    //Create the OAuth2RestTemplate bean with the corresponding clientOAuth2Details
    @Bean("oktaOAuth2RestTemplate")
    protected OAuth2RestTemplate oktaOAuth2RestTemplate() {
        return new OAuth2RestTemplate(oktaOAuth2Details());
    }

    @Bean("keycloakOAuth2RestTemplate")
    protected OAuth2RestTemplate keycloakOAuth2RestTemplate() {
        return new OAuth2RestTemplate(keycloakOAuth2Details());
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

然后,您可以使用 @Qualifier 注释自动连接 OAuth2RestTemplate,选择所需的实现,如下所示

@Component
public class AsyncService {

    //@Value("#{ @environment['example.baseUrl'] }")
    private static final String API_URL1 = "http://localhost:8081";
    private static final String API_URL2 = "http://localhost:8082";

    private final Logger log = LoggerFactory.getLogger(AsyncService.class);

    @Autowired
    @Qualifier("oktaOAuth2RestTemplate")
    OAuth2RestTemplate oktaOAuth2RestTemplate;

    @Autowired
    @Qualifier("keycloakOAuth2RestTemplate")
    OAuth2RestTemplate keycloakOAuth2RestTemplate;

    public void oktaRequest() {
        log.info("Okta access_token: {}", oktaOAuth2RestTemplate.getAccessToken());
        log.info(oktaOAuth2RestTemplate.getForObject(API_URL1 + "/message", String.class));
    }

    public void keylcloakRequest() {
        log.info("Keycloak access_token: {}", keycloakOAuth2RestTemplate.getAccessToken());
        log.info(keycloakOAuth2RestTemplate.getForObject(API_URL2 + "/message", String.class));
    }

    //@Async
    public void requestMessage() {
        oktaRequest();
        keycloakRequest();
    }
}

Spring 会在令牌过期时自动刷新令牌,这太酷了。

于 2020-09-01T20:28:07.840 回答