In this blog, we will demonstrate how to add basic authentication your your Spring Boot application.
We will start with the simplest possible authentication using in-memory user authentication, and then move to authentication using users/roles from standard tables for auth.
Finally we will move onto authentication using custom user and roles tables, and also look at Spring’s internal classes/workflow that does the actual authentication.
Table of Contents
- 1.0 Adding Simple In-Memory User Authentication
- 2.0 Adding Basic Role Based Authorization
- 3.0 Authenticating using database user/roles using encrypted passwords
- 4.0 Using custom user/roles tables for authentication
- 4.1 Custom tables for user and roles
- 4.2 Modifying Security Configuration to use custom database tables
- 4.3 Internals of Spring Authentication
- 5.0 Autowiring an exisiting User Service for authentication
The entire code of for this project can be found at https://github.com/chatterjeesunit/spring-boot-app/tree/v3.0
To checkout this Release tag, run following command
git clone https://github.com/chatterjeesunit/spring-boot-app.git
cd spring-boot-app
git checkout tags/v3.0 -b v3.0-with-auth
1.0 Adding Simple In-Memory User Authentication
In our previous blogs, we demonstrated how to create Spring Boot application and add Flyway
However the application lacked a very basic feature – Application Security. There was no authentication and authorization build into the application. Anybody could hit the application urls and create and fetch data.
Authentication is verifying your identity. e.g. Login in via your username and password.
Authorization is being able to verify what you can access. e.g. Accessing pages/ links etc, based on your roles and privileges
This blog builds on top of the previous blog for Adding Flyway Integration to Spring Boot Application.
Hence the source code for the base Spring Boot application that we will use can be found at – https://github.com/chatterjeesunit/spring-boot-app/tree/v2.0
We recommend that you follow this step by step guide using the above base source code. But it is not required, and you can follow these steps for any Spring boot application code that you have and add authentication to it.
1.1 Configurations
Add following Spring Security dependency to your gradle project.
compile('org.springframework.boot:spring-boot-starter-security')
1.2 Adding Authentication
Create new Security Configuration Class
- This class should extend from
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
- Add annotation
@Configuration
to the class
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
}
Now override the method – configure(HttpSecurity http
) in this class, and add following code to enable authentication.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().disable()
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests().anyRequest().authenticated();
}
The above code configures HTTP security to the application.
This line of code is most important – .authorizeRequests().anyRequest().authenticated()
– as it tells that all HTTP Requests will be authenticated.
Now try hitting any of the application url
curl -i -X POST -H "Content-Type: application/json" http://localhost:8080/customer/ -d '{"firstName":"John","lastName":"Doe","emailAddress":"johndoe@gmail.com","addresses":[{"streetAddress":"4487 Elsie Drive","city":"Onida","stateCode":"SD","zipCode":"57564","country":"USA"}]}'
You will get a HTTP 401
status back, which mean UnAuthorised Request.
Below is the sample response that you may get
HTTP/1.1 401
Set-Cookie: JSESSIONID=20BDADF36A01A509D6E5D327A6FBC6CB; Path=/; HttpOnly
WWW-Authenticate: Basic realm="Realm"
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 15 Aug 2019 13:23:15 GMT
{"timestamp":"2019-08-15T13:23:15.455+0000","status":401,"error":"Unauthorized","message":"Unauthorized","path":"/customer/"}%
This means that we have added authentication to our application and nobody can access the application REST urls without providing authentication information.
1.3 Adding In Memory User Authentication
As of now there are no users in the system that we can authenticate with.
To start with lets create an in memory database of users that we use for authentication.
Using In Memory authentication is useful for demo purposes but not good for production environments. In later sections we will later see how to do authentication with users in database.
Modify the security config class that we created earlier – SecurityConfig
and add following
- Override following method –
configure(AuthenticationManagerBuilder auth)
- Lets add two users for authentication purpose
- User: admin01 , Password : admin01@123# , Role : ADMIN
- User: user01, Password : welcome@123#, Role : USER
Although we have added User Roles, ignore them for now, and we will use them later.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin01")
.password("{noop}admin01@123#")
.roles("ADMIN")
.and()
.withUser("user01")
.password("{noop}welcome@123#")
.roles("USER");
}
We have prefixed {noop}
in front of all the passwords, to ensure that the Spring Boot application uses the following password encoder – org.springframework.security.crypto.password.NoOpPasswordEncoder
.
This is NOT a secure option but good to start with, as it performs authentication using un-encrypted text passwords.
In later sections we will see how to use encrypted passwords.
1.4 Testing Authentication
Build and run the application. Now we can authenticate http requests to the application using the above two users that we created in in memory database.
We will do testing using two REST clients – curl
and Postman
1.4.1 Testing using CURL
Use the -u <user>:<password>
option of curl command to provide the username and password for the http request.
e.g Just add -u admin01:admin01@123#
OR -u user01:welcome@123#
to the http requests.
curl -i -u user01:welcome@123# -X POST -H "Content-Type: application/json" http://localhost:8080/customer/ -d '{"firstName":"John","lastName":"Doe","emailAddress":"johndoe@gmail.com","addresses":[{"streetAddress":"4487 Elsie Drive","city":"Onida","stateCode":"SD","zipCode":"57564","country":"USA"}]}'
Now you can see the the http requests are getting authenticated and are successful.
You can also try with some invalid username or password combination, to test the 401 Unauthorised response.
1.4.2 Testing using Postman
If you are using Postman Rest client, then select BasicAuth
under Authorization
tab, and provide the username and password.

1.5 Skipping Authentication for Some Urls
There are situations where you may not want authentication for all urls of the system. Some example could be application health and metric urls, that should be available without authentication.
Try running below Spring Boot Health url, and you will get 401 Unauthorized error
curl -i 'localhost:8080/actuator/'
curl -i 'localhost:8080/actuator/health'
So we may need to add configuration so skip authentication for some urls.
To do this modify the Configure HTTP Auth
method in the SecurityConfig
class, and allow these URLs explicitly.
e.g Add following line of code – .antMatchers("/actuator/**").permitAll()
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().disable()
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated();
}
Above code will ensure that any URL that starts with /actuator/
will not be authenticated, and remaining all http requests will require authentication.
2.0 Adding Basic Role Based Authorization
Now that we have added authentication, lets look into the authorization aspect. As of now any authenticated users can access all http requests of the application.
Ideally this is never the real world scenario and we have people given access to specific areas of the system.
Lets consider a scenario, where we want that
- Only users with ADMIN role can create or update customers
- But all users can find customers or get customers.
So basically what we want is to that only users with ADMIN role can do POST or PUT http requests, and any other authenticate users can do GET requests
To do this we will add another AntMatcher in the HTTP Security Config and provide role information that is required for it.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().disable()
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers(HttpMethod.POST).hasRole("ADMIN")
.antMatchers(HttpMethod.PUT).hasRole("ADMIN")
.anyRequest().authenticated();
}
Now lets try hitting the POST Request with user01, who does not have ADMIN role access. You should get HTTP 403 status back on doing this.
This means the user is authenticated but not authorized or forbidden to access this request.
curl -i -u user01:welcome@123# -X POST -H "Content-Type: application/json" http://localhost:8080/customer/ -d '{"firstName":"John","lastName":"Doe","emailAddress":"johndoe@gmail.com","addresses":[{"streetAddress":"4487 Elsie Drive","city":"Onida","stateCode":"SD","zipCode":"57564","country":"USA"}]}'
HTTP/1.1 403
.....
.....
{"timestamp":"2019-08-15T15:19:32.082+0000","status":403,"error":"Forbidden","message":"Forbidden","path":"/customer/"}%
However when you try the same request with admin01@tw.com user, the request will get authorized.
curl -i -u admin01:admin01@123# -X POST -H "Content-Type: application/json" http://localhost:8080/customer/ -d '{"firstName":"John","lastName":"Doe","emailAddress":"johndoe@gmail.com","addresses":[{"streetAddress":"4487 Elsie Drive","city":"Onida","stateCode":"SD","zipCode":"57564","country":"USA"}]}'
But you can make the GET requests with users of any role.
curl -i -u user01:welcome@123# 'localhost:8080/customer/?pageNum=0&pageSize=2'
curl -i -u admin01:admin01@123# 'localhost:8080/customer/?pageNum=0&pageSize=2'
3.0 Authenticating using database user/roles using encrypted passwords
Till now we have added both authentication and authorization to our application, and also skipped authentication for some urls like Spring Boot Actuator urls.
So far so good.
However, this application is still not ready for production. Two primary reasons
- User authentication should be done using Users in the database
- Passwords should be encrypted.
Lets do both of them in this section
3.1 Database changes
We will need two tables in the database
- users – for storing the user information
- authorities – for user-role definitions
The advantage of using the default table names/table schema is that you don’t need to add any Java code to fetch users, etc. The Default implementation of Spring Boot automatically takes care of it.
Below script will create the required tables and also create initial data of users and roles that we require for our authentication demo.
If you are using Flyway, then add below SQL to flyway migration script.
Else you can just run the below script on your database.
create table users(
username varchar(50) not null primary key,
password varchar(500) not null,
enabled boolean not null);
create table authorities (
username varchar(50) not null,
authority varchar(50) not null,
constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);
INSERT INTO `users` (`username`, `password`, `enabled`)
VALUES ('admin01', '{noop}admin01@123#', true);
INSERT INTO `users` (`username`, `password`, `enabled`)
VALUES ('user01', '{noop}welcome@123#', true);
INSERT INTO `authorities` (`username`, `authority`)
VALUES ('admin01', 'ROLE_ADMIN');
INSERT INTO `authorities` (`username`, `authority`)
VALUES ('user01', 'ROLE_USER');
commit;
Make sure to prefix
ROLE_
to the name of the roles when you insert into theauthorities
database.
3.2 Enabling authentication using database users
Now our database is ready with all users and role information.
Modify SecurityConfig
class, and change the code in configure(AuthenticationManagerBuilder auth)
method.
- Remove the entire code for In memory database
- Add code for JDBC authentication
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
Next autowire the Datasource into your SecurityConfig
class
@Autowired
DataSource dataSource;
Add a Bean Definition for PasswordEncoder
@Bean
public PasswordEncoder getPasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Test the application again
curl -i -u admin01:admin01@123# -X POST -H "Content-Type: application/json" http://localhost:8080/customer/ -d '{"firstName":"John","lastName":"Doe","emailAddress":"johndoe@gmail.com","addresses":[{"streetAddress":"4487 Elsie Drive","city":"Onida","stateCode":"SD","zipCode":"57564","country":"USA"}]}'
curl -i -u user01:welcome@123# 'localhost:8080/customer/?pageNum=0&pageSize=2'
3.3 Using encrypted Passwords
Now lets perform the last step and encrypt the passwords.
We will use Bcrypt Hashing to generate encrypted passwords. We will create the encrypted passwords using Online Bcyrpt password generator.
Now lets modify the database and update the encrypted passwords. Either apply the script through Flyway migration script or apply it directly on your database.
UPDATE `users`
set `password` = '$2a$10$4LEwPTJ86OF/oZUn8hl0vOhSUhFqX5YwNO./i/bTeTD6cn5lRLj2S'
where `username` = 'admin01';
UPDATE `users`
set `password` = '$2a$10$yPIWEiYj8sGLox.9cPKPZe6GgGRy.T8iV/sR2Br1PyA0UzLaYVOa.'
where `username` = 'user01';
commit;
Next modify the SecurityConfig
code to use BcryptPasswordEncoder
.
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
Run the application and test again.
Now the authentication and authorization is working from JDBC authentication and using encrypted passwords.
4.0 Using custom user/roles tables for authentication
3.4 Authentication using custom database tables for users and roles
So far we have seen how to JDBC based authentication using default database schema for users and authorities table.
What if you already have a different table for Users and roles in your database and it does not match with the recommended schema for users
and authorities
?
What if we already have a different table with all user data and we want to the users to login using their email_address?
Spring boot is very flexible in this matter and also allows us to customize the authentication from our custom tables, as long as we have some way to identify users and their passwords and associated roles.
4.1 Custom tables for users and roles
Suppose your application already has following user and roles tables as given below

You may want to use your custom tables for user authentication and user user’s email address
as username
for authentication.
This assumes you already have these tables and users and roles data inserted into the tables, and that the
user_info
table contains passwords encrypted using Bcrypt Hashing.
However if you don’t have these tables but still want to try them out, you can run this script to create the tables and insert users.
4.2 Modifying Security Configuration to use custom database tables
In order to authenticate using user and role information from custom tables, we need to create two queries first.
- User Query : We need a database query to fetch users by user’s login name, and return it’s
username
,password
and a boolean flag (enabled or not). Since we don’t haveenabled
flag in our database schema, we return a default value oftrue
.
select email_address, password, true as enabled
from user_info where email_address = ?
- User Role Query : We need a database query to get username and the name of role associated with the user.
select u.email_address, r.role_name
from userinfo u inner join user_roles ur on u.id = ur.user_id
inner join roles r on r.id = ur.role_id
where u.email_address = ?
?
in above queries is place holder for user’s login name – which is email_address
in current scenario.
Now that we have our queries ready, add Queries to the SecurityConfig
class
private final String USER_QUERY =
"select email_address, password, true as enabled " +
"from user_info where email_address = ?";
private String USER_ROLE_QUERY =
"select u.email_address, r.role_name " +
"from user_info u inner join user_roles ur on u.id = ur.user_id " +
"inner join roles r on r.id = ur.role_id " +
"where u.email_address = ?";
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(USER_QUERY)
.authoritiesByUsernameQuery(USER_ROLE_QUERY);
}
Now start the application and we are ready to test with new user credential from custom database tables.
curl -i -u user01@tw.com:welcome@123# 'localhost:8080/customer/?pageNum=0&pageSize=2'
curl -i -u admin01@tw.com:admin01@123# 'localhost:8080/customer/?pageNum=0&pageSize=2'
4.3 Internals of Spring Authentication
Now that you have seen how authentication works, you might be wondering how does everything works so easily.
We only specified the query for fetching users and authorities, but how did it work automatically? What is the magic behind it?
The logic behind that resides in the javax.servlet.Filter
and org.springframework.security.authentication.AuthenticationProvider
classes already present in Spring Boot code.
The most important classes are
org.springframework.security.web.authentication.www.BasicAuthenticationFilter
org.springframework.security.authentication.dao.DaoAuthenticationProvider
See the sequence diagram below to look at the code flow within Spring Boot’s code that does username/password authentication.

5.0 Autowiring an exisiting User Service for authentication
Till now we have seen multiple ways to authenticate in our application
- In memory user authentication
- Authentication with users/role stored in database tables (with standard table names and structure)
- Authentication with custom user/role tables (by configuring the sql queries to load user and roles)
Although the last one above solved our requirement, but this did not seem a very clean solution. Primary reason being that we had to write explicit SQL queries on how to fetch users and roles, and also autowire a datasource dependency within the SecurityConfig
.
Ideally when you have custom classes for users and roles you also might have following already implemented
- User and Role Entity objects
- UserRepository
- UserService that can be used to fetch users and roles.

How do we use this existing UserService
for authentication?
Role
class must implementorg.springframework.security.core.GrantedAuthority
, and implementString getAuthority()
method to return the role name.
User
class must implementorg.springframework.security.core.userdetails.UserDetails
, and implement methodCollection<? extends GrantedAuthority> getAuthorities()
– return all the roles for the user.String getUsername()
– return the user’s email address.boolean isAccountNonExpired()
– return true.boolean isAccountNonLocked()
– return true.boolean isCredentialsNonExpired()
– return true.boolean isEnabled()
– return true.
UserService
must implementorg.springframework.security.core.userdetails.UserDetailsService
, and implement the methodUserDetails loadUserByUsername(String username)
– fetch user by email address and throwUserNotFoundException
if user with the email does not exists.
If you do the above then your UserService
is ready to be integrated to the authentication flow.
Go to the class – com.dev.springdemo.config.SecurityConfig
, that we had created and remove all the SQL queries and modify the method configure(AuthenticationManagerBuilder auth)
as shown below
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
// remaining code hidden
}
That is all we need to do to integrate our UserService
to authentication flow. You can test the application now by trying to hit any application URL (with username/password).
If you debug the authentication workflow you will notice one change now from above – JdbcDAOImpl
is now replaced by our UserService
, and rest of authentication flow is exactly same as before.

This will bring us to the end of this blog. In future blog we will look at how we can skip passing username/password with each url, and use a
JWT
for authentication.The entire code of for this project can be found at https://github.com/chatterjeesunit/spring-boot-app/tree/v3.0
To checkout this Release tag, run following command
git clone https://github.com/chatterjeesunit/spring-boot-app.git
cd spring-boot-app
git checkout tags/v3.0 -b v3.0-with-auth
In future articles we will look into how to add auditing, caching, etc to this Spring boot application.