OAuth2.0 and Dynamic Client Registration (using the Spring Security OAuth legacy stack) – OAuth2.0和动态客户端注册(使用Spring Security OAuth遗留栈)

最后修改: 2016年 12月 21日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we are going to prepare a dynamic client registration with the OAuth2.0. The OAuth2.0 is an authorization framework that enables obtaining limited access to user accounts on an HTTP service. The OAuth2.0 client is the application that wants to access the user’s account. This client can be an external web application, an user agent or just a native client.

在本教程中,我们将准备一个使用OAuth2.0的动态客户端注册。OAuth2.0是一个授权框架,可以在HTTP服务上获得对用户账户的有限访问。OAuth2.0客户端是指想要访问用户账户的应用程序。这个客户端可以是一个外部的网络应用程序、一个用户代理或只是一个本地客户端。

In order to achieve dynamic client registration, we’re going to store the credentials in database, instead of hardcoded configuration. The application we’re going to extend was initially described in Spring REST API + OAuth2 tutorial.

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

Note: this article is using the Spring OAuth legacy project.

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

2. Maven Dependencies

2.Maven的依赖性

We’ll first set up the following set of dependencies:

我们首先要建立以下一组依赖关系。

<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>

Note that we’re using spring-jdbc because we’re going to use a DB to store the newly registered users with passwords.

注意,我们使用spring-jdbc是因为我们要使用一个DB来存储新注册的用户和密码。

3. OAuth2.0 Server Configuration

3.OAuth2.0服务器配置

First, we need to configure our OAuth2.0 authorization server. The main configuration is inside the following class:

首先,我们需要配置我们的OAuth2.0授权服务器。主要的配置在下面的类里面。

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

There are a few major things we need to configure; let’s start with ClientDetailsServiceConfigurer:

我们需要配置几个主要的东西;让我们从ClientDetailsServiceConfigurer:开始。

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

This will make sure we’re using persistence to get the client information from.

这将确保我们使用持久性来获取客户信息。

Let’s of course set up this standard data source:

当然,让我们来设置这个标准的数据源。

@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;
}

And so, now, our application will use the database as a source of registered clients, instead of the typical hard-coded in memory clients.

因此,现在,我们的应用程序将使用数据库作为注册客户的来源,而不是典型的在内存中硬编码的客户。

4. The DB Scheme

4.DB计划

Let’s now define the SQL structure for storing our OAuth clients:

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

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)
);

The most important fields from the oauth_client_details we should focus on are:

我们应该关注oauth_client_details中最重要的字段。

  • client_id – to store the id of newly registered clients
  • client_secret – to store the password of clients
  • access_token_validity – which indicates if client is still valid
  • authorities – to indicate what roles are permitted with particular client
  • scope – allowed actions, for example writing statuses on Facebook etc.
  • authorized_grant_types, which provides information how users can login to the particular client (in our example case it’s a form login with password)

Please note, that each client has one to many relationship with users, which naturally means that multiple users can utilize a single client.

请注意,每个客户端与用户有一对多的关系,这自然意味着多个用户可以利用一个客户端

5. Let’s Persist Some Clients

5.让我们坚持一些客户

With SQL schema define, we can finally create some data in the system – and basically define a client.

通过SQL模式定义,我们终于可以在系统中创建一些数据–基本上可以定义一个客户端。

We’re going to use the following data.sql script – which Spring Boot will run by default – to initialize the DB:

我们将使用以下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);

The description of the most important fields in oauth_client_details is provided in previous section.

oauth_client_details中最重要的字段描述已在上一节中提供。

6. Testing

6.测试

In order to test the dynamic client registration, we need to run both spring-security-oauth-server and spring-security-oauth-resource projects, on the 8081 and 8082 ports, respectively.

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

Now, we can finally write a few live tests.

现在,我们终于可以写一些现场测试了。

Let’s assume, that we registered client with id named fooClientIdPassword, that has an access to read foos.

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

First, we’ll try to obtain an Access Token from the Auth Server, using an already defined client:

首先,我们将尝试使用一个已经定义好的客户端,从Auth服务器获得一个访问令牌。

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

And here’s the logic of obtaining the Access Token:

而这里是获取访问令牌的逻辑。

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. Conclusion

7.结论

In this tutorial, we learned how to dynamically register unlimited number of clients with OAuth2.0 framework.

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

The full implementation of this tutorial can be found over on GitHub – this is a Maven-based project, so it should be easy to import and run as it is.

本教程的完整实现可以在GitHub上找到over on GitHub – 这是一个基于Maven的项目,所以应该很容易导入并按原样运行。

Please note, that in order to test, you’ll need to add clients into DB, and that the .inMemory() config will be no longer valid. If you want to use the old .inMemory() config, there is a second file containing configuration with hardcoded clients.

请注意,为了测试,你需要将客户端添加到DB中,并且.inMemory()配置将不再有效。如果你想使用旧的.inMemory()配置,有第二个文件含有硬编码客户端的配置。