OAuth2.0 與動態客户端註冊(使用 Spring Security OAuth 遺留棧)

Spring Security
Remote
0
10:17 PM · Nov 29 ,2025

1. 簡介在本教程中,我們將準備一個動態客户端註冊,使用OAuth2.0。OAuth2.0是一個授權框架,它允許獲取用户在HTTP服務上的有限訪問權限。OAuth2.0客户端是希望訪問用户賬户的應用程序。該客户端可以是外部Web應用程序、用户代理或原生客户端。

為了實現動態客户端註冊,我們將存儲憑據到數據庫,而不是硬編碼配置。我們即將擴展的應用程序最初描述在Spring REST API + OAuth2教程中。

注意:本文使用Spring OAuth 遺留項目

2. Maven 依賴項

我們首先將設置以下依賴項:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>

請注意,我們使用 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的客户端,該客户端可以訪問“foo”數據。

首先,我們將嘗試從認證服務器獲取訪問令牌,使用已定義的客户端:

@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 框架動態註冊無限數量的客户端。

請注意,為了測試,您需要將客户端添加到數據庫中,並且 .inMemory() 配置將不再有效。如果您想使用舊的 .inMemory() 配置,則有一個包含配置的第二個文件,其中包含硬編碼的客户端。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.