如何配置必须使用“密码”授权类型从授权服务器请求令牌的客户端 Java 应用程序?

Posted

技术标签:

【中文标题】如何配置必须使用“密码”授权类型从授权服务器请求令牌的客户端 Java 应用程序?【英文标题】:How do I configure a client Java application which must request a token from an authorization server using a 'password' grant type? 【发布时间】:2021-05-23 19:49:35 【问题描述】:

我需要使用 Maven 实现客户端 Java 应用程序,该应用程序从资源服务器发出请求。为了授权请求,必须在请求的标头中提供不记名令牌。要获得令牌,必须先完成另一个请求。服务器为我提供了发出令牌请求所需的所有参数:client_id、client_secret、用户名、密码等。令牌请求的 grant_type 是“密码”。

服务器基于 GraphQL,因此我使用以下 Maven 插件来生成执行 GraphQL 查询的 Java 类: https://github.com/graphql-java-generator/graphql-maven-plugin-project

他们提供了有关如何“访问 OAuth2 GraphQL 服务器”的教程: https://graphql-maven-plugin-project.graphql-java-generator.com/client_oauth2.html

正如他们在教程中提到的,为了连接到 OAuth 服务器,我必须使用 Spring。本教程解释了如何访问使用“client_credentials”授权类型的服务器。我不熟悉spring和spring-security,所以我没有设法将我的Spring客户端配置为'password'授权类型(我所做的只是在application.properties中将grant_type从'client_credentials'更改为'password') .需要在哪里配置用户名和密码?我需要做哪些额外的配置?没有春天有没有办法做到这一点?我想要获得的是从授权服务器请求令牌并(在后台)将令牌添加到资源请求的方式。

以下是我遵循教程的一段代码:

@SpringBootApplication(scanBasePackageClasses =  MinimalSpringApp.class, GraphQLConfiguration.class)
public class MinimalSpringApp implements CommandLineRunner

   /**
    * The executor, that allows to execute GraphQL queries. The class name is the one defined in the GraphQL schema.
    */
   @Autowired
   QueryExecutor queryExecutor;


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


   /**
    * This method is started by Spring, once the Spring context has been loaded. This is run, as this class implements
    * @link CommandLineRunner
    */
   @Override
   public void run( String... args ) throws Exception
   
// Create query
      GraphQLRequest softwareTechnologyRequest = queryExecutor.getSoftwareTechnologyGraphQLRequest( " id name " );
      SoftwareTechnologyFilter filter = new SoftwareTechnologyFilter();

// Execute query
      List<SoftwareTechnology> technologies =
            queryExecutor.softwareTechnology( softwareTechnologyRequest, filter, 0, "", 1000, "", 0,
                  Collections.emptyList() );

      System.out.println( technologies );
   

   @Bean
   ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction(
         ReactiveClientRegistrationRepository clientRegistrations) 
      ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
            clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
      oauth.setDefaultClientRegistrationId("provider_test");
      return oauth;
   

当我运行应用程序时,它会抛出由“401 Unauthorized from POST”引起的 IllegalStateException。

【问题讨论】:

【参考方案1】:

已解决

我需要创建自己的客户端管理器 bean,其中设置了用户名和密码。 Spring 官方文档的“资源所有者密码凭据”部分有一个示例: https://docs.spring.io/spring-security/site/docs/5.2.0.RELEASE/reference/htmlsingle/#oauth2client

   @Bean
   public ReactiveOAuth2AuthorizedClientManager authorizedClientManager( ReactiveClientRegistrationRepository clientRegistrationRepository,
         ReactiveOAuth2AuthorizedClientService authorizedClientService)
   
      ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
            ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password().refreshToken().build();
      AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
            new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService );
      authorizedClientManager.setAuthorizedClientProvider( authorizedClientProvider );
      authorizedClientManager.setContextAttributesMapper( contextAttributesMapper() );
      return authorizedClientManager;
   

   private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper()
   
      return authorizeRequest -> 
         Map<String, Object> contextAttributes = new HashMap<>();
         contextAttributes.put( OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, "username" );
         contextAttributes.put( OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, "password" );
         return Mono.just(contextAttributes);
      ;
   

然后需要调整 graphql java generator plugin tutorial 中的 bean 才能使用提供的客户端管理器:

   @Bean
   ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction(
         ReactiveOAuth2AuthorizedClientManager authorizedClientManager) 
      ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
            authorizedClientManager);
      oauth.setDefaultClientRegistrationId("provider_test");
      return oauth;
   

【讨论】:

以上是关于如何配置必须使用“密码”授权类型从授权服务器请求令牌的客户端 Java 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 auth0 自定义 api 发出用户名密码请求,出现错误“不支持的授权类型:密码”错误

Postman-OAuth 2.0授权

授权码授权流程但没有浏览器

OAuth2.0协议流程

如何管理从本地移动应用程序调用 Play2!-Scala REST 服务发送的用户请求的身份验证/授权

如何在角度 http 请求中实现 client_credentials 授权类型?