DEV Community

Cover image for Let’s Implement Basic JWT Based Authentication in Spring boot
Nil Madhab
Nil Madhab

Posted on • Edited on • Originally published at simplecoding.dev

5

Let’s Implement Basic JWT Based Authentication in Spring boot

Step by step guild how to implement JWT Based Authentication, Part 1

Photo by [NeONBRAND](https://unsplash.com/@neonbrand?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

JWT based authentication is very useful when we have web and mobile clients and a backend server, which provides a good alternative to session-based authentication. I did not find a good tutorial on this topic, so I decided to make one.

Theory

Video Tutorial

I found this awesome tutorial for implementing basic JWT based authentication for noobs like me.


In this tutorial, we will implement basic JWT based authentication, with the mock user and no database, to understand the concept. We will extend it later to integrate database and full signup, login functionalities.

If you just want to check out the code, checkout the Github branch.

Features we will develop

  1. JWT util file to create and validate JWT token

  2. A controller to generate JWT token

  3. User will be hardcoded

  4. No password encoder

Youtube Demo

I have created a short video of 7 min, to explain all the component, if you do not have time to watch the full 40 min Javabrains video, or you already know about JWT auth, but do not know how to implement it in spring.

Step 1

Create a Spring project with the following dependency in pom.xml

<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.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
view raw jwt_pom.xml hosted with ❤ by GitHub

Step 2

Create a custom MyUserDetailsService service, which extend UserDetailsService of security.core package and override the loadUserByUsername by hardcoding an user, for the sake of simplicity.

We will store and retrieve user from the database in a later tutorial.

package com.simplecoding.jwt.service;
import org.springframework.security.core.userdetails.User;
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 java.util.ArrayList;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User("foo", "foo", new ArrayList<>());
}
}

Step 3

Create a SecurityConfigurer file and enable EnableWebSecurity annotation

Things to note

  1. We did not use any password encoder in this tutorial. We will use BCrypt encoding in next tutorial

  2. Antmatcher means except the ‘/authenticate’ endpoint, all other apis will be authenticated

  3. We are using a stateless session creation policy, i.e every api will be authenticated.

  4. we are providing our implementation of Userdetail service to Spring AuthenticationManagerBuilder

    package com.simplecoding.jwt.config;
    import com.simplecoding.jwt.service.MyUserDetailsService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    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.password.NoOpPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    @EnableWebSecurity
    public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailsService myUserDetailService;
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    super.configure(auth);
    auth.userDetailsService(myUserDetailService);
    }
    @Override
    public void configure(HttpSecurity security) throws Exception {
    security.csrf().disable()
    .authorizeRequests().antMatchers("/authenticate").permitAll()
    .anyRequest().authenticated().and().sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    ;
    security.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
    return NoOpPasswordEncoder.getInstance();
    }
    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
    return super.authenticationManager();
    }
    }

    Step 4

Create the JWT util file to validate and create tokens. Check out the video (forward to 10:00) above to know more about each function.

package com.simplecoding.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Service
public class JwtUtil {
private String SECRET_KEY = "secret";
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
view raw JwtUtil.java hosted with ❤ by GitHub

Step 5

Create a filter chain to extract the JWT token from Authorization header, validate the token, and set the authentication in a security context.

package com.simplecoding.jwt.config;
import com.simplecoding.jwt.JwtUtil;
import com.simplecoding.jwt.service.MyUserDetailsService;
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.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
JwtUtil jwtUtil;
@Autowired
MyUserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String jwtToken = null;
String username = null;
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
jwtToken = bearerToken.substring(7, bearerToken.length());
username = jwtUtil.extractUsername(jwtToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
}
filterChain.doFilter(request, response);
}
}

Step 6

Create the controller to test the authentication

package com.simplecoding.jwt;
import com.simplecoding.jwt.payloads.AuthenticationRequest;
import com.simplecoding.jwt.payloads.AuthenticationResponse;
import com.simplecoding.jwt.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
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.userdetails.UserDetails;
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;
@RestController
public class AuthController {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
MyUserDetailsService userDetailsService;
@Autowired
JwtUtil jwtUtil;
@RequestMapping("/hello")
public String hello(){
return "Nil here";
}
@PostMapping("/authenticate")
public ResponseEntity<?> authenticate(@RequestBody AuthenticationRequest authenticationRequest) {
Authentication authenticate = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);
UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String jwt = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(new AuthenticationResponse(jwt));
}
}

Authenticate

Access an API with JWT token

In the next tutorial, we will hook this with real users in the MySQL database and implement signup/login functionality.

Thanks for reading :)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (1)

Collapse
 
soshihomma profile image
SoshiHomma

As a note: you need AuthenticationRequest and AuthenticationResponse as a payload.
Check it out from his github: github.com/webtutsplus/spring-auth...

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay