You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询Spring Boot中Refresh Token的实现方法及用户登录保持方案

Implementing Refresh Tokens in Spring Boot (and Other Session Persistence Options)

Hey there! Glad to hear your JWT Access Token setup is working smoothly. Let's tackle how to add Refresh Tokens first—this is the standard approach for handling short-lived Access Tokens without forcing users to re-login constantly. Then we'll cover alternative ways to keep users logged in.

Step-by-Step Refresh Token Implementation

1. Define a Refresh Token Entity

First, create a JPA entity to store refresh tokens in your database. This tracks which user owns the token, its expiration date, and validity:

@Entity
public class RefreshToken {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    @Column(nullable = false, unique = true)
    private String token;

    @Column(nullable = false)
    private Instant expiryDate;

    // Constructors, getters, and setters
}

2. Create a Repository for Refresh Tokens

Add a Spring Data JPA repository to interact with the refresh token table:

public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
    Optional<RefreshToken> findByToken(String token);
    Optional<RefreshToken> findByUser(User user);
    void deleteByUser(User user);
}

3. Build a Refresh Token Service

This service handles generating, validating, and refreshing tokens, plus cleaning up expired entries:

@Service
public class RefreshTokenService {
    @Value("${jwt.refresh-token-expiration-ms}") // e.g., 604800000 for 7 days
    private Long refreshTokenExpiryMs;

    @Autowired
    private RefreshTokenRepository refreshTokenRepo;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    // Generate a new refresh token for a user
    public RefreshToken createRefreshToken(User user) {
        // Optional: Delete existing token to prevent multiple active sessions
        refreshTokenRepo.deleteByUser(user);

        RefreshToken refreshToken = new RefreshToken();
        refreshToken.setUser(user);
        refreshToken.setToken(UUID.randomUUID().toString());
        refreshToken.setExpiryDate(Instant.now().plusMillis(refreshTokenExpiryMs));

        return refreshTokenRepo.save(refreshToken);
    }

    // Validate token and check expiration
    public RefreshToken validateRefreshToken(String token) {
        RefreshToken refreshToken = refreshTokenRepo.findByToken(token)
                .orElseThrow(() -> new RuntimeException("Invalid refresh token"));

        if (refreshToken.getExpiryDate().isBefore(Instant.now())) {
            refreshTokenRepo.delete(refreshToken);
            throw new RuntimeException("Refresh token expired");
        }

        return refreshToken;
    }

    // Generate new access token using valid refresh token
    public ResponseEntity<?> getNewAccessToken(@RequestBody TokenRefreshRequest request) {
        RefreshToken validToken = validateRefreshToken(request.getRefreshToken());
        User user = validToken.getUser();
        String newAccessToken = jwtTokenProvider.generateToken(
                userDetailsService.loadUserByUsername(user.getUsername())
        );

        // Optional: Rotate refresh token here for extra security
        // RefreshToken newRefreshToken = createRefreshToken(user);
        // return ResponseEntity.ok(new TokenRefreshResponse(newAccessToken, newRefreshToken.getToken()));

        return ResponseEntity.ok(new TokenRefreshResponse(newAccessToken, request.getRefreshToken()));
    }
}

Add simple DTOs for request/response handling:

// TokenRefreshRequest.java
public class TokenRefreshRequest {
    private String refreshToken;
    // Getter and setter
}

// TokenRefreshResponse.java
public class TokenRefreshResponse {
    private String accessToken;
    private String refreshToken;
    // Constructor, getters
}

4. Add a Refresh Token Endpoint

Create a controller endpoint to handle token refresh requests:

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private RefreshTokenService refreshTokenService;

    @PostMapping("/refresh-token")
    public ResponseEntity<?> refreshAccessToken(@RequestBody TokenRefreshRequest request) {
        return refreshTokenService.getNewAccessToken(request);
    }
}

5. Update Login Flow

When a user logs in successfully, generate both an Access Token and Refresh Token, return both in the response, and store the Refresh Token in your database.

6. Handle Logout

Invalidate the user's Refresh Token on logout to prevent misuse:

@PostMapping("/logout")
public ResponseEntity<?> logout(@AuthenticationPrincipal UserDetails userDetails) {
    User user = userRepository.findByUsername(userDetails.getUsername())
            .orElseThrow(() -> new RuntimeException("User not found"));
    refreshTokenRepo.deleteByUser(user);
    return ResponseEntity.ok("Logged out successfully");
}

Other Ways to Maintain User Login State

If Refresh Tokens don't fit your use case, here are alternative approaches:

  • Session-Based Authentication (Traditional Spring Security):
    Use server-side sessions with a session ID stored in an HttpOnly cookie. Spring Security handles this out of the box; for distributed apps, use Spring Session with Redis to share sessions across instances. Ideal for simpler monolithic apps where stateless auth isn't a requirement.

  • HttpOnly Secure Cookies for Access Tokens:
    Store short-lived Access Tokens in HttpOnly, Secure cookies instead of returning them in the response body. Browsers auto-send the cookie with every request, and HttpOnly blocks XSS attacks from stealing the token. Pair this with Spring Security's built-in CSRF protection.

  • Long-Lived Access Tokens (Not Recommended):
    Extend the Access Token's expiry (e.g., 30 days), but this carries risk—stolen tokens grant long-term access. You'd need a Redis-based token blacklist to invalidate tokens early, which adds overhead.


内容的提问来源于stack exchange,提问作者odke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:22:00