In this post I show the usage of Spring Security with the JWT base authentication. For that, I use the HTTP filters and a service Provider for the creation and validation of the JWT.
Content:
- The configuration of Spring Security;
- The HTTP filters;
- The creation and validation of a JWT.
Check this explanatory video for more details.
All the code of the article is available in the following repository.
In a previous article, I’ve already added some controllers and services. But the are unprotected. Now, I need to configure Spring Security to protect them.
I will start configuring the CORS, with the allowed headers and the HTTP methods.
@Configuration
@EnableWebMvc
public class WebConfig {
private static final Long MAX_AGE = 3600L;
private static final int CORS_FILTER_ORDER = -102;
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.setAllowedHeaders(Arrays.asList(
HttpHeaders.AUTHORIZATION,
HttpHeaders.CONTENT_TYPE,
HttpHeaders.ACCEPT));
config.setAllowedMethods(Arrays.asList(
HttpMethod.GET.name(),
HttpMethod.POST.name(),
HttpMethod.PUT.name(),
HttpMethod.DELETE.name()));
config.setMaxAge(MAX_AGE);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
// should be set order to -100 because we need to CorsFilter before SpringSecurityFilter
bean.setOrder(CORS_FILTER_ORDER);
return bean;
}
}
The configuration of Spring Security
Let’s continue adding the Maven dependency for Spring Security.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
And now, in the security configuration, i will need the following:
- An entry point to handle the authentication exceptions, this is not mandatory but i prefer to return a proper message than a stacktrace;
- An HTTP filter dedicated to handle the regular authentication, the login and password authentication;
- An HTTP filter dedicated to handle the authentication with JWT;
- Indicate that the session will never be created, as i am in a stateless application. Which endpoints doesn’t require the authentication, and the remaining will require the authentication;
- And i will create a component, a provider, which will validate the token and communicate with the authentication service to validate the login and password.
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserAuthenticationEntryPoint userAuthenticationEntryPoint;
private final UserAuthenticationProvider userAuthenticationProvider;
public SecurityConfig(UserAuthenticationEntryPoint userAuthenticationEntryPoint,
UserAuthenticationProvider userAuthenticationProvider) {
this.userAuthenticationEntryPoint = userAuthenticationEntryPoint;
this.userAuthenticationProvider = userAuthenticationProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().authenticationEntryPoint(userAuthenticationEntryPoint)
.and()
.addFilterBefore(new UsernamePasswordAuthFilter(userAuthenticationProvider), BasicAuthenticationFilter.class)
.addFilterBefore(new JwtAuthFilter(userAuthenticationProvider), UsernamePasswordAuthFilter.class)
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/v1/signIn", "/v1/signUp").permitAll()
.anyRequest().authenticated();
}
}
Let’s create previously used classes.
The HTTP Filters
Why handle the authentication via HTTP filters?
This way, the authentication is not present in the code, in the controllers. The HTTP filters will be executed before reaching the controls. For the username/password authentication, i could have done in the controller, but for authentication based on a token, i must always pass through a process to validate the request is authenticated. The token must be sent in all the requests done by the frontend. It will be sent in a header. So, before reaching a controller, where it’s asking for some personal content, i must ensure that the request has the authentication information in the headers. If yes, let continue the request until the controller. And if it’s not correctly authenticated, return an exception.
Okay then, why to use an HTTP filter for the username/password authentication?
This way, i have a single way to authenticate a user: via the HTTP filters. I don’t have the logic splitted in several services. But the best advantage is the following.
@PostMapping("/signIn")
public ResponseEntity<UserDto> signIn(@AuthenticationPrincipal UserDto user) {
user.setToken(userAuthenticationProvider.createToken(user.getLogin()));
return ResponseEntity.ok(user);
}
The @AuthenticationPrincipal will inject the object of the authenticated user in my controller. Using the HTTP filters, i set into the security context the user information, the user object, and spring, with this annotation, will look for the user object in the security context. This way, having the user authenticated with any system type, JWT or password, i will always have the user object injected in my controllers.
Let’s go now with the filter dedicated to handle the regular authentication, the authentication via username and password.
public class UsernamePasswordAuthFilter extends OncePerRequestFilter {
private static final ObjectMapper MAPPER = new ObjectMapper();
private final UserAuthenticationProvider userAuthenticationProvider;
public UsernamePasswordAuthFilter(UserAuthenticationProvider userAuthenticationProvider) {
this.userAuthenticationProvider = userAuthenticationProvider;
}
@Override
protected void doFilterInternal(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
if ("/v1/signIn".equals(httpServletRequest.getServletPath())
&& HttpMethod.POST.matches(httpServletRequest.getMethod())) {
CredentialsDto credentialsDto = MAPPER.readValue(httpServletRequest.getInputStream(), CredentialsDto.class);
try {
SecurityContextHolder.getContext().setAuthentication(
userAuthenticationProvider.validateCredentials(credentialsDto));
} catch (RuntimeException e) {
SecurityContextHolder.clearContext();
throw e;
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
Let’s go now with the more interesting filter, the JWT filter.
public class JwtAuthFilter extends OncePerRequestFilter {
private final UserAuthenticationProvider userAuthenticationProvider;
public JwtAuthFilter(UserAuthenticationProvider userAuthenticationProvider) {
this.userAuthenticationProvider = userAuthenticationProvider;
}
@Override
protected void doFilterInternal(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
String header = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION);
if (header != null) {
String[] authElements = header.split(" ");
if (authElements.length == 2
&& "Bearer".equals(authElements[0])) {
try {
SecurityContextHolder.getContext().setAuthentication(
userAuthenticationProvider.validateToken(authElements[1]));
} catch (RuntimeException e) {
SecurityContextHolder.clearContext();
throw e;
}
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
Before going to the authentication provider service, let’s quickly see what the entry point contains, the entry point which handles the authentication exceptions.
@Component
public class UserAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
OBJECT_MAPPER.writeValue(response.getOutputStream(), new ErrorDto("Unauthorized path"));
}
}
And now, the expected service provider where the JWT is built and validated, and where the username/password is also validated.
The creation and validation of a JWT
@Component
public class UserAuthenticationProvider {
@Value("${security.jwt.token.secret-key:secret-key}")
private String secretKey;
private final AuthenticationService authenticationService;
public UserAuthenticationProvider(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
@PostConstruct
protected void init() {
// this is to avoid having the raw secret key available in the JVM
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
public String createToken(String login) {
Claims claims = Jwts.claims().setSubject(login);
Date now = new Date();
Date validity = new Date(now.getTime() + 3600000); // 1 hour
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
public Authentication validateToken(String token) {
String login = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody()
.getSubject();
UserDto user = authenticationService.findByLogin(login);
return new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList());
}
public Authentication validateCredentials(CredentialsDto credentialsDto) {
UserDto user = authenticationService.authenticate(credentialsDto);
return new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList());
}
}
The JWT is divided in three parts:
- The header which contains the information about the algorithm used to cipher the token, and what kind of token it is.
- Then comes the payload with the claims. The claims are some standard information you can find in the token, as the username, the expiration time, the creation date, and more. And i can have some custom claims.
- And the last part is the signature. For the signature, i use the header content, encoded in base64, i then use the payload content also encoded in base64, and encrypt the concatenation of both using the algorithm specified in the header. The resulting token will be: the header encoded in base64, followed by a dot, the payload encoded in base64, followed by a dot, and the signature.
| Decoded | Encoded |
| { “alg”: “HS256”, “typ”: “JWT” } | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 |
| { “sub”: “1234567890”, “name”: “John Doe”, “iat”: 1516239022 } | eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ |
| HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), “the-secret-key”) | SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c |
Hopefully i have a library to perform those actions for me.
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Conclusion
- I created the security configuration which defines the order of the HTTP filters, the exception handler with the entry point, the endpoints which don’t need authentication, and the session management;
- I’ve created two HTTP filters, one for the username/password authentication, and the other for JWT;
- I’ve created the entry point to handle the authentication exceptions, and return a custom response;
- I’ve created a service provider which builds the JWT, validates it, and validates the password;
- And finally, if the request is correctly authenticated, add the user information in the security context to have it available in the controllers via the annotation.
References
All the code of the article is available in the following repository.



Leave a reply to Roles Authorization – The Dev World – by Sergio Lema Cancel reply