“How to build SpringBoot Token Based Authentication Example?” is one of the most common questions for Java development world. So in the tutorial, I will introduce how to build it with clearly architecture and coding examples.
Video Guide
Spring Security JWT Architecture – Springboot Token Based Authentication Example
This is diagram for Spring Security/JWT (Springboot Token Based Authentication Example) classes that are separated into 3 layers:
– HTTP
– Spring Security
– REST API

Look at the diagram above, we can easily associate these components with Spring Security Authentication process: receive HTTP request, filter, authenticate, store Authentication data, generate token, get User details, authorize, handle exception…
At a glance:
– SecurityContextHolder
provides access to the SecurityContext
.
– SecurityContext
holds the Authentication
and possibly request-specific security information.
– Authentication
represents the principal which includes GrantedAuthority
that reflects the application-wide permissions granted to a principal.
– UserDetails
contains necessary information to build an Authentication
object from DAOs or other source of security data.
– UserDetailsService
helps to create a UserDetails from a String-based username and is usually used by AuthenticationProvider
.
– JwtAuthTokenFilter
(extends OncePerRequestFilter
) pre-processes HTTP request, from Token
, create Authentication
and populate it to SecurityContext
.
– JwtProvider
validates, parses token String or generates token String from UserDetails
.
– UsernamePasswordAuthenticationToken
gets username/password from login Request
and combines into an instance of Authentication
interface.
– AuthenticationManager
uses DaoAuthenticationProvider
(with help of UserDetailsService
& PasswordEncoder
) to validate instance of UsernamePasswordAuthenticationToken
, then returns a fully populated Authentication
instance on successful authentication.
– SecurityContext
is established by calling SecurityContextHolder.getContext().setAuthentication(…)
with returned authentication object above.
– AuthenticationEntryPoint
handles AuthenticationException
.
– Access to Restful API is protected by HTTPSecurity
and authorized with Method Security Expressions.
Receive HTTP Request
When a HTTP request comes (from a browser, a web service client, an HttpInvoker or an AJAX application – Spring doesn’t care), it will go through a chain of filters for authentication and authorization purposes.
So, it is also true for a User Authentication request, that filter chain will be applied until relevant Authentication Filter is found.
Filter the Request
In this architecture, we add our JwtAuthTokenFilter
(that extends Spring OncePerRequestFilter
abstract class) to the chain of filters.
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
...
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
JwtAuthTokenFilter
validates the Token using JwtProvider
:
class JwtAuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtProvider tokenProvider;
@Override
protected void doFilterInternal(...) {
String jwt = getJwt(request);
if (jwt!=null && tokenProvider.validateJwtToken(jwt)) {
...
}
filterChain.doFilter(request, response);
}
}
Now we have 2 cases for Springboot Token Based Authentication Example:
– Login/SignUp: RestAPI with non-protected APIs -> authenticate Login Request with AuthenticationManager
, if error occurs, handle AuthenticationException
with AuthenticationEntryPoint
.
– With protected Resources:
+ jwt token is null/invalid -> if Authenticated Error occurs, handle AuthenticationException
with AuthenticationEntryPoint
.
+ jwt token is valid -> from token, get User
information, then create AuthenticationToken
.
Create AuthenticationToken from Token
JwtAuthTokenFilter
extracts username/password from the received token using JwtProvider
, then based on the extracted data, JwtAuthTokenFilter
:
– creates a AuthenticationToken
(that implements Authentication
)
– uses the AuthenticationToken
as Authentication
object and stores it in the SecurityContext
for future filter uses (e.g: Authorization
filters).
In this tutorial, we use UsernamePasswordAuthenticationToken
:
// extract user information
String username = tokenProvider.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// create AuthenticationToken
UsernamePasswordAuthenticationToken authentication
= new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
Store Authentication object in SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder
is the most fundamental object where we store details of the present security context of the application (includes details of the principal). Spring Security uses an Authentication object to represent this information and we can query this Authentication object from anywhere in our application:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// currently authenticated user
Object principal = authentication.getPrincipal();
getContext()
returns an instance of SecurityContext
interface that holds the Authentication
and possibly request-specific security information.
Delegate AuthenticationToken for AuthenticationManagager
After AuthenticationToken
object was created, it will be used as input parameter for authenticate()
method of the AuthenticationManager
:
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
We can see that AuthenticationManager
is just an interface, the default implementation in Spring Security is ProviderManager
:
public class ProviderManager implements AuthenticationManager, ... {
private List providers;
}
Authenticate with AuthenticationProvider
AuthenticationProviders
ProviderManager
delegates to a list of configured AuthenticationProviders
, each of them will try to authenticate the User
, then either throw an exception or return a fully populated Authentication
object:
public class ProviderManager implements AuthenticationManager, ... {
private List providers;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
for (AuthenticationProvider provider : getProviders()) {
...
try {
...
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (Exception...) {}
...
return result;
}
}
}
These are some authentication providers that Spring Framework provides:
- DaoAuthenticationProvider
- PreAuthenticatedAuthenticationProvider
- LdapAuthenticationProvider
- ActiveDirectoryLdapAuthenticationProvider
- JaasAuthenticationProvider
- CasAuthenticationProvider
- RememberMeAuthenticationProvider
- AnonymousAuthenticationProvider
- RunAsImplAuthenticationProvider
- OpenIDAuthenticationProvider
DaoAuthenticationProvider
DaoAuthenticationProvider
works well with form-based logins or HTTP Basic authentication which submits a simple username/password authentication request.
It authenticates the User simply by comparing the password submitted in a UsernamePasswordAuthenticationToken
against the one loaded by the UserDetailsService
(as a DAO):
@Autowired
AuthenticationManager authenticationManager;
...
Authentication authentication =
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.username, loginRequest.password)
);
Configuring this provider is simple with AuthenticationManagerBuilder
:
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Retrieve User details with UserDetailsService
We can obtain a principal from the Authentication
object. This principal can be cast into a UserDetails
object to lookup the username, password and GrantedAuthoritys
.
Therefore, after authenticating is successful, we can simply get UserDetails
from Authentication
object:
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
// userDetails.getUsername()
// userDetails.getPassword()
// userDetails.getAuthorities()
DaoAuthenticationProvider
also uses UserDetailsService
for getting UserDetails
object. This is the common approach in which we only pass a String-based ‘username’ argument and returns a UserDetails
:
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
It is simple to implement UserDetailsService
and easy for us to retrieve authentication information using a persistence strategy:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username).orElseThrow(
() -> new UsernameNotFoundException("User Not Found with -> username or email : " + username));
return UserPrinciple.build(user); // UserPrinciple implements UserDetails
}
}
Get GrantedAuthority
Another important method provided by Authentication
is getAuthorities()
that provides an collection of GrantedAuthority objects:
public interface Authentication extends Principal, Serializable {
Collection extends GrantedAuthority> getAuthorities();
}
A GrantedAuthority
is an authority that is granted to the principal. Such authorities are usually ‘roles’, such as ROLE_ADMIN
, ROLE_PM
, ROLE_USER
…
Protect Resources with HTTPSecurity & Method Security Expressions
Configure HTTPSecurity
To help Spring Security know when we want to require all users to be authenticated, which Exception Handler to be chosen, which filter and when we want it to work. We implement WebSecurityConfigurerAdapter
and provide a configuration in the configure(HttpSecurity http)
method:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().
authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
...;
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Method Security Expressions
Spring Security provides some annotations for pre and post-invocation authorization checks, filtering of submitted collection arguments or return values: @PreAuthorize
, @PreFilter
, @PostAuthorize
and @PostFilter
.
To enable Method Security Expressions, we use @EnableGlobalMethodSecurity
annotation:
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
In the code below, we use the most useful annotation @PreAuthorize
to decide whether a method can actually be invoked or not:
@RestController
public class TestRestAPIs {
@GetMapping("/api/test/user")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public String userAccess() {
return ">>> User Contents!";
}
@GetMapping("/api/test/pm")
@PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
public String projectManagementAccess() {
return ">>> Project Management Board";
}
@GetMapping("/api/test/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return ">>> Admin Contents";
}
}
Handle AuthenticationException – AuthenticationEntryPoint
If the user requests a secure HTTP resource without being authenticated, AuthenticationEntryPoint
will be called. At this time, an AuthenticationException
is thrown, commence()
method on the entry point is triggered:
@Component
public class JwtAuthEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e)
throws IOException, ServletException {
logger.error("Unauthorized error. Message - {}", e.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized");
}
}
Build Springboot Token Based Authentication Example
Technologies
For Springboot Token Based Authentication Example, we use below stack of tech:
– Spring Boot
– jjwt – 0.9.0
– Spring Security
– Spring JPA
– MySQL
Introduce JSON Web Token
JSON Web Token (JWT) defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
Scenarios where JSON Web Tokens are useful:
- Authorization: the most common scenario for using JWT. Single Sign On is a feature that widely uses JWT
- Information Exchange: Because JWTs can be signed, JSON Web Tokens are a good way of securely transmitting information between parties.
JSON Web Tokens consist of 3 parts:
- Header
- Payload
- Signature
-> JWT
looks like Header-Base64-String.Payload-Base64-String.Signature-Base64-String
Header consists of two parts:
- token type.
- hashing algorithm.
-> Example:
{
"alg": "HS256",
"typ": "JWT"
}
Payload contains the claims. Claims are statements about an entity and additional information.
There are 3 types of claims ->
Registered claims
-> These are a set of predefined claims: iss (issuer), exp (expiration time), sub (subject)Public claims
Private claims
Example:
{
"sub": "thomas-loizenjava.com",
"iat": 1537603195,
"exp": 1537689595
}
Signature: To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.
Example:
HMACSHA512(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
Combine all together, we get 3 Base64-URL strings separated by dots, example:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog
When accessing a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema.
Example:
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog
Overview
Project Structure for Springboot Token Based Authentication Example
We create a SpringBoot project as below:

– model
package defines 2 entities User
& Role
that have many-to-many relationship:

– repository
package contains interfaces that use Hibernate JPA to store/retrieve data from MySQL database.
– controller
package defines RestAPIs for user signup/signin and testing protected resources that is secured with JWT.
– message
package defines payload data transferred from user agents (Browser/RestClient…) to RestAPIs and message back.
– security
package is the main part of the project that implements JWT security.
Goal
In the tutorial “Springboot Token Based Authentication Example”, we expose 2 RestAPIs to signup
and signin
:
Sign up: /api/auth/signup

– Sign In /api/auth/signin
:

– In the tutorial Springboot Token Based Authentication Example, We expose 3 RestAPIs to test protected resources:
@GetMapping("/api/test/user")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public String userAccess() {
return ">>> User Contents!";
}
@GetMapping("/api/test/pm")
@PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
public String projectManagementAccess() {
return ">>> Board Management Project";
}
@GetMapping("/api/test/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return ">>> Admin Contents";
}
– Example Access Successfully:

– Example Unauthorized:

PRACTICE – SpringBoot Token Based Authentication Example
Create SpringBoot project
In the tutorial Springboot Token Based Authentication Example, We create a SpringBoot project with below dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
If you use MySQL database, add more the below dependency:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
If you use PostgreSQL database, add more the below dependency:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
Create Models
– User.java
model contains 5 attributes:
- id
- name
- username
- password
package com.loizenjava.jwtauthentication.model;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import org.hibernate.annotations.NaturalId;
@Entity
@Table(name = "users", uniqueConstraints = {
@UniqueConstraint(columnNames = {
"username"
}),
@UniqueConstraint(columnNames = {
"email"
})
})
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@Size(min=3, max = 50)
private String name;
@NotBlank
@Size(min=3, max = 50)
private String username;
@NaturalId
@NotBlank
@Size(max = 50)
@Email
private String email;
@NotBlank
@Size(min=6, max = 100)
private String password;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set roles = new HashSet<>();
public User() {}
public User(String name, String username, String email, String password) {
this.name = name;
this.username = username;
this.email = email;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set getRoles() {
return roles;
}
public void setRoles(Set roles) {
this.roles = roles;
}
}
– Role.java
model contains 2 attributes:
- id
- rolename
package com.loizenjava.jwtauthentication.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.NaturalId;
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
@NaturalId
@Column(length = 60)
private RoleName name;
public Role() {}
public Role(RoleName name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public RoleName getName() {
return name;
}
public void setName(RoleName name) {
this.name = name;
}
}
– RoleName.java
:
package com.loizenjava.jwtauthentication.model;
public enum RoleName {
ROLE_USER,
ROLE_PM,
ROLE_ADMIN
}
Implement Repository
– UserRepository
:
package com.loizenjava.jwtauthentication.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.loizenjava.jwtauthentication.model.User;
@Repository
public interface UserRepository extends JpaRepository {
Optional findByUsername(String username);
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
}
– RoleRepository.java
:
package com.loizenjava.jwtauthentication.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.loizenjava.jwtauthentication.model.Role;
import com.loizenjava.jwtauthentication.model.RoleName;
@Repository
public interface RoleRepository extends JpaRepository {
Optional findByName(RoleName roleName);
}
Implement JWT Security
– Configure WebSecurityConfig.java
:
package com.loizenjava.jwtauthentication.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.loizenjava.jwtauthentication.security.jwt.JwtAuthEntryPoint;
import com.loizenjava.jwtauthentication.security.jwt.JwtAuthTokenFilter;
import com.loizenjava.jwtauthentication.security.services.UserDetailsServiceImpl;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
prePostEnabled = true
)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Autowired
private JwtAuthEntryPoint unauthorizedHandler;
@Bean
public JwtAuthTokenFilter authenticationJwtTokenFilter() {
return new JwtAuthTokenFilter();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().
authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
– @EnableWebSecurity
is used to enable web security in a project.
– @EnableGlobalMethodSecurity(prePostEnabled = true)
is used to enable Spring Security global method security.
-> Example:
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public String userAccess() {
@GetMapping("/api/test/pm")
@PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
@GetMapping("/api/test/admin")
@PreAuthorize("hasRole('ADMIN')")
– PasswordEncoder
uses the BCrypt strong hashing function.
UserDetails Service
– UserDetailsServiceImpl
implements UserDetailsService
that will override loadUserByUsername
method. loadUserByUsername
method will find a record from users database tables to build a UserDetails object for authentication.
package com.loizenjava.jwtauthentication.security.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.loizenjava.jwtauthentication.model.User;
import com.loizenjava.jwtauthentication.repository.UserRepository;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() ->
new UsernameNotFoundException("User Not Found with -> username or email : " + username)
);
return UserPrinciple.build(user);
}
}
-> UserPrinciple
will implement UserDetails
.
UserPrinciple
is not used directly by Spring Security for security purposes.
It simply stores user information which is later encapsulated into Authentication
objects. This allows non-security related user information (such as email addresses, telephone numbers etc) to be stored.
package com.loizenjava.jwtauthentication.security.services;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.loizenjava.jwtauthentication.model.User;
public class UserPrinciple implements UserDetails {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private String username;
private String email;
@JsonIgnore
private String password;
private Collection extends GrantedAuthority> authorities;
public UserPrinciple(Long id, String name,
String username, String email, String password,
Collection extends GrantedAuthority> authorities) {
this.id = id;
this.name = name;
this.username = username;
this.email = email;
this.password = password;
this.authorities = authorities;
}
public static UserPrinciple build(User user) {
List authorities = user.getRoles().stream().map(role ->
new SimpleGrantedAuthority(role.getName().name())
).collect(Collectors.toList());
return new UserPrinciple(
user.getId(),
user.getName(),
user.getUsername(),
user.getEmail(),
user.getPassword(),
authorities
);
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserPrinciple user = (UserPrinciple) o;
return Objects.equals(id, user.id);
}
}
JWT Authentication Classes
– JwtAuthTokenFilter
extends OncePerRequestFilter
.
org.springframework.web.filter.OncePerRequestFilter
-> Executes once per request. This is a filter base class that is used to guarantee a single execution per request dispatch. It provides a doFilterInternal
method with HttpServletRequest
and HttpServletResponse
arguments.
In JwtAuthTokenFilter
class, the doFilterInternal
method will do:
- get JWT token from header
- validate JWT
- parse username from validated JWT
- load data from users table, then build an authentication object
- set the authentication object to Security Context
package com.loizenjava.jwtauthentication.security.jwt;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import com.loizenjava.jwtauthentication.security.services.UserDetailsServiceImpl;
public class JwtAuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtProvider tokenProvider;
@Autowired
private UserDetailsServiceImpl userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(JwtAuthTokenFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = getJwt(request);
if (jwt!=null && tokenProvider.validateJwtToken(jwt)) {
String username = tokenProvider.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication
= new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Can NOT set user authentication -> Message: {}", e);
}
filterChain.doFilter(request, response);
}
private String getJwt(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.replace("Bearer ","");
}
return null;
}
}
– JwtAuthEntryPoint
is used to handle Error exception when having unauthorized requests.
package com.loizenjava.jwtauthentication.security.jwt;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class JwtAuthEntryPoint implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class);
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e)
throws IOException, ServletException {
logger.error("Unauthorized error. Message - {}", e.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized");
}
}
– JwtProvider
is an util class, it implements useful functions:
- generate a JWT token
- valiate a JWT token
- parse username from JWT token
package com.loizenjava.jwtauthentication.security.jwt;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.loizenjava.jwtauthentication.security.services.UserPrinciple;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
/**
* @Copyright https://loizenjava.com
* @author loizenjava.com
*
*/
@Component
public class JwtProvider {
private static final Logger logger = LoggerFactory.getLogger(JwtProvider.class);
@Value("${loizenjava.app.jwtSecret}")
private String jwtSecret;
@Value("${loizenjava.app.jwtExpiration}")
private int jwtExpiration;
public String generateJwtToken(Authentication authentication) {
UserPrinciple userPrincipal = (UserPrinciple) authentication.getPrincipal();
return Jwts.builder()
.setSubject((userPrincipal.getUsername()))
.setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpiration*1000))
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
logger.error("Invalid JWT signature -> Message: {} ", e);
} catch (MalformedJwtException e) {
logger.error("Invalid JWT token -> Message: {}", e);
} catch (ExpiredJwtException e) {
logger.error("Expired JWT token -> Message: {}", e);
} catch (UnsupportedJwtException e) {
logger.error("Unsupported JWT token -> Message: {}", e);
} catch (IllegalArgumentException e) {
logger.error("JWT claims string is empty -> Message: {}", e);
}
return false;
}
public String getUserNameFromJwtToken(String token) {
return Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody().getSubject();
}
}
Implement RestControllers
Create Payload Message
LoginForm.java
contains username
& password
->
package com.loizenjava.jwtauthentication.message.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
public class LoginForm {
@NotBlank
@Size(min=3, max = 60)
private String username;
@NotBlank
@Size(min = 6, max = 40)
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
– SignUpForm.java
contains:
- name
- username
- role
- password
package com.loizenjava.jwtauthentication.message.request;
import java.util.Set;
import javax.validation.constraints.*;
public class SignUpForm {
@NotBlank
@Size(min = 3, max = 50)
private String name;
@NotBlank
@Size(min = 3, max = 50)
private String username;
@NotBlank
@Size(max = 60)
@Email
private String email;
private Set role;
@NotBlank
@Size(min = 6, max = 40)
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set getRole() {
return this.role;
}
public void setRole(Set role) {
this.role = role;
}
}
– JwtResponse.java
is returned by SpringBoot server after successful authentication, it contains 2 parts:
- JWT Token
- Schema Type of Token
package com.loizenjava.jwtauthentication.message.response;
public class JwtResponse {
private String token;
private String type = "Bearer";
public JwtResponse(String accessToken) {
this.token = accessToken;
}
public String getAccessToken() {
return token;
}
public void setAccessToken(String accessToken) {
this.token = accessToken;
}
public String getTokenType() {
return type;
}
public void setTokenType(String tokenType) {
this.type = tokenType;
}
}
RestAPIs Controller
– AuthRestAPIs.java
defines 2 APIs:
/api/auth/signup
: sign up
-> check username/email is already in use.
-> create User object
-> store to database/api/auth/signin
: sign in
-> attempt to authenticate withAuthenticationManager
bean.
-> add authentication object toSecurityContextHolder
-> Generate JWT token, then return JWT to client
package com.loizenjava.jwtauthentication.controller;
import java.util.HashSet;
import java.util.Set;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.loizenjava.jwtauthentication.message.request.LoginForm;
import com.loizenjava.jwtauthentication.message.request.SignUpForm;
import com.loizenjava.jwtauthentication.message.response.JwtResponse;
import com.loizenjava.jwtauthentication.model.Role;
import com.loizenjava.jwtauthentication.model.RoleName;
import com.loizenjava.jwtauthentication.model.User;
import com.loizenjava.jwtauthentication.repository.RoleRepository;
import com.loizenjava.jwtauthentication.repository.UserRepository;
import com.loizenjava.jwtauthentication.security.jwt.JwtProvider;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthRestAPIs {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;
@Autowired
PasswordEncoder encoder;
@Autowired
JwtProvider jwtProvider;
@PostMapping("/signin")
public ResponseEntity> authenticateUser(@Valid @RequestBody LoginForm loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtProvider.generateJwtToken(authentication);
return ResponseEntity.ok(new JwtResponse(jwt));
}
@PostMapping("/signup")
public ResponseEntity registerUser(@Valid @RequestBody SignUpForm signUpRequest) {
if(userRepository.existsByUsername(signUpRequest.getUsername())) {
return new ResponseEntity("Fail -> Username is already taken!",
HttpStatus.BAD_REQUEST);
}
if(userRepository.existsByEmail(signUpRequest.getEmail())) {
return new ResponseEntity("Fail -> Email is already in use!",
HttpStatus.BAD_REQUEST);
}
// Creating user's account
User user = new User(signUpRequest.getName(), signUpRequest.getUsername(),
signUpRequest.getEmail(), encoder.encode(signUpRequest.getPassword()));
Set strRoles = signUpRequest.getRole();
Set roles = new HashSet<>();
strRoles.forEach(role -> {
switch(role) {
case "admin":
Role adminRole = roleRepository.findByName(RoleName.ROLE_ADMIN)
.orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
roles.add(adminRole);
break;
case "pm":
Role pmRole = roleRepository.findByName(RoleName.ROLE_PM)
.orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
roles.add(pmRole);
break;
default:
Role userRole = roleRepository.findByName(RoleName.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
roles.add(userRole);
}
});
user.setRoles(roles);
userRepository.save(user);
return ResponseEntity.ok().body("User registered successfully!");
}
}package com.loizenjava.jwtauthentication.controller;
import java.util.HashSet;
import java.util.Set;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.loizenjava.jwtauthentication.message.request.LoginForm;
import com.loizenjava.jwtauthentication.message.request.SignUpForm;
import com.loizenjava.jwtauthentication.message.response.JwtResponse;
import com.loizenjava.jwtauthentication.model.Role;
import com.loizenjava.jwtauthentication.model.RoleName;
import com.loizenjava.jwtauthentication.model.User;
import com.loizenjava.jwtauthentication.repository.RoleRepository;
import com.loizenjava.jwtauthentication.repository.UserRepository;
import com.loizenjava.jwtauthentication.security.jwt.JwtProvider;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthRestAPIs {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;
@Autowired
PasswordEncoder encoder;
@Autowired
JwtProvider jwtProvider;
@PostMapping("/signin")
public ResponseEntity> authenticateUser(@Valid @RequestBody LoginForm loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtProvider.generateJwtToken(authentication);
return ResponseEntity.ok(new JwtResponse(jwt));
}
@PostMapping("/signup")
public ResponseEntity registerUser(@Valid @RequestBody SignUpForm signUpRequest) {
if(userRepository.existsByUsername(signUpRequest.getUsername())) {
return new ResponseEntity("Fail -> Username is already taken!",
HttpStatus.BAD_REQUEST);
}
if(userRepository.existsByEmail(signUpRequest.getEmail())) {
return new ResponseEntity("Fail -> Email is already in use!",
HttpStatus.BAD_REQUEST);
}
// Creating user's account
User user = new User(signUpRequest.getName(), signUpRequest.getUsername(),
signUpRequest.getEmail(), encoder.encode(signUpRequest.getPassword()));
Set strRoles = signUpRequest.getRole();
Set roles = new HashSet<>();
strRoles.forEach(role -> {
switch(role) {
case "admin":
Role adminRole = roleRepository.findByName(RoleName.ROLE_ADMIN)
.orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
roles.add(adminRole);
break;
case "pm":
Role pmRole = roleRepository.findByName(RoleName.ROLE_PM)
.orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
roles.add(pmRole);
break;
default:
Role userRole = roleRepository.findByName(RoleName.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
roles.add(userRole);
}
});
user.setRoles(roles);
userRepository.save(user);
return ResponseEntity.ok().body("User registered successfully!");
}
}
– TestRestAPIs
define 3 RestAPIs:
/api/test/user
-> access by users hasUSER_ROLE
orADMIN_ROLE
/api/test/pm
-> access by users hasUSER_PM
orADMIN_ROLE
/api/test/admin
-> access by users hasADMIN_ROLE
package com.loizenjava.jwtauthentication.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestRestAPIs {
@GetMapping("/api/test/user")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public String userAccess() {
return ">>> User Contents!";
}
@GetMapping("/api/test/pm")
@PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
public String projectManagementAccess() {
return ">>> Board Management Project";
}
@GetMapping("/api/test/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return ">>> Admin Contents";
}
}
Application Properties
– Configure for MySQL database:
spring.datasource.url=jdbc:mysql://localhost:3306/loizenjavadb
spring.datasource.username=root
spring.datasource.password=12345
spring.jpa.generate-ddl=true
# App Properties
loizenjava.app.jwtSecret=jwtLoizenai.comSecretKey
loizenjava.app.jwtExpiration=86400
– Configure for PostgreSQL database:
## PostgreSQL
spring.datasource.url=jdbc:postgresql://localhost:5432/loizenjavadb
spring.datasource.username=postgres
spring.datasource.password=123
#drop & create table again, good for testing, comment this in production
spring.jpa.hibernate.ddl-auto=create
Run & Check Results
Start SpringBoot
– Start Springboot server by commandline mvn spring-boot:run
– Check database tables:

– Insert data to roles table :
INSERT INTO roles(name) VALUES('ROLE_USER');
INSERT INTO roles(name) VALUES('ROLE_PM');
INSERT INTO roles(name) VALUES('ROLE_ADMIN');
SignUp
Sign-Up 3 users:
- Jack has
ROLE_USER
role - Adam has
ROLE_PM
&ROLE_USER
roles - Thomas has
ROLE_ADMIN
role

– Check database’s tables:

SignIn and Access Protected Resources
– Jack can access api/test/user
url, can NOT access others.
-> Sign In:

Access Protected Resources:


– Adam
can access api/test/user
and api/test/pm
url.
Can NOT access /api/test/admin
url.
-> Sign In:

-> Access Protected Resources:



– Thomas can access all URLs.
-> Sign In:

-> Access Protected Resource:

Sourcecode
All features of sourcecode for the tutorial Springboot Token Based Authentication Example – MySQL/PostgreSQL + Spring JPA + RestAPIs:
- Implement JPA Repository
- Implement SpringBoot JWT Security: UserDetails Service & JWT Authentication classes
- Implement Protected RestControllers
SpringBootJwtAuthenticationExamples
– GitHub Sourcecode:
SpringBootJwtAuthenticationExamples – GitHub Sourcecode
Related posts
Read More:
Wow! LOve friends!
Very Nice Post
How can I do it with SpringBoot Angular and MongoDB, please post it with video guide
Hello man, Please integrate it with Reactjs + SpringBOot
Thank for the right post I’m on the way to find for my project! Keep do more pro
Good representation and details about Spring JWT Authentication
Love the way you share! So hard and details ! Good luck man
Great post pro!