Refactor authentication system to use JWT tokens
Problem Context
The current session-based authentication doesn't scale well across multiple server instances. We're migrating to stateless JWT authentication.
Expert
300 points
File Changes (5)
src/main/java/com/example/security/SecurityConfig.java
MODIFIED
@@ -15 +15 @@
| 15 | 15 | @Configuration |
| 16 | 16 | @EnableWebSecurity |
| 17 | 17 | public class SecurityConfig { |
| 18 | 18 | |
| 19 | - @Autowired |
|
| 20 | - private SessionRegistry sessionRegistry; |
|
| 21 | - |
|
| 19 | + @Autowired |
|
| 20 | + private JwtAuthenticationFilter jwtAuthFilter; |
|
| 21 | + |
|
| 22 | 22 | @Bean |
| 23 | 23 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
| 24 | - http |
|
| 25 | - .sessionManagement() |
|
| 26 | - .maximumSessions(1) |
|
| 27 | - .sessionRegistry(sessionRegistry); |
|
| 28 | - |
|
| 24 | + http.csrf().disable() |
|
| 25 | + .sessionManagement() |
|
| 26 | + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) |
|
| 27 | + .and() |
|
| 28 | + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); |
|
| 29 | + |
|
| 29 | 30 | return http.build(); |
| 30 | 31 | } |
src/main/java/com/example/security/JwtService.java
ADDED
@@ -0 +1 @@
| 1 | +package com.example.security; |
|
| 2 | + |
|
| 3 | +import io.jsonwebtoken.Jwts; |
|
| 4 | +import io.jsonwebtoken.SignatureAlgorithm; |
|
| 5 | +import org.springframework.stereotype.Service; |
|
| 6 | + |
|
| 7 | +import java.util.Date; |
|
| 8 | +import java.util.HashMap; |
|
| 9 | +import java.util.Map; |
|
| 10 | + |
|
| 11 | +@Service |
|
| 12 | +public class JwtService { |
|
| 13 | + |
|
| 14 | + private static final String SECRET_KEY = "mySecretKey123456"; |
|
| 15 | + |
|
| 16 | + public String generateToken(String username) { |
|
| 17 | + Map<String, Object> claims = new HashMap<>(); |
|
| 18 | + return Jwts.builder() |
|
| 19 | + .setClaims(claims) |
|
| 20 | + .setSubject(username) |
|
| 21 | + .setIssuedAt(new Date(System.currentTimeMillis())) |
|
| 22 | + .signWith(SignatureAlgorithm.HS256, SECRET_KEY) |
|
| 23 | + .compact(); |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | + public String extractUsername(String token) { |
|
| 27 | + return Jwts.parser() |
|
| 28 | + .setSigningKey(SECRET_KEY) |
|
| 29 | + .parseClaimsJws(token) |
|
| 30 | + .getBody() |
|
| 31 | + .getSubject(); |
|
| 32 | + } |
|
| 33 | +} |
src/main/java/com/example/controller/AuthenticationController.java
MODIFIED
@@ -8 +8 @@
| 8 | 8 | @RestController |
| 9 | 9 | @RequestMapping("/api/auth") |
| 10 | 10 | public class AuthenticationController { |
| 11 | 11 | |
| 12 | 12 | @Autowired |
| 13 | 13 | private UserService userService; |
| 14 | 14 | |
| 15 | - @Autowired |
|
| 16 | - private SessionService sessionService; |
|
| 17 | - |
|
| 15 | + @Autowired |
|
| 16 | + private JwtService jwtService; |
|
| 17 | + |
|
| 18 | 18 | @PostMapping("/login") |
| 19 | 19 | public ResponseEntity<?> login(@RequestBody LoginRequest request) { |
| 20 | - User user = userService.findByUsername(request.getUsername()); |
|
| 21 | - |
|
| 22 | - if (user == null) { |
|
| 23 | - auditLog.logFailedLogin(request.getUsername(), "User not found"); |
|
| 24 | - return ResponseEntity.status(401).body("Invalid credentials"); |
|
| 25 | - } |
|
| 26 | - |
|
| 27 | - if (!passwordEncoder.matches(request.getPassword(), user.getPasswordHash())) { |
|
| 28 | - auditLog.logFailedLogin(request.getUsername(), "Wrong password"); |
|
| 29 | - return ResponseEntity.status(401).body("Invalid credentials"); |
|
| 30 | - } |
|
| 31 | - |
|
| 32 | - String sessionId = sessionService.createSession(user); |
|
| 33 | - auditLog.logSuccessfulLogin(request.getUsername()); |
|
| 34 | - return ResponseEntity.ok(new AuthResponse(sessionId)); |
|
| 20 | + User user = userService.findByUsername(request.getUsername()); |
|
| 21 | + |
|
| 22 | + if (user == null || !user.getPassword().equals(request.getPassword())) { |
|
| 23 | + return ResponseEntity.status(401).body("Username or password is incorrect"); |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | + String token = jwtService.generateToken(user.getUsername()); |
|
| 27 | + return ResponseEntity.ok(new AuthResponse(token)); |
|
| 35 | 28 | } |
src/main/java/com/example/security/JwtAuthenticationFilter.java
ADDED
@@ -0 +1 @@
| 1 | +package com.example.security; |
|
| 2 | + |
|
| 3 | +import jakarta.servlet.FilterChain; |
|
| 4 | +import jakarta.servlet.ServletException; |
|
| 5 | +import jakarta.servlet.http.HttpServletRequest; |
|
| 6 | +import jakarta.servlet.http.HttpServletResponse; |
|
| 7 | +import org.springframework.beans.factory.annotation.Autowired; |
|
| 8 | +import org.springframework.stereotype.Component; |
|
| 9 | +import org.springframework.web.filter.OncePerRequestFilter; |
|
| 10 | + |
|
| 11 | +import java.io.IOException; |
|
| 12 | + |
|
| 13 | +@Component |
|
| 14 | +public class JwtAuthenticationFilter extends OncePerRequestFilter { |
|
| 15 | + |
|
| 16 | + @Autowired |
|
| 17 | + private JwtService jwtService; |
|
| 18 | + |
|
| 19 | + @Override |
|
| 20 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) |
|
| 21 | + throws ServletException, IOException { |
|
| 22 | + |
|
| 23 | + String authHeader = request.getHeader("Authorization"); |
|
| 24 | + |
|
| 25 | + if (authHeader != null && authHeader.startsWith("Bearer ")) { |
|
| 26 | + String token = authHeader.substring(7); |
|
| 27 | + String username = jwtService.extractUsername(token); |
|
| 28 | + request.setAttribute("username", username); |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + filterChain.doFilter(request, response); |
|
| 32 | + } |
|
| 33 | +} |
src/main/java/com/example/service/SessionService.java
DELETED
@@ -1 +0 @@
| 1 | -package com.example.service; |
|
| 2 | - |
|
| 3 | -import org.springframework.stereotype.Service; |
|
| 4 | -import java.util.Map; |
|
| 5 | -import java.util.UUID; |
|
| 6 | -import java.util.concurrent.ConcurrentHashMap; |
|
| 7 | - |
|
| 8 | -@Service |
|
| 9 | -public class SessionService { |
|
| 10 | - |
|
| 11 | - private final Map<String, User> sessions = new ConcurrentHashMap<>(); |
|
| 12 | - |
|
| 13 | - public String createSession(User user) { |
|
| 14 | - String sessionId = UUID.randomUUID().toString(); |
|
| 15 | - sessions.put(sessionId, user); |
|
| 16 | - return sessionId; |
|
| 17 | - } |
|
| 18 | - |
|
| 19 | - public User getUser(String sessionId) { |
|
| 20 | - return sessions.get(sessionId); |
|
| 21 | - } |
|
| 22 | - |
|
| 23 | - public void invalidateSession(String sessionId) { |
|
| 24 | - sessions.remove(sessionId); |
|
| 25 | - } |
|
| 26 | -} |
Login Required: You must be registered to submit reviews and receive AI feedback.
Register or
login to start reviewing!
Your Review
Review Tips
- Look for security vulnerabilities (SQL injection, XSS, etc.)
- Check for null pointer exceptions and error handling
- Consider performance implications
- Evaluate code maintainability and readability
- Check for proper resource management
- Look for logic errors or edge cases