1. 概述

在这个教程中,我们将准备一个动态的OAuth2.0客户端注册。OAuth2.0是一个授权框架,它允许在HTTP服务上获取对用户账户的有限访问权限。OAuth2.0客户端是希望访问用户账户的应用程序,它可以是外部Web应用、用户代理或纯粹的原生客户端。

为了实现动态客户端注册,我们将把凭据存储在数据库中,而不是硬编码在配置中。我们将扩展的应用最初在Spring REST API + OAuth2教程中介绍。

注意:本文使用的是Spring OAuth遗产项目

2. Maven依赖

首先,我们需要设置以下依赖:

请注意,我们使用了spring-jdbc,因为我们将使用数据库来存储新注册的带有密码的客户端。

3. OAuth2.0服务器配置

首先,我们需要配置我们的OAuth2.0授权服务器。主要配置位于以下类中:

@Configuration
@PropertySource({ "classpath:persistence.properties" })
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig
  extends AuthorizationServerConfigurerAdapter {
    
    // config
}

我们需要配置的主要内容包括:ClientDetailsServiceConfigurer::

@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
    clients.jdbc(dataSource())
    
    // ...        
}

这将确保我们使用持久化方式获取客户端信息。

当然,我们也需要设置标准的数据源:

@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();

    dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
    dataSource.setUrl(env.getProperty("jdbc.url"));
    dataSource.setUsername(env.getProperty("jdbc.user"));
    dataSource.setPassword(env.getProperty("jdbc.pass"));
    return dataSource;
}

这样,我们的应用程序将使用数据库作为注册客户端的来源,而不是通常的内存中硬编码客户端。

4. 数据库模式

现在让我们定义存储OAuth客户端的SQL结构:

create table oauth_client_details (
    client_id VARCHAR(256) PRIMARY KEY,
    resource_ids VARCHAR(256),
    client_secret VARCHAR(256),
    scope VARCHAR(256),
    authorized_grant_types VARCHAR(256),
    web_server_redirect_uri VARCHAR(256),
    authorities VARCHAR(256),
    access_token_validity INTEGER,
    refresh_token_validity INTEGER,
    additional_information VARCHAR(4096),
    autoapprove VARCHAR(256)
);

oauth_client_details表中,我们应该关注的关键字段有:

  • client_id - 存储新注册客户端的ID
  • client_secret - 存储客户端的密码
  • access_token_validity - 表示客户端是否仍然有效
  • authorities - 指定与特定客户端相关的权限
  • scope - 允许的操作,例如在Facebook上发布状态等
  • authorized_grant_types - 提供用户如何登录特定客户端的信息(在本例中,是使用密码的表单登录)

请注意,每个客户端与用户之间是一对多的关系,这意味着多个用户可以使用同一个客户端

5. 存储一些客户端

有了SQL模式,我们终于可以在系统中创建数据,实质上定义一个客户端。

我们将使用名为data.sql的脚本,Spring Boot默认会运行它来初始化数据库:

INSERT INTO oauth_client_details
    (client_id, client_secret, scope, authorized_grant_types,
    web_server_redirect_uri, authorities, access_token_validity,
    refresh_token_validity, additional_information, autoapprove)
VALUES
    ("fooClientIdPassword", "secret", "foo,read,write,
    "password,authorization_code,refresh_token", null, null, 36000, 36000, null, true);

oauth_client_details表中关键字段的描述已在前一节提供。

6. 测试

为了测试动态客户端注册,我们需要运行spring-security-oauth-serverspring-security-oauth-resource项目,分别在8081和8082端口上。

现在我们可以编写一些实际测试。

假设我们注册了一个名为fooClientIdPassword的客户端,它有权读取foos。

首先,我们将尝试使用已定义的客户端从Auth服务器获取访问令牌:

@Test
public void givenDBUser_whenRevokeToken_thenAuthorized() {
    String accessToken = obtainAccessToken("fooClientIdPassword", "john", "123");
    
    assertNotNull(accessToken);
}

获取访问令牌的逻辑如下:

private String obtainAccessToken(String clientId, String username, String password) {
    Map<String, String> params = new HashMap<String, String>();
    params.put("grant_type", "password");
    params.put("client_id", clientId);
    params.put("username", username);
    params.put("password", password);
    Response response = RestAssured.given().auth().preemptive()
      .basic(clientId, "secret").and().with().params(params).when()
      .post("http://localhost:8081/spring-security-oauth-server/oauth/token");
    return response.jsonPath().getString("access_token");
}

7. 总结

在这篇教程中,我们学习了如何使用OAuth2.0框架动态注册无限数量的客户端。

这个教程的完整实现可以在GitHub上找到 - 这是一个基于Maven的项目,导入并运行起来应该很容易。

请注意,为了测试,你需要将客户端添加到数据库中,并且.inMemory()配置将不再有效。如果你想使用旧的.inMemory()配置,还有一个包含硬编码客户端的配置文件可供选择。