- Мне не нужны определенные URL-адреса, такие как /api/auth/refresh-token, /api/auth/ access-token для прохождения класса фильтрации jwt, поскольку я хочу заменить старый просроченный/недействительный токен на более новый. Если мы не внесем в белый список, аутентификация всегда будет неудачной.
Прав ли я, внося эти URL-адреса в белый список для фильтрации jwt? - Правильно ли? использование метода mustNotFilter() класса OncePerRequestFilter уместно в данном случае или это нужно как-то сделать в классе SecurityConfiguration?
[*]Я получаю исключение TokenExpiration, когда пытаюсь извлечь имя пользователя из старого токена, что кажется очевидным, но как тогда я буду извлекать имя пользователя, которое будет использоваться для генерации нового токена?
[*]Должен ли я проверять, истек ли срок действия старого токена, прежде чем создавать новые токены, или мне все равно следует его генерировать, если клиент запрашивает его?
5.Должен ли я также генерировать новый токен доступа при создании нового токена обновления?
Класс SecurityConfiguration:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(registry -> {
registry.requestMatchers("/api/auth/**").permitAll();
registry.anyRequest().authenticated();
})
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.
STATELESS
))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin(AbstractAuthenticationFilterConfigurer::permitAll)
.build();
}@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(registry -> {
registry.requestMatchers("/api/auth/**").permitAll();
registry.anyRequest().authenticated();
})
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin(AbstractAuthenticationFilterConfigurer::permitAll)
.build();
}
Класс JwtAuthenticationFilter:
@Override
protected void doFilterInternal(@Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response,
@Nonnull FilterChain filterChain) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
final String jwt;
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwt = authHeader.substring(7);
String username = jwtService.extractUsername(jwt);
if (username != null && SecurityContextHolder.
getContext
().getAuthentication() == null) {
var user = userDetailsService.loadUserByUsername(username);
if (jwtService.isTokenValid(jwt, user)) {
if (jwtService.isTokenExpired(jwt)) {
throw new TokenExpiredException("Token expired");
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(
user,
null,
user.getAuthorities()
);
usernamePasswordAuthenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.
getContext
().setAuthentication(usernamePasswordAuthenticationToken);
} else {
response.setStatus(HttpServletResponse.
SC_UNAUTHORIZED
);
response.getWriter().write("Unauthorized: Invalid token");
return;
}
}
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return path.startsWith("/api/auth/refresh-token");
}
Метод класса JwtService (место, где я получаю исключение TokenExpired):
@Service
public class JwtService {
@Value("${jwt.secret.key}")
private String secretKey;
@Value("${jwt.access.expiration.time}")
private long accessTokenExpiryDate;
@Value("${jwt.refresh.expiration.time}")
private long refreshTokenExpiryDate;
@Value("${spring.application.name}")
private String issuer;
public String extractUsername(String token) {
return extractClaims(token, Claims::getSubject);
}
public String generateToken(UserDetails userDetails) {
return generateToken(new HashMap(), userDetails, accessTokenExpiryDate);
}
public String generateRefreshToken(UserDetails userDetails) {
return generateToken(new HashMap(), userDetails, refreshTokenExpiryDate);
}
public boolean isRefreshTokenValid(String token, UserDetails userDetails) {
return isTokenValid(token, userDetails);
}
public boolean isTokenValid(String token, UserDetails userDetails) {
final String username = extractUsername(token);
final String tokenIssuer = extractClaims(token, Claims::getIssuer);
return tokenIssuer.equalsIgnoreCase(issuer)
&& username.equalsIgnoreCase(userDetails.getUsername());
}
private String generateToken(Map extraClaims, UserDetails userDetails, long expiryDate) {
return Jwts.builder()
.id(UUID.randomUUID().toString())
.claim("authorities", Arrays.toString(userDetails.getAuthorities().toArray()))
.claims(extraClaims)
.issuer(issuer)
.subject(userDetails.getUsername())
.issuedAt(Date.from(Instant.now()))
.expiration(Date.from(Instant.now().plusMillis(expiryDate)))
.signWith(getSecretKey())
.compact();
}
public boolean isTokenExpired(String token) {
return extractExpiration(token).before(Date.from(Instant.now()));
}
private Date extractExpiration(String token) {
return extractClaims(token, Claims::getExpiration);
}
private Claims extractAllClaims(String token) {
return Jwts.parser()
.verifyWith(getSecretKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
private SecretKey getSecretKey() {
byte[] secretKeyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(secretKeyBytes);
}
private T extractClaims(String token, Function claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
}
Методы класса UserService:
public TokenResponse refreshToken(String oldRefreshToken) {
String jwt = oldRefreshToken.substring(7);
var username = jwtService.extractUsername(jwt);
var user = userRepository.findUserByUsernameEqualsIgnoreCase(username).orElseThrow(UserNotFoundException::new);
if (!jwtService.isRefreshTokenValid(jwt, user)) {
throw new TokenExpiredException("Refresh token is not expired yet");
}
var newRefreshToken = jwtService.generateRefreshToken(user);
var newAccessToken = jwtService.generateToken(user);
return new TokenResponse(newAccessToken, newRefreshToken);
}
public String accessToken(String refreshToken) {
var jwt = refreshToken.substring(7);
var username = jwtService.extractUsername(jwt);
var user = userRepository.findUserByUsernameEqualsIgnoreCase(username).orElseThrow(UserNotFoundException::new);
if (!jwtService.isRefreshTokenValid(jwt, user)) {
throw new TokenExpiredException("Refresh token is not expired yet");
}
return jwtService.generateToken(user);
}
public TokenResponse refreshToken(String oldRefreshToken) {
String jwt = oldRefreshToken.substring(7);
var username = jwtService.extractUsername(jwt);
var user = userRepository.findUserByUsernameEqualsIgnoreCase(username).orElseThrow(UserNotFoundException::new);
if (!jwtService.isRefreshTokenValid(jwt, user)) {
throw new TokenExpiredException("Refresh token is not expired yet");
}
var newRefreshToken = jwtService.generateRefreshToken(user);
var newAccessToken = jwtService.generateToken(user);
return new TokenResponse(newAccessToken, newRefreshToken);
}
public String accessToken(String refreshToken) {
var jwt = refreshToken.substring(7);
var username = jwtService.extractUsername(jwt);
var user = userRepository.findUserByUsernameEqualsIgnoreCase(username).orElseThrow(UserNotFoundException::new);
if (!jwtService.isRefreshTokenValid(jwt, user)) {
throw new TokenExpiredException("Refresh token is not expired yet");
}
return jwtService.generateToken(user);
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... s-possible
Мобильная версия