In this tutorial, we’re gonna look at how to use Spring Security for JWT Authentication in Spring Boot 2 that helps us secure our REST APIs.
Contents
- JWT (JSON Web Token)
- Spring Security JWT in Spring Boot 2
- Receive HTTP Request
- Filter the Request
- Create AuthenticationToken from Token
- Store Authentication object in SecurityContext
- Delegate AuthenticationToken for AuthenticationManagager
- Authenticate with AuthenticationProvider
- Retrieve User details with UserDetailsService
- Implement Repositories for MySQL database
- Get GrantedAuthority
- Protect Resources with HTTPSecurity & Method Security Expressions
- Handle AuthenticationException – AuthenticationEntryPoint
- Implement Spring Boot JWT MySQL project
- Further Reading
JWT (JSON Web Token)
JWT is popular for Authentication and Information Exchange. Server encodes data into a JSON Web Token and send it to the Client. The Client saves the JWT, then every Request from Client to protected routes or resources should be attached that JWT (commonly at header). The Server will validate that JWT and return the Response.
The big advantage of JWT (Token-based Authentication) is that we store the Token on Client side: Local Storage for Browser, Keychain for IOS and SharedPreferences for Android… So we don’t need to build another backend project that supports Native Apps or an additional Authentication module for Native App users.
There are three important parts of a JWT: Header, Payload, Signature. Together they are combined to a standard structure: header.payload.signature
.
The Client typically attact JWT in Authorization header with Bearer prefix:
Authorization: Bearer [header].[payload].[signature]
For more details, you can visit:
In-depth Introduction to JWT-JSON Web Token
Spring Security JWT in Spring Boot 2
This is diagram for Spring Security/JWT 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
. UserDetailsService
works with MySQL database via Spring Data JPA.
– 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.
This is our Spring Boot application demo running with MySQL database and test Rest Apis with Postman.
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
**Note: WebSecurityConfigurerAdapter
is deprecated from Spring 2.7.0, you can check the source code for update. More details at:
WebSecurityConfigurerAdapter Deprecated in Spring Boot
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:
– 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 AuthenticationProvider
s, 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 GrantedAuthority
s.
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
}
}
In the code above, we get full custom User object using UserRepository
(implementation of Spring Data JPARepository
) to work with user data in MySQL, then we build a UserDetails
object using static build()
method.
Implement Repositories for MySQL database
Now, each model above needs a repository for persisting and accessing data. In repository package, we have 2 repositories.
There are 3 necessary methods that JpaRepository
supports.
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
}
RoleRepository
also extends JpaRepository
and provides a finder method.
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByName(ERole name);
}
Get GrantedAuthority
Another important method provided by Authentication is getAuthorities()
that provides an collection of GrantedAuthority
objects:
public interface Authentication extends Principal, Serializable {
Collection 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);
}
}
**Note: WebSecurityConfigurerAdapter
is deprecated from Spring 2.7.0, you can read following post for update:
WebSecurityConfigurerAdapter Deprecated in Spring Boot
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");
}
}
Implement Spring Boot JWT MySQL project
Here is the project structure that we’re gonna build:
This is our Spring Boot application demo running with MySQL database and test Rest Apis with Postman.
The implementation and source code can be found at:
Spring Boot Token based Authentication with Spring Security & JWT
Or you can find way to make authentication with MongoDB database:
Spring Boot, MongoDB: JWT Authentication with Spring Security
Or PostgreSQL:
Spring Boot, Spring Security, PostgreSQL: JWT Authentication example
**Note: WebSecurityConfigurerAdapter
is deprecated from Spring 2.7.0, you can check the source code for update. More details at:
WebSecurityConfigurerAdapter Deprecated in Spring Boot
Further Reading
- Spring Security Documentation
- In-depth Introduction to JWT-JSON Web Token
- Spring Boot + Swagger 3 example (with OpenAPI 3)
Thanks!
Hello! I liked a lot of your content. Can you say what is common when we use REST requisitions with tokens for confirming a user? When we use it, we always do a new query to the database to confirm the user. Is it the most common? I am really confused by this detail. This seems to consume a lot of resources. I believe that with your experience you can help us. Thank you excellent post skin
// 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));
This tutorial is actually good, it helps new developers who use Spring Security. Thanks!
I’m definitely enjoying what you write in this Spring Security tutorial!
Fantastic blog and fantastic content.
Thank you for sharing this helpful tutorial with us.
It’s difficult to find experienced people for this Spring Boot Security topic, but you give me good explanation! Thanks!
Your posts are constructed clearly and have complex explanation, JWT based security is now easy for me, thanks!
The clarity in your post is merely cool and I could assume you are an expert on this subject matter.
Thanks a lot a million!