Spring Security With JWT and Oauth2 With Spring Boot

2018-02-23 spring-boot jwt oauth2 security

Reading time: 6 Min(s)

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

This will boot the spring application.

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 application

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

7.1. Set the properties related to security

The detail of the properties is as below -

  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 BCrypt

  6. 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=resource resource id.

7.3. JWTConfigProperties.java

Create a class to access the properties set in application property file.

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.

*Autowire the userDetailsService instance.

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

Create a class 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

*Autowire the jwtConfigProperties, jwtAccessTokenConverter, tokenStore and authenticationManager instance.

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

Create a class 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

*Autowire the jwtConfigProperties, defaultTokenService bean instances.

Resource id is configured in application.property file. defaultTokenService is created in class WebSecurityConfig.

9.2. Configure HttpSecurity

This configuration says, authenticate all request that contains "/app/**"in there URL.

9.3. Enable the resource server configuration

10. Modify the controller class

Finally set the roles that can access the application using @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

Now all the configurations are done, now we can run the application using below maven command in terminal

mvn spring-boot:run

11.1 Access Resource with User credentials

After server started, We can get the access token. Run below command to get the access token-

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

To access the resource that required admin privilege you need to get the access token with user james/james, because this user have admin privilege

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.