Introduction-
In this article we shall see example of how to secure a spring boot rest application with Spring Boot2, Spring Security, Oauth2, and JWT token.1. Source Code Repository -
The code used in this article is available in this repository GitHub.2. Dependencies -
2.1. Versions -
- Spring Boot: 2.1.3.RELEASE
- Java: 1.8
3. Entity Class -
We create two Entity Classes, one is User and another is Role. User entity class contains user details like name, password, roles details, and role entity contains the role details. One User may have many roles hence we to create many-to-many relationship between User and Role Entity.
3.1. User Entity.java
Entity class that contains the user details.
3.2. Role.java
Entity class that contains the user role data.
4. Create CRUD Repository -
We create spring CRUD repository class, to access user credentials from the database.
Note: we create repository only for User entity class, because in User entity class we specify Many to Many relationship between User and Role entity, and
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
at line number 35 in User.java, will ensure that when new record save in User entity it will also save in Role entity. 4.1. UserRepository.java
5. Create a Spring boot Initialize class
Now we need to create initial data for users create data.sql file and put it into resource directory.
6. Controller class
Now create controller class to access the resources.
Now our basic application is created, just the start the application server to see the output.
use
mvn spring-boot:run
to run the applicationAfter the server start just curl the below URL to check if everything is working correct or not
curl http://localhost:8080/app/listAll
Response -
[
{
"id": 1,
"username": "vikas",
"password": "$2a$04$lZj8KgBFkcPwgRWjH8DwBeCIR7HE6AsIZqTXu2VyeEw5sYLySNAGe",
"firstName": "vikas",
"lastName": "verma",
"roles": [
{
"id": 1,
"roleName": "ADMIN",
"description": "admin role"
}
]
},
{
"id": 2,
"username": "james",
"password": "$2a$04$P2GbxPDh1MYNYyNn/bj.4.QxwDC2jze0xPQF4u6/cNpdkrPq3OdPy",
"firstName": "james",
"lastName": "james",
"roles": [
{
"id": 1,
"roleName": "ADMIN",
"description": "admin role"
},
{
"id": 2,
"roleName": "USER",
"description": "user role"
}
]
}
]
7. Authentication and Authorization
Now since our basic application is working, it’s time to add authentication and authorization to our application.
Add Dependencies for the Oauth2 Security7.1. Set the properties related to security
1.
security.encoding-strength=256
size of the encoding. 2.
security.signing-key
is the sign key used to encode the token. 3.
security.security-realm
realm of the authentication. see 4.
security.jwt.client-id=client
Client Id. 5.
security.jwt.client-secret=
It should be BCrypt format you can use this tool to encode any string to BCrypt6.
security.jwt.grant-type=password
Grant type, it can be "password", "refresh-token". 7.
security.jwt.scope-read=read
read scope. 8.
security.jwt.scope-write=write
write scope. 9.
security.jwt.resource-ids=testresource
resource id. 7.3. JWTConfigProperties.java
7.4. UserDetailServiceImpl.java
To access the user credentials from database, we need to implement interface
UserDetailsService
. It is used throughout the framework as a user DAO. The interface requires only one read-only method, which simplifies support for new data-access strategies. 7.5. WebSecurityConfig.java
Configure the web security by extending the
WebSecurityConfigurerAdapter
class. It provides a convenient base class for creating a WebSecurityConfigurer
instance. The implementation allows customization by overriding methods.
Step#1 Configure UserDetailsService to
AuthenticationManager
class, and add a password encoder.
Step#2 Now create AuthenticationManager bean
Since we are using password as grant-type we need to provide the AuthenticationManager implementation.
Step#3 Configure the JWT Token related beans -
tokenStore()
Create a new JwtTokenStore with this token enhancer(accessTokenConverter).accessTokenConverter()
Configure JWT signing key. It can be either a simple MAC key or an RSA key. . RSA keys should be in OpenSSH format, as produced by ssh-keygen.tokenServices()
it is default implimentation of the tokenServices, it used to configure the resource related properties. In the tokenServices we used the default implementation of the TokeService interface, and set persistence strategy for token storage. the tokenStore, and configure to support the refresh token.Now enable Web Security in the application, and Global Method security-
@EnableWebSecurity
: It allow the Spring Security configuration defined in any WebSecurityConfigurer or more likely by extending the WebSecurityConfigurerAdapter
base class and overriding individual methods.
@EnableGlobalMethodSecurity(prePostEnabled = true)
: Enables Spring Security global method security. @Order
: Define the order the security filter chain. The priority of the WebSecurityConfigurerAdapter is more than the resourceServerConfigurationAdapter
, hence we re-define the order WebSecurityConfigurerAdapter
8. Define Authentication Manager
AuthenticationServerConfig.java
that extends AuthorizationServerConfigurerAdapter.java
to provides the default implementation for the AuthorizationServer. It is used to register the clients that can access the resource of the application, and also endpoints of the authorization server. 8.1. Configure Clients of the application
the code is self-explanatory, we jest configure the client details that are stored in memory. All the clients’ details are store in the application.property file.
*Autowire the jwtConfigProperties instance. 8.2. Configure the Authorization endpoints
Configure the tokenstore, and tokenEnhancer with the
AuthorizationServerEndpointsConfigurer
Configure the TokenStore, authenticationManager, and tokenEnhancerChain in the AuthorizationServerEndpointsConfigurer class.
8.3. Enable Authorization Server in the current application context
9. Resource Server Configuration
ResourceServerConfig.java
that extends ResourceServerConfigurerAdapter.java
This class is used to configure the resourceIds, and the http request URLs that are allowed to access the application, and the URLs that need to be authenticated.
9.1. Configure Resource Id
Resource id is configured in application.property file.
defaultTokenService
is created in class WebSecurityConfig
. 9.2. Configure HttpSecurity
"/app/**"
in there URL. 9.3. Enable the resource server configuration
10. Modify the controller class
@PostAuthorize
annotation 11. Run The application
11.1 Run MySQL Database
Run below command to run the mysql db in docker
docker-compose -f docker/docker-compose up
mvn spring-boot:run
11.1 Access Resource with User credentials
11.1.1. Get Access Token
Result-
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzb3VyY2UiXSwidXNlcl9uYW1lIjoidmlrYXMiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTUyMDA5ODM2LCJhdXRob3JpdGllcyI6WyJBRE1JTiJdLCJqdGkiOiI3YWJkOThhNC1hNjM4LTRmYmQtOWYzMC0zZWJiNDQ0M2FhMTciLCJjbGllbnRfaWQiOiJjbGllbnQifQ.P0UgLpAuswCs8iHsxT4q23TI1infsIMqZ1YtMlbfWe8",
"token_type": "bearer",
"expires_in": 43199,
"scope": "read write",
"jti": "7abd98a4-a638-4fbd-9f30-3ebb4443aa17"
}
11.2 Access Resource
11.2.1. Get the access token
** "Y2xpZW50OnNlY3JldA==" is Base64 encoded client-id:secret. you can encode and decode in base64 format using this site. The format of client-id and client-secret should be 'client-id:secret'.
Result-
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzb3VyY2UiXSwidXNlcl9uYW1lIjoidmlrYXMiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTUyMDA5ODM2LCJhdXRob3JpdGllcyI6WyJBRE1JTiJdLCJqdGkiOiI3YWJkOThhNC1hNjM4LTRmYmQtOWYzMC0zZWJiNDQ0M2FhMTciLCJjbGllbnRfaWQiOiJjbGllbnQifQ.P0UgLpAuswCs8iHsxT4q23TI1infsIMqZ1YtMlbfWe8",
"token_type": "bearer",
"expires_in": 43199,
"scope": "read write",
"jti": "7abd98a4-a638-4fbd-9f30-3ebb4443aa17"
}
11.2.2. Access Admin Resources
Result-
Access admin resources -
Hello Admin!!!
Access user resources -
Hello User!!!
11.2.3. Access the User resources
Result-
Hello User!!!
That's all for the Authentication and Authorization with Oauth2. You can access the resource from GitHub.