代码音符

spring-boot-starter-oauth2-resource-server

创建时间: 9-30 17:33

浏览: 5

package com.example.demo.api.component;

import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;

@Service
public class JwtService {

    private final JwtEncoder jwtEncoder;

    public JwtService(JwtEncoder jwtEncoder) {
        this.jwtEncoder = jwtEncoder;
    }

    public String generateToken(String username) {
        Instant now = Instant.now();
        Instant expiry = now.plus(1, ChronoUnit.HOURS); // 1 小时过期

        JwtClaimsSet claims = JwtClaimsSet.builder()
                .subject(username)
                .claim("name", username)
                .claim("roles", List.of("USER"))
                .issuedAt(now)
                .expiresAt(expiry)
                .build();

        return jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
    }

}

package com.example.demo.api.component;

import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.FileOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Base64;

@Component
public class KeyGenerator {

    private final static Logger logger = LoggerFactory.getLogger(KeyGenerator.class);

    @PostConstruct
    public void generateKeys() throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048);
        KeyPair keyPair = keyGen.generateKeyPair();

        // 保存私钥
        try (FileOutputStream fos = new FileOutputStream("src/main/resources/private.key")) {
            fos.write("-----BEGIN PRIVATE KEY-----\n".getBytes());
            fos.write(Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()).getBytes());
            fos.write("\n-----END PRIVATE KEY-----\n".getBytes());
        }

        // 保存公钥
        try (FileOutputStream fos = new FileOutputStream("src/main/resources/public.key")) {
            fos.write("-----BEGIN PUBLIC KEY-----\n".getBytes());
            fos.write(Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()).getBytes());
            fos.write("\n-----END PUBLIC KEY-----\n".getBytes());
        }

        logger.info("公钥私钥生成完毕!");
    }

}

package com.example.demo.api.config;

import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;

import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.List;

@Configuration
public class JwtConfig {

    @Bean
    public JwtEncoder jwtEncoder() {
        try {
            // 1. 读取私钥文件(PKCS#8 格式)
            ClassPathResource resource = new ClassPathResource("private.key");
            InputStream is = resource.getInputStream();
            byte[] keyBytes = is.readAllBytes();
            is.close();

            String privateKeyPem = new String(keyBytes)
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s", "");

            byte[] decoded = Base64.getDecoder().decode(privateKeyPem);
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = kf.generatePrivate(spec);

            // 2. 读取公钥文件(用于 JWK)
            ClassPathResource pubResource = new ClassPathResource("public.key");
            InputStream pubIs = pubResource.getInputStream();
            byte[] pubKeyBytes = pubIs.readAllBytes();
            pubIs.close();

            String publicKeyPem = new String(pubKeyBytes)
                    .replace("-----BEGIN PUBLIC KEY-----", "")
                    .replace("-----END PUBLIC KEY-----", "")
                    .replaceAll("\\s", "");

            byte[] pubDecoded = Base64.getDecoder().decode(publicKeyPem);
            X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubDecoded);
            PublicKey publicKey = kf.generatePublic(pubSpec);

            // 3. 构建 RSAKey(用于 JWK)
            RSAKey rsaKey = new RSAKey.Builder((java.security.interfaces.RSAPublicKey) publicKey)
                    .privateKey(privateKey)
                    .build();

            // 4. 创建 JWK Source
            JWKSource<SecurityContext> jwkSource = (keySelectorParameters, context) -> List.of(rsaKey);

            // 5. 返回 JwtEncoder
            return new NimbusJwtEncoder(jwkSource);
        } catch (Exception e) {
            throw new RuntimeException("无法加载私钥或公钥", e);
        }
    }

}

package com.example.demo.api.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.web.server.SecurityWebFilterChain;

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .authorizeExchange(authorize ->
                        authorize.pathMatchers("/auth/login").permitAll()
                                .pathMatchers("/api/**").authenticated()
                                .anyExchange().denyAll()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt.jwtDecoder(jwtDecoder()))
                )
                .build();
    }

    @Bean
    public ReactiveJwtDecoder jwtDecoder() {
        return NimbusReactiveJwtDecoder.withPublicKey(loadPublicKey()).build();
    }

    private RSAPublicKey loadPublicKey() {
        try (InputStream is = new FileInputStream("src/main/resources/public.key")) {
            ClassPathResource resource = new ClassPathResource("public.key");
            byte[] keyBytes = resource.getInputStream().readAllBytes();

            String publicKeyPem = new String(keyBytes)
                    .replace("-----BEGIN PUBLIC KEY-----", "")
                    .replace("-----END PUBLIC KEY-----", "")
                    .replaceAll("\\s", "");

            byte[] decoded = Base64.getDecoder().decode(publicKeyPem);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PublicKey publicKey = kf.generatePublic(spec);

            // 强制转换为 RSAPublicKey
            return (RSAPublicKey) publicKey;
        } catch (Exception e) {
            throw new RuntimeException("Failed to load public key", e);
        }
    }

}

创建时间: 9-30 17:33

浏览: 5

*本文遵循 CC BY-NC-SA 许可协议。转载请注明出处!