You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Java Spring Boot中使用自定义证书生成JWT Token的最简实现方案咨询

嘿,我来帮你一步步搞定这个需求,尽量用最简单的方式——毕竟你说对Token、公钥这些技术不太熟,我会尽量讲明白每一步的作用~

最简实现步骤

1. 关键前提:你需要私钥

先给你划个重点:生成JWT必须用私钥签名,你现在提供的证书(.crt/.pem)里包含的是公钥,公钥是用来验证Token合法性的,不是用来生成Token的。所以你得找到当初生成这个证书时对应的私钥文件(通常是.pem格式,比如private-key.pem)——用openssl生成证书时一定会同时生成私钥,要是找不到的话可能得重新生成密钥对,但你已经有证书了,应该能找到对应的私钥~

2. 添加必要依赖(标准库无JWT实现)

Java标准库没有内置JWT的生成/解析工具,咱们用最常用的轻量库jjwt,它的API很友好,上手快。如果是Maven项目,在pom.xml里加这些依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

Gradle项目的话,对应添加:

implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

3. 编写JWT生成工具类

把你的私钥文件放在src/main/resources目录下(比如命名为private-key.pem),然后写一个工具类加载私钥并生成Token:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.time.Instant;
import java.util.Date;

public class JwtTokenGenerator {

    // 从项目资源目录加载私钥
    private static PrivateKey loadPrivateKey() throws IOException {
        ClassPathResource resource = new ClassPathResource("private-key.pem");
        String privateKeyPem = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
        // 自动解析PEM格式的私钥
        return Keys.privateKeyFromPem(privateKeyPem);
    }

    // 生成JWT Token,参数可以传用户名(用来标识用户)
    public static String generateToken(String username) throws IOException {
        PrivateKey privateKey = loadPrivateKey();
        // 设置Token过期时间,这里设1小时,你可以按需调整
        Instant expiration = Instant.now().plusSeconds(3600);

        return Jwts.builder()
                // 设置Token的主题(一般存用户名)
                .setSubject(username)
                // 设置Token签发时间
                .setIssuedAt(Date.from(Instant.now()))
                // 设置Token过期时间
                .setExpiration(Date.from(expiration))
                // 用私钥+RS256算法签名(非对称加密,比对称加密更安全)
                .signWith(privateKey, SignatureAlgorithm.RS256)
                // 生成最终的Token字符串
                .compact();
    }
}

4. 写生成Token的Controller

创建一个Controller接口,让客户端调用它获取Token:

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
public class TokenController {

    // 客户端调用这个接口获取Token,比如访问 GET http://localhost:8080/token?username=testUser
    @GetMapping("/token")
    public ResponseEntity<String> getToken(@RequestParam String username) {
        try {
            String token = JwtTokenGenerator.generateToken(username);
            return ResponseEntity.ok(token);
        } catch (IOException e) {
            return ResponseEntity.status(500).body("生成Token失败:" + e.getMessage());
        }
    }
}

5. 配置JWT认证(让其他接口能验证Token)

接下来要让你的其他接口能识别并验证这个Token,咱们用Spring Security来配置:

5.1 加载公钥(从你的证书文件)

把你的证书文件(比如certificate.crt)放在src/main/resources下,写个工具类加载公钥:

import io.jsonwebtoken.security.Keys;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;

public class JwtPublicKeyLoader {

    public static PublicKey loadPublicKey() throws IOException {
        ClassPathResource resource = new ClassPathResource("certificate.crt");
        String certPem = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
        // 从证书中解析出公钥
        return Keys.publicKeyFromPem(certPem);
    }
}

5.2 配置Spring Security

创建一个Security配置类,设置哪些接口需要认证,以及怎么验证Token:

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.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;
import java.security.PublicKey;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final PublicKey publicKey;

    // 构造函数中加载公钥
    public SecurityConfig() throws IOException {
        this.publicKey = JwtPublicKeyLoader.loadPublicKey();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // 关闭CSRF,因为是无状态的API服务
                .csrf().disable()
                // 不用Session,完全靠Token认证
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 设置接口权限:/token允许匿名访问,其他接口必须认证
                .authorizeHttpRequests()
                .antMatchers("/token").permitAll()
                .anyRequest().authenticated()
                .and()
                // 添加JWT验证过滤器
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    // 创建JWT验证过滤器
    private JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter(publicKey);
    }
}

5.3 编写JWT验证过滤器

这个过滤器会从请求头的Authorization字段(格式是Bearer <你的Token>)里提取Token,并用公钥验证其合法性:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.PublicKey;
import java.util.Collections;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final PublicKey publicKey;

    public JwtAuthenticationFilter(PublicKey publicKey) {
        this.publicKey = publicKey;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求头获取Authorization字段
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        // 提取Token部分
        String token = authHeader.substring(7);

        try {
            // 用公钥验证Token,并解析出里面的用户信息
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(publicKey)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();

            // 获取用户名
            String username = claims.getSubject();

            // 将用户信息设置到Spring Security的上下文里,这样后续接口就能识别用户
            UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                    username, null, Collections.emptyList()
            );
            SecurityContextHolder.getContext().setAuthentication(authToken);

        } catch (Exception e) {
            // Token验证失败,清空认证信息
            SecurityContextHolder.clearContext();
        }

        filterChain.doFilter(request, response);
    }
}

6. 测试一下

  1. 启动Spring Boot应用,调用GET http://localhost:8080/token?username=testUser,会得到一串JWT字符串。
  2. 调用你其他需要认证的接口(比如GET /api/user),在请求头里添加Authorization: Bearer <刚才拿到的Token>,就能正常访问了。

给你补点基础概念(怕你懵)

  • 私钥:只有你的服务端持有,用来给JWT签名,绝对不能泄露。
  • 公钥(证书):可以公开给客户端或者其他服务,用来验证Token是不是你的服务端签发的。
  • RS256算法:非对称加密算法,签名用私钥,验证用公钥,比对称加密(比如HS256)更安全,不用和客户端共享密钥。
  • Token过期时间:一定要设置,避免Token被盗用后永久有效,这里设了1小时,你可以根据业务需求调整。

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

火山引擎 最新活动