If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:
>> LEARN SPRING SECURITYMocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
1. Introduction
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.
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.
Note: this article is using the Spring OAuth legacy project.
2. Maven Dependencies
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.
3. OAuth2.0 Server Configuration
First, we need to configure our OAuth2.0 authorization server. The main configuration is inside the following class:
@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:
@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
Letβs now define the SQL structure for storing our OAuth clients:
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:
- 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
With SQL schema define, we can finally create some data in the system β and basically define a client.
Weβre going to use the following data.sql script β which Spring Boot will run by default β to initialize the DB:
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.
6. Testing
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.
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.
First, weβll try to obtain an Access Token from the Auth Server, using an already defined client:
@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
In this tutorial, we learned how to dynamically register unlimited number of clients with OAuth2.0 framework.
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.
