Implementing Login/Logout with JWT and Token API
Recently, the architecture of web applications has become increasingly complex, and security issues are a significant concern for all developers. In this course, we will take a detailed look at how to implement login and logout features based on JWT (JSON Web Token) and the token API using Spring Boot. This course is targeted at developers with basic Spring Boot development experience and includes an understanding of JWT.
Table of Contents
- 1. What is JWT?
- 2. Project Setup
- 3. User Authentication and JWT Issuance
- 4. Token Verification and Authorization
- 5. Implementing Logout Functionality
- 6. API Testing
- 7. Conclusion
1. What is JWT?
JWT stands for JSON Web Token, an open standard (RFC 7519) for securely transmitting user authentication information. JWT is one of the most commonly used authentication methods, used to prove that a user is logged in without the need to maintain state between the client and server. JWT consists of three parts:
- Header: Specifies the type of token and the signing algorithm.
- Payload: Contains user information and claims.
- Signature: A signature created based on the header and payload.
One of the main advantages of JWT is that it allows for stateless authentication. This means that the server does not store session information, thereby reducing the server’s burden and making it easier to scale out.
2. Project Setup
Before starting the project, you need to create a Spring Boot project. You can easily set up a project using Spring Initializr. Add the following dependencies:
- Spring Web
- Spring Security
- Spring Data JPA
- H2 Database (or any database of your choice)
- jjwt (Java JWT library)
After creating the project, add the necessary libraries to your `pom.xml`:
io.jsonwebtoken
jjwt
0.9.1
3. User Authentication and JWT Issuance
To authenticate users, create a User entity and UserRepository. Then, set up the service layer and implement the logic to generate JWT.
// Example of User Entity
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
// getters and setters
}
// Example of UserRepository
public interface UserRepository extends JpaRepository {
Optional findByUsername(String username);
}
Now, let’s add the functionality to create JWT:
// JwtUtil.java
@Component
public class JwtUtil {
private String secretKey = "secret"; // In production, manage this in environment variables or separate files
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
}
3.1 Implementing Login API
Now we will implement a Controller to handle login requests:
// AuthController.java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserRepository userRepository;
@PostMapping("/login")
public ResponseEntity login(@RequestBody User user) {
authenticate(user.getUsername(), user.getPassword());
String token = jwtUtil.generateToken(user.getUsername());
return ResponseEntity.ok(token);
}
private void authenticate(String username, String password) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
4. Token Verification and Authorization
To handle authenticated requests using JWT, you need to implement a filter to validate the token in the request header.
// JwtRequestFilter.java
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
5. Implementing Logout Functionality
Since JWT is stateless, implementing logout functionality means that the client simply deletes the token. However, it is also possible to implement logout functionality by managing a blacklist on the server.
// Example of Logout
@PostMapping("/logout")
public ResponseEntity logout(HttpServletRequest request) {
String token = request.getHeader("Authorization").substring(7);
// Implement logic to add to blacklist here
return ResponseEntity.ok("Logout successful");
}
6. API Testing
You can test the API using tools like Postman. After the login request, confirm that you can access protected resources using the JWT issued by the server.
7. Conclusion
In this course, we learned how to implement a JWT-based authentication system using Spring Boot. Depending on the system requirements, JWT can be utilized in various ways, and security should always remain a key consideration. Going forward, consider adding more advanced features to expand the system.