最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - A new RefreshToken is not generating even after the expiration time has passed. JWT - Stack Overflow

programmeradmin4浏览0评论

I am working on a movie project where when i log in, it gives me a new access token every time i log in. expiration time of access token is 50 seconds. when i log in after even 4 seconds, it changes. but once generated, i can access the secured endpoint for 50 seconds until i login againa nd change it so it might be working fine (it is my first time working on it). Now refresh Token. it has expiration time of 60 seconds (for testing). Now if i login after 60 seconds it does not change at all. and remains the same. I have tried calling createRefreshtoken in the verify Refresh Token function but it throws a database integrity error. then i tried to update it. but that time also it wasnt updating and only giving me last refreshToken.

Code: AuthController:

package com.movieApi.movieApi.controllers;

import com.movieApi.movieApi.auth.entities.RefreshToken;
import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.services.JwtService;
import com.movieApi.movieApi.auth.services.RefreshTokenService;
import com.movieApi.movieApi.auth.utils.AuthResponse;
import com.movieApi.movieApi.auth.utils.LoginRequest;
import com.movieApi.movieApi.auth.utils.RefreshTokenRequest;
import com.movieApi.movieApi.auth.utils.RegisterRequest;
import .springframework.http.ResponseEntity;
import .springframework.web.bind.annotation.PostMapping;
import .springframework.web.bind.annotation.RequestBody;
import .springframework.web.bind.annotation.RequestMapping;
import .springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/v1/auth")
public class AuthController {

    private final JwtService.AuthService authService;
    private final RefreshTokenService refreshTokenService;
    private final JwtService jwtService;

    public AuthController(JwtService.AuthService authService, RefreshTokenService refreshTokenService, JwtService jwtService) {
        this.authService = authService;
        this.refreshTokenService = refreshTokenService;
        this.jwtService = jwtService;
    }


    @PostMapping("/register")
    public ResponseEntity<AuthResponse> register(@RequestBody RegisterRequest registerRequest) {
        return ResponseEntity.ok(authService.register(registerRequest));
    }
    @PostMapping("/login")
    public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest loginRequest) {

        return ResponseEntity.ok(authService.login(loginRequest));
    }

    @PostMapping("/refresh")
    public ResponseEntity<AuthResponse> refreshToken(@RequestBody RefreshTokenRequest refreshTokenRequest) {

        RefreshToken refreshToken = refreshTokenService.verifyRefreshToken(refreshTokenRequest.getRefreshToken());
        User user = refreshToken.getUser();

        String accessToken = jwtService.generateToken(user);

        return ResponseEntity.ok(
                AuthResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken.getRefreshToken())
                .build()
        );


    }
}

JwtService:

    package com.movieApi.movieApi.auth.services;

import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.entities.UserRole;
import com.movieApi.movieApi.auth.repositories.UserRepository;
import com.movieApi.movieApi.auth.utils.AuthResponse;
import com.movieApi.movieApi.auth.utils.LoginRequest;
import com.movieApi.movieApi.auth.utils.RegisterRequest;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import .springframework.security.authentication.AuthenticationManager;
import .springframework.security.authentication.UsernamePasswordAuthenticationToken;
import .springframework.security.core.userdetails.UserDetails;
import .springframework.security.core.userdetails.UsernameNotFoundException;
import .springframework.security.crypto.password.PasswordEncoder;
import .springframework.stereotype.Service;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Service
public class JwtService {

    private static final String SECRET_KEY = "14bed0b7f0c64601bac40f50358e4e690c35756edfdfa0afb0bac40f27557f18dc35daec5731085eb781a882094e56cbf69fb3caab48410de2847588bac44632e1248f88da9b1ff6c60ad7773644cb6b3edc4b6429846b1132eead675293950161147693a5a848ae2d82934c1f13a68efbf5aa390413b29424af8eab145b953a1e021056f6837ba878b18a5cfb5e219dc4fb98762bb1b7f771eb4ef6f7b879704ce0a95bd2adc9df647968a468c6813d49e1181d011b0450f32267a867ff3410b410ef8685d1889024aa17cecb7df478caf7d61179080ed719e37658b9fefebd58ab1b26a1317de1ad731d0fc2053106262195c923c7b32a9ae79b886d716663";

    //extract username from JWT

    public String extractUsername(String token){
        return extractClaim(token, Claims::getSubject);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllCClaims(token);
        return claimsResolver.apply(claims);
    }

    //extract information from JWT
    public Claims extractAllCClaims(String token){
        return Jwts
                .parserBuilder()
                .setSigningKey(getSignInKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    //decode and get the key
    private Key getSignInKey(){

        //decode SECRET_KEY
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);

    }

    public String generateToken(UserDetails userDetails){
        return generateToken(new HashMap<>(), userDetails);
    }

    public String generateToken(
            Map<String, Object> extraClaims,
            UserDetails userDetails
    ) {
        return Jwts
                .builder()
                .setClaims(extraClaims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 50))
                .signWith(getSignInKey(), SignatureAlgorithm.HS256)
                pact();
    }

    //if token is valid by checking if the token is expired for the current user
    public boolean isTokenValid(String token, UserDetails userDetails){
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    // if token is expired
    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    //get expiration date from token
    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    @Service
    @RequiredArgsConstructor
    public static class AuthService {

        private final PasswordEncoder passwordEncoder;
        private final UserRepository userRepository;
        private final JwtService jwtService;
        private final RefreshTokenService refreshTokenService;
        private final AuthenticationManager authenticationManager;



        public AuthResponse register(RegisterRequest registerRequest){
            var user = User.builder()
                    .name(registerRequest.getName())
                    .email(registerRequest.getEmail())
                    .username(registerRequest.getUsername())
                    .password(passwordEncoder.encode(registerRequest.getPassword()))
                    .role(UserRole.USER) //by default all the users created will be of USER type
                    .build();

            User savedUser = userRepository.save(user);
            var accessToken = jwtService.generateToken(savedUser);
            var refreshToken = refreshTokenService.createRefreshToken(savedUser.getEmail());

            return AuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken.getRefreshToken())
                    .build();
        }

        public AuthResponse login(LoginRequest loginRequest) {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            loginRequest.getEmail(),
                            loginRequest.getPassword()
                    )
            );

            var user = userRepository.findByEmail(loginRequest.getEmail()).orElseThrow(() -> new UsernameNotFoundException("Use Not Found"));
            var accessToken = jwtService.generateToken(user);
            var refreshToken = refreshTokenService.createRefreshToken(loginRequest.getEmail());
            return AuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken.getRefreshToken())
                    .build();
        }
    }
}

RefreshToken:

    package com.movieApi.movieApi.auth.services;

import com.movieApi.movieApi.auth.entities.RefreshToken;
import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.repositories.RefreshTokenRepository;
import com.movieApi.movieApi.auth.repositories.UserRepository;
import .springframework.security.core.userdetails.UsernameNotFoundException;
import .springframework.stereotype.Service;

import java.time.Instant;
import java.util.UUID;

@Service
public class RefreshTokenService {
    private final UserRepository userRepository;
    private final RefreshTokenRepository refreshTokenRepository;

    public RefreshTokenService(UserRepository userRepository, RefreshTokenRepository refreshTokenRepository) {
        this.userRepository = userRepository;
        this.refreshTokenRepository = refreshTokenRepository;
    }

    public RefreshToken createRefreshToken(String username) {
        User user = userRepository.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with email: " + username));

        RefreshToken refreshToken = user.getRefreshToken();




        if (refreshToken == null) {
            long refreshTokenValidity = 60 * 1000;
            refreshToken = RefreshToken.builder()
                    .refreshToken(UUID.randomUUID().toString())
                    .expirationTime(Instant.now().plusMillis(refreshTokenValidity))
                    .user(user)
                    .build();

            refreshTokenRepository.save(refreshToken);


        }
        return refreshToken;

    }


    public RefreshToken verifyRefreshToken(String refreshToken) {
        RefreshToken refToken = refreshTokenRepository.findByRefreshToken(refreshToken)
                .orElseThrow(() -> new RuntimeException("Refresh Token Not Found"));

        if (refToken.getExpirationTime()pareTo(Instant.now()) < 0) {
            refreshTokenRepository.delete(refToken);
            throw new RuntimeException("Refresh Token Not Found");
            // Instead of throwing an error, generate a new refresh token
        }
        return refToken;
    }
}

AuthService:

    package com.movieApi.movieApi.auth.services;

import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.entities.UserRole;
import com.movieApi.movieApi.auth.repositories.UserRepository;
import com.movieApi.movieApi.auth.utils.AuthResponse;
import com.movieApi.movieApi.auth.utils.LoginRequest;
import com.movieApi.movieApi.auth.utils.RegisterRequest;
import lombok.RequiredArgsConstructor;
import .springframework.security.authentication.AuthenticationManager;
import .springframework.security.authentication.UsernamePasswordAuthenticationToken;
import .springframework.security.core.userdetails.UsernameNotFoundException;
import .springframework.security.crypto.password.PasswordEncoder;
import .springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class AuthService {

    private final PasswordEncoder passwordEncoder;
    private final UserRepository userRepository;
    private final JwtService jwtService;
    private final RefreshTokenService refreshTokenService;
    private final AuthenticationManager authenticationManager;

    public AuthResponse register(RegisterRequest registerRequest){
        var user = User.builder()
                .name(registerRequest.getName())
                .email(registerRequest.getEmail())
                .username(registerRequest.getEmail())
                .password(passwordEncoder.encode(registerRequest.getPassword()))
                .role(UserRole.USER)
                .build();

        User savedUser = userRepository.save(user);
        var accessToken = jwtService.generateToken(savedUser);
        var refreshToken = refreshTokenService.createRefreshToken(savedUser.getEmail());

        return AuthResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken.getRefreshToken())
                .build();
    }

    public AuthResponse login(LoginRequest loginRequest){
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getEmail(),
                        loginRequest.getPassword()
                )
        );
        var user = userRepository.findByEmail(loginRequest.getEmail()).orElseThrow(() -> new UsernameNotFoundException("User Not found"));
        var accessToken = jwtService.generateToken(user);
        var refreshToken = refreshTokenService.createRefreshToken(loginRequest.getEmail());

        return AuthResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken.getRefreshToken())
                .build();
    }
}

AuthFilterService:

    package com.movieApi.movieApi.auth.services;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import .springframework.lang.NonNull;
import .springframework.security.authentication.UsernamePasswordAuthenticationToken;
import .springframework.security.core.context.SecurityContextHolder;
import .springframework.security.core.userdetails.UserDetails;
import .springframework.security.core.userdetails.UserDetailsService;
import .springframework.security.web.authentication.WebAuthenticationDetailsSource;
import .springframework.stereotype.Service;
import .springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Service
public class AuthFilterService extends OncePerRequestFilter {

    private final JwtService jwtService;
    private final UserDetailsService userDetailsService;

    public AuthFilterService(JwtService jwtService, UserDetailsService userDetailsService) {
        this.jwtService = jwtService;
        this.userDetailsService = userDetailsService;
    }


    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request,
                                    @NonNull HttpServletResponse response,
                                    @NonNull FilterChain filterChain) throws ServletException, IOException {
        final String authHeader = request.getHeader("Authorization");
        String jwt;
        String username;

        if(authHeader == null || !authHeader.startsWith("Bearer ")){
            filterChain.doFilter(request, response);
            return;
        }

        //extract JWT
        jwt = authHeader.substring(7);

        //extract username from JWT
        username = jwtService.extractUsername(jwt);

        if(username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if(jwtService.isTokenValid(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails,
                        null,
                        userDetails.getAuthorities()
                );

                authenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );

                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }

        }

        filterChain.doFilter(request, response);
    }
}

I am working on a movie project where when i log in, it gives me a new access token every time i log in. expiration time of access token is 50 seconds. when i log in after even 4 seconds, it changes. but once generated, i can access the secured endpoint for 50 seconds until i login againa nd change it so it might be working fine (it is my first time working on it). Now refresh Token. it has expiration time of 60 seconds (for testing). Now if i login after 60 seconds it does not change at all. and remains the same. I have tried calling createRefreshtoken in the verify Refresh Token function but it throws a database integrity error. then i tried to update it. but that time also it wasnt updating and only giving me last refreshToken.

Code: AuthController:

package com.movieApi.movieApi.controllers;

import com.movieApi.movieApi.auth.entities.RefreshToken;
import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.services.JwtService;
import com.movieApi.movieApi.auth.services.RefreshTokenService;
import com.movieApi.movieApi.auth.utils.AuthResponse;
import com.movieApi.movieApi.auth.utils.LoginRequest;
import com.movieApi.movieApi.auth.utils.RefreshTokenRequest;
import com.movieApi.movieApi.auth.utils.RegisterRequest;
import .springframework.http.ResponseEntity;
import .springframework.web.bind.annotation.PostMapping;
import .springframework.web.bind.annotation.RequestBody;
import .springframework.web.bind.annotation.RequestMapping;
import .springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/v1/auth")
public class AuthController {

    private final JwtService.AuthService authService;
    private final RefreshTokenService refreshTokenService;
    private final JwtService jwtService;

    public AuthController(JwtService.AuthService authService, RefreshTokenService refreshTokenService, JwtService jwtService) {
        this.authService = authService;
        this.refreshTokenService = refreshTokenService;
        this.jwtService = jwtService;
    }


    @PostMapping("/register")
    public ResponseEntity<AuthResponse> register(@RequestBody RegisterRequest registerRequest) {
        return ResponseEntity.ok(authService.register(registerRequest));
    }
    @PostMapping("/login")
    public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest loginRequest) {

        return ResponseEntity.ok(authService.login(loginRequest));
    }

    @PostMapping("/refresh")
    public ResponseEntity<AuthResponse> refreshToken(@RequestBody RefreshTokenRequest refreshTokenRequest) {

        RefreshToken refreshToken = refreshTokenService.verifyRefreshToken(refreshTokenRequest.getRefreshToken());
        User user = refreshToken.getUser();

        String accessToken = jwtService.generateToken(user);

        return ResponseEntity.ok(
                AuthResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken.getRefreshToken())
                .build()
        );


    }
}

JwtService:

    package com.movieApi.movieApi.auth.services;

import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.entities.UserRole;
import com.movieApi.movieApi.auth.repositories.UserRepository;
import com.movieApi.movieApi.auth.utils.AuthResponse;
import com.movieApi.movieApi.auth.utils.LoginRequest;
import com.movieApi.movieApi.auth.utils.RegisterRequest;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import .springframework.security.authentication.AuthenticationManager;
import .springframework.security.authentication.UsernamePasswordAuthenticationToken;
import .springframework.security.core.userdetails.UserDetails;
import .springframework.security.core.userdetails.UsernameNotFoundException;
import .springframework.security.crypto.password.PasswordEncoder;
import .springframework.stereotype.Service;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Service
public class JwtService {

    private static final String SECRET_KEY = "14bed0b7f0c64601bac40f50358e4e690c35756edfdfa0afb0bac40f27557f18dc35daec5731085eb781a882094e56cbf69fb3caab48410de2847588bac44632e1248f88da9b1ff6c60ad7773644cb6b3edc4b6429846b1132eead675293950161147693a5a848ae2d82934c1f13a68efbf5aa390413b29424af8eab145b953a1e021056f6837ba878b18a5cfb5e219dc4fb98762bb1b7f771eb4ef6f7b879704ce0a95bd2adc9df647968a468c6813d49e1181d011b0450f32267a867ff3410b410ef8685d1889024aa17cecb7df478caf7d61179080ed719e37658b9fefebd58ab1b26a1317de1ad731d0fc2053106262195c923c7b32a9ae79b886d716663";

    //extract username from JWT

    public String extractUsername(String token){
        return extractClaim(token, Claims::getSubject);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllCClaims(token);
        return claimsResolver.apply(claims);
    }

    //extract information from JWT
    public Claims extractAllCClaims(String token){
        return Jwts
                .parserBuilder()
                .setSigningKey(getSignInKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    //decode and get the key
    private Key getSignInKey(){

        //decode SECRET_KEY
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);

    }

    public String generateToken(UserDetails userDetails){
        return generateToken(new HashMap<>(), userDetails);
    }

    public String generateToken(
            Map<String, Object> extraClaims,
            UserDetails userDetails
    ) {
        return Jwts
                .builder()
                .setClaims(extraClaims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 50))
                .signWith(getSignInKey(), SignatureAlgorithm.HS256)
                pact();
    }

    //if token is valid by checking if the token is expired for the current user
    public boolean isTokenValid(String token, UserDetails userDetails){
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    // if token is expired
    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    //get expiration date from token
    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    @Service
    @RequiredArgsConstructor
    public static class AuthService {

        private final PasswordEncoder passwordEncoder;
        private final UserRepository userRepository;
        private final JwtService jwtService;
        private final RefreshTokenService refreshTokenService;
        private final AuthenticationManager authenticationManager;



        public AuthResponse register(RegisterRequest registerRequest){
            var user = User.builder()
                    .name(registerRequest.getName())
                    .email(registerRequest.getEmail())
                    .username(registerRequest.getUsername())
                    .password(passwordEncoder.encode(registerRequest.getPassword()))
                    .role(UserRole.USER) //by default all the users created will be of USER type
                    .build();

            User savedUser = userRepository.save(user);
            var accessToken = jwtService.generateToken(savedUser);
            var refreshToken = refreshTokenService.createRefreshToken(savedUser.getEmail());

            return AuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken.getRefreshToken())
                    .build();
        }

        public AuthResponse login(LoginRequest loginRequest) {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            loginRequest.getEmail(),
                            loginRequest.getPassword()
                    )
            );

            var user = userRepository.findByEmail(loginRequest.getEmail()).orElseThrow(() -> new UsernameNotFoundException("Use Not Found"));
            var accessToken = jwtService.generateToken(user);
            var refreshToken = refreshTokenService.createRefreshToken(loginRequest.getEmail());
            return AuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken.getRefreshToken())
                    .build();
        }
    }
}

RefreshToken:

    package com.movieApi.movieApi.auth.services;

import com.movieApi.movieApi.auth.entities.RefreshToken;
import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.repositories.RefreshTokenRepository;
import com.movieApi.movieApi.auth.repositories.UserRepository;
import .springframework.security.core.userdetails.UsernameNotFoundException;
import .springframework.stereotype.Service;

import java.time.Instant;
import java.util.UUID;

@Service
public class RefreshTokenService {
    private final UserRepository userRepository;
    private final RefreshTokenRepository refreshTokenRepository;

    public RefreshTokenService(UserRepository userRepository, RefreshTokenRepository refreshTokenRepository) {
        this.userRepository = userRepository;
        this.refreshTokenRepository = refreshTokenRepository;
    }

    public RefreshToken createRefreshToken(String username) {
        User user = userRepository.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with email: " + username));

        RefreshToken refreshToken = user.getRefreshToken();




        if (refreshToken == null) {
            long refreshTokenValidity = 60 * 1000;
            refreshToken = RefreshToken.builder()
                    .refreshToken(UUID.randomUUID().toString())
                    .expirationTime(Instant.now().plusMillis(refreshTokenValidity))
                    .user(user)
                    .build();

            refreshTokenRepository.save(refreshToken);


        }
        return refreshToken;

    }


    public RefreshToken verifyRefreshToken(String refreshToken) {
        RefreshToken refToken = refreshTokenRepository.findByRefreshToken(refreshToken)
                .orElseThrow(() -> new RuntimeException("Refresh Token Not Found"));

        if (refToken.getExpirationTime()pareTo(Instant.now()) < 0) {
            refreshTokenRepository.delete(refToken);
            throw new RuntimeException("Refresh Token Not Found");
            // Instead of throwing an error, generate a new refresh token
        }
        return refToken;
    }
}

AuthService:

    package com.movieApi.movieApi.auth.services;

import com.movieApi.movieApi.auth.entities.User;
import com.movieApi.movieApi.auth.entities.UserRole;
import com.movieApi.movieApi.auth.repositories.UserRepository;
import com.movieApi.movieApi.auth.utils.AuthResponse;
import com.movieApi.movieApi.auth.utils.LoginRequest;
import com.movieApi.movieApi.auth.utils.RegisterRequest;
import lombok.RequiredArgsConstructor;
import .springframework.security.authentication.AuthenticationManager;
import .springframework.security.authentication.UsernamePasswordAuthenticationToken;
import .springframework.security.core.userdetails.UsernameNotFoundException;
import .springframework.security.crypto.password.PasswordEncoder;
import .springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class AuthService {

    private final PasswordEncoder passwordEncoder;
    private final UserRepository userRepository;
    private final JwtService jwtService;
    private final RefreshTokenService refreshTokenService;
    private final AuthenticationManager authenticationManager;

    public AuthResponse register(RegisterRequest registerRequest){
        var user = User.builder()
                .name(registerRequest.getName())
                .email(registerRequest.getEmail())
                .username(registerRequest.getEmail())
                .password(passwordEncoder.encode(registerRequest.getPassword()))
                .role(UserRole.USER)
                .build();

        User savedUser = userRepository.save(user);
        var accessToken = jwtService.generateToken(savedUser);
        var refreshToken = refreshTokenService.createRefreshToken(savedUser.getEmail());

        return AuthResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken.getRefreshToken())
                .build();
    }

    public AuthResponse login(LoginRequest loginRequest){
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getEmail(),
                        loginRequest.getPassword()
                )
        );
        var user = userRepository.findByEmail(loginRequest.getEmail()).orElseThrow(() -> new UsernameNotFoundException("User Not found"));
        var accessToken = jwtService.generateToken(user);
        var refreshToken = refreshTokenService.createRefreshToken(loginRequest.getEmail());

        return AuthResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken.getRefreshToken())
                .build();
    }
}

AuthFilterService:

    package com.movieApi.movieApi.auth.services;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import .springframework.lang.NonNull;
import .springframework.security.authentication.UsernamePasswordAuthenticationToken;
import .springframework.security.core.context.SecurityContextHolder;
import .springframework.security.core.userdetails.UserDetails;
import .springframework.security.core.userdetails.UserDetailsService;
import .springframework.security.web.authentication.WebAuthenticationDetailsSource;
import .springframework.stereotype.Service;
import .springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Service
public class AuthFilterService extends OncePerRequestFilter {

    private final JwtService jwtService;
    private final UserDetailsService userDetailsService;

    public AuthFilterService(JwtService jwtService, UserDetailsService userDetailsService) {
        this.jwtService = jwtService;
        this.userDetailsService = userDetailsService;
    }


    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request,
                                    @NonNull HttpServletResponse response,
                                    @NonNull FilterChain filterChain) throws ServletException, IOException {
        final String authHeader = request.getHeader("Authorization");
        String jwt;
        String username;

        if(authHeader == null || !authHeader.startsWith("Bearer ")){
            filterChain.doFilter(request, response);
            return;
        }

        //extract JWT
        jwt = authHeader.substring(7);

        //extract username from JWT
        username = jwtService.extractUsername(jwt);

        if(username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if(jwtService.isTokenValid(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails,
                        null,
                        userDetails.getAuthorities()
                );

                authenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );

                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }

        }

        filterChain.doFilter(request, response);
    }
}
Share Improve this question asked Mar 24 at 7:27 user30039244user30039244 1
Add a comment  | 

1 Answer 1

Reset to default 0

If a RefreshToken already exists for a user, your current createRefreshToken method does not create a new one. Instead, it relies on the existing refresh token and ignores its expiration logic.

public RefreshToken createRefreshToken(String username) {
    User user = userRepository.findByEmail(username)
            .orElseThrow(() -> new UsernameNotFoundException("User not found with email: " + username));

    RefreshToken refreshToken = user.getRefreshToken();

    long refreshTokenValidity = 60 * 1000; // 60 seconds for testing purposes

    if (refreshToken == null || refreshToken.getExpirationTime()pareTo(Instant.now()) < 0) {
        refreshToken = RefreshToken.builder()
                .refreshToken(UUID.randomUUID().toString())
                .expirationTime(Instant.now().plusMillis(refreshTokenValidity))
                .user(user)
                .build();
        refreshTokenRepository.save(refreshToken);
    }

    return refreshToken;
}

Instead of throwing an exception when the refresh token is expired, regenerate a new refresh token for the user.

public RefreshToken verifyRefreshToken(String refreshToken) {
    RefreshToken refToken = refreshTokenRepository.findByRefreshToken(refreshToken)
            .orElseThrow(() -> new RuntimeException("Refresh Token Not Found"));

    if (refToken.getExpirationTime()pareTo(Instant.now()) < 0) {
        refreshTokenRepository.delete(refToken);
        return createRefreshToken(refToken.getUser().getEmail());
    }
    return refToken;
}

In the refreshToken method inside the AuthController, ensure that the verifyRefreshToken method not only validates the token but also generates a new one if necessary.

@PostMapping("/refresh")
public ResponseEntity<AuthResponse> refreshToken(@RequestBody RefreshTokenRequest refreshTokenRequest) {
    RefreshToken refreshToken = refreshTokenService.verifyRefreshToken(refreshTokenRequest.getRefreshToken());

    User user = refreshToken.getUser();
    String accessToken = jwtService.generateToken(user);

    return ResponseEntity.ok(
            AuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken.getRefreshToken())
                    .build()
    );
}

Add a constraint at the database level to ensure the unique relationship between User and RefreshToken

ALTER TABLE refresh_token ADD CONSTRAINT unique_refresh_token_user UNIQUE (user_id);
发布评论

评论列表(0)

  1. 暂无评论