您的当前位置:首页正文

SpringBoot 中基于 JWT 的双 Token(access_token + refresh_token)授权和续期方案

2024-11-27 来源:个人技术集锦

在现代 Web 应用程序中,安全性和用户体验是两个关键要素。使用 JWT(JSON Web Token) 进行用户认证已经成为一种流行的方式。在许多应用场景中,仅使用一个 access_token 可能会导致用户体验不佳,尤其是当 access_token 过期后。为此,使用 refresh_token 来续期 access_token 是一种非常有效的方案。

本文将通过详细的代码示例,介绍如何在 SpringBoot 项目中实现基于 JWT 的双 Token(access_token + refresh_token)授权和续期方案。

一、环境准备

1.1 引入 Maven 依赖

首先,在你的 SpringBoot 项目的 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

1.2 配置 JWT 属性

application.yml 中配置 JWT 的相关属性,例如密钥、过期时间等:

jwt:
  secret: your_secret_key
  access-token-expiration: 3600  # 1小时
  refresh-token-expiration: 604800  # 1周

二、实现 JWT 工具类

我们首先创建一个 JWT 工具类,用于生成和解析 token。

2.1 JWTUtils.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JWTUtils {

    @Value("${jwt.secret}")
    private String secretKey;

    @Value("${jwt.access-token-expiration}")
    private long accessTokenExpiration;

    @Value("${jwt.refresh-token-expiration}")
    private long refreshTokenExpiration;

    public String generateAccessToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username, accessTokenExpiration);
    }

    public String generateRefreshToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username, refreshTokenExpiration);
    }

    private String createToken(Map<String, Object> claims, String subject, long expiration) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    public String extractUsername(String token) {
        return extractClaims(token).getSubject();
    }

    public boolean isTokenExpired(String token) {
        return extractClaims(token).getExpiration().before(new Date());
    }

    private Claims extractClaims(String token) {
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody();
    }
}
代码说明:
  • generateAccessTokengenerateRefreshToken 方法分别生成 access_token 和 refresh_token。
  • createToken 方法负责创建 JWT,设置声明、主题、发放时间和过期时间。
  • extractUsernameisTokenExpired 方法用于提取 token 中的信息和检查 token 是否过期。

三、实现用户认证

我们需要实现一个简单的用户认证机制,以便在用户登录时生成并返回 access_token 和 refresh_token。

3.1 AuthController.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

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

    @Autowired
    private JWTUtils jwtUtils;

    @PostMapping("/login")
    public AuthResponse login(@RequestBody AuthRequest request) {
        // 假设用户验证通过
        String username = request.getUsername();

        String accessToken = jwtUtils.generateAccessToken(username);
        String refreshToken = jwtUtils.generateRefreshToken(username);

        return new AuthResponse(accessToken, refreshToken);
    }
}

class AuthRequest {
    private String username;
    private String password;

    // Getters and Setters
}

class AuthResponse {
    private String accessToken;
    private String refreshToken;

    public AuthResponse(String accessToken, String refreshToken) {
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
    }

    // Getters
}
代码说明:
  • AuthController 中创建了 /auth/login 接口,接收用户登录请求。
  • 在登录成功后,生成 access_token 和 refresh_token,并返回给用户。

四、实现 Token 刷新

在用户的 access_token 过期后,可以使用 refresh_token 来获取新的 access_token。

4.1 RefreshTokenController.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
public class RefreshTokenController {

    @Autowired
    private JWTUtils jwtUtils;

    @PostMapping("/refresh")
    public AuthResponse refresh(@RequestBody String refreshToken) {
        String username = jwtUtils.extractUsername(refreshToken);
        if (username != null && !jwtUtils.isTokenExpired(refreshToken)) {
            String newAccessToken = jwtUtils.generateAccessToken(username);
            return new AuthResponse(newAccessToken, refreshToken);
        }
        throw new RuntimeException("Refresh token is invalid or expired");
    }
}
代码说明:
  • RefreshTokenController 中创建了 /auth/refresh 接口,用于刷新 access_token。
  • 检查 refresh_token 是否有效,若有效,则生成新的 access_token。

五、配置 Spring Security

为了保护我们的 API,我们需要在 Spring Security 中配置 JWT 认证。

5.1 SecurityConfig.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/auth/**").permitAll()  // 允许访问认证相关的接口
            .anyRequest().authenticated();  // 其他请求需认证
    }
}

六、总结

通过本文的详细介绍,您已经掌握了如何在 SpringBoot 中实现基于 JWT 的双 Token(access_token + refresh_token)授权和续期方案。这种方案不仅提高了安全性,还增强了用户体验,使用户能够在 token 过期后无缝续期。

  • 我们使用了 JWT 工具类来生成和解析 token,确保了安全性。
  • 提供了用户认证和 token 刷新接口,使得用户能够便捷地获取和更新 token。

这种双 Token 的设计模式在许多现代 Web 应用中都有广泛的应用,如 RESTful API、微服务等,为用户提供了一种安全且高效的认证方式。

显示全文