Add refresh token generation

This commit is contained in:
Dmitrii Cravcenco
2023-10-12 12:52:39 +03:00
parent b1b13dc736
commit 3160a19fa5
8 changed files with 81 additions and 24 deletions

View File

@@ -0,0 +1,19 @@
package com.faf223.expensetrackerfaf.config;
import com.faf223.expensetrackerfaf.controller.auth.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
public class ExceptionHandlers {
@ExceptionHandler(TokenExpiredException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ResponseBody
public ErrorResponse handleTokenExpiredException(TokenExpiredException ex) {
return new ErrorResponse("Unauthorized", ex.getMessage());
}
}

View File

@@ -1,5 +1,6 @@
package com.faf223.expensetrackerfaf.config;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@@ -15,7 +16,6 @@ import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@@ -35,23 +35,28 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
}
final String authHeader = request.getHeader("Authorization");
final String jwt;
final String userEmail;
String userEmail;
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwt = authHeader.substring(7);
try {
userEmail = jwtService.extractUsername(jwt);
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
if (jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource()
.buildDetails(request));
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
} catch (ExpiredJwtException e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
filterChain.doFilter(request, response);
}
}

View File

@@ -38,6 +38,10 @@ public class JwtService {
return generateToken(new HashMap<>(), userDetails);
}
public String generateRefreshToken(UserDetails userDetails) {
return generateRefreshToken(new HashMap<>(), userDetails);
}
public String generateToken(
Map<String, Object> extraClaims,
UserDetails userDetails
@@ -45,6 +49,13 @@ public class JwtService {
return buildToken(extraClaims, userDetails, jwtExpiration);
}
public String generateRefreshToken(
Map<String, Object> extraClaims,
UserDetails userDetails
) {
return buildToken(extraClaims, userDetails, refreshExpiration);
}
private String buildToken(Map<String, Object> extraClaims, UserDetails userDetails, long expiration) {
return Jwts
.builder()

View File

@@ -19,6 +19,7 @@ public class SecurityConfiguration {
private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
// private final Http401UnauthorizedEntryPoint entryPoint;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
@@ -29,6 +30,7 @@ public class SecurityConfiguration {
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// .exceptionHandling((e) -> e.authenticationEntryPoint(entryPoint))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); // will be executed before UsernamePasswordAuthenticationFilter

View File

@@ -0,0 +1,7 @@
package com.faf223.expensetrackerfaf.config;
public class TokenExpiredException extends RuntimeException {
public TokenExpiredException(String message) {
super(message);
}
}

View File

@@ -1,19 +1,17 @@
package com.faf223.expensetrackerfaf.controller.auth;
import com.faf223.expensetrackerfaf.service.AuthenticationService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("api/v1/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService service;
public AuthenticationController(AuthenticationService service) {
this.service = service;
}
@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> register(@RequestBody RegisterRequest request) {
return ResponseEntity.ok(service.register(request));
@@ -24,8 +22,8 @@ public class AuthenticationController {
return ResponseEntity.ok(service.authenticate(request));
}
@PostMapping("/refresh")
public ResponseEntity<AuthenticationResponse> refreshAccessToken(@RequestBody TokenRefreshRequest refreshRequest) {
return ResponseEntity.ok(service.refreshAccessToken(refreshRequest));
@PostMapping("/refreshtoken")
public ResponseEntity<AuthenticationResponse> refreshAccessToken(@RequestBody TokenRefreshRequest request) {
return ResponseEntity.ok(service.refreshAccessToken(request));
}
}

View File

@@ -0,0 +1,16 @@
package com.faf223.expensetrackerfaf.controller.auth;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ErrorResponse {
private String error;
private String message;
public ErrorResponse(String error, String message) {
this.error = error;
this.message = message;
}
}

View File

@@ -18,7 +18,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Optional;
@Service
@@ -45,7 +44,7 @@ public class AuthenticationService {
UserDetails userDetails = new PersonDetails(credential);
String jwtToken = jwtService.generateToken(userDetails);
String refreshToken = jwtService.generateToken(userDetails);
String refreshToken = jwtService.generateRefreshToken(userDetails);
return AuthenticationResponse.builder()
.accessToken(jwtToken)
@@ -60,7 +59,7 @@ public class AuthenticationService {
UserDetails userDetails = new PersonDetails(credential);
String jwtToken = jwtService.generateToken(userDetails);
String refreshToken = jwtService.generateToken(userDetails);
String refreshToken = jwtService.generateRefreshToken(userDetails);
return AuthenticationResponse.builder()
.accessToken(jwtToken)
.refreshToken(refreshToken)
@@ -77,7 +76,7 @@ public class AuthenticationService {
String jwtToken = jwtService.generateToken(userDetails);
return AuthenticationResponse.builder()
.accessToken(jwtToken)
.refreshToken(refreshToken) // Return the same refresh token
.refreshToken(refreshToken)
.build();
} else {
throw new RuntimeException("Invalid or expired refresh token");