Spring OAuth2 服务器水平缩放导致身份验证失败

问题描述 投票:0回答:1

我当前正在 PCF 上运行的应用程序中运行 Spring OAuth2 身份验证服务器。最近应用程序的扩展意味着现在我们看到应用程序上有更多的负载,并且我想增加该服务器的实例数量,但是,一旦我这样做了,因为我的逻辑是基于为令牌生成随机密钥,每个实例给出了不同的响应。

我的调用者应用程序在检查令牌时,可以从任何一个实例获取一个令牌,并且主应用程序可能会引用任何其他实例进行验证,这会导致令牌不匹配并且身份验证失败。

在下面的代码中,我尝试对 RSA 公钥、RSA 私钥、密钥 ID 甚至“iat”等令牌参数进行硬编码,但我没有从服务器获得单一一致的响应。

这就是我的 RSA 密钥对的生成方式,如您所见,使用随机序列,因此每个实例都会生成自己的随机序列:

private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            SecureRandom se = SecureRandom.getInstance("SHA1PRNG");
            keyPairGenerator.initialize(2048, se);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

这就是我在 RSAKey 对象中使用生成的密钥并创建 JWKSet 的方式,然后将其返回到 ImmutableJWKSet 对象中:

 public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey(); // From the method above
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .issueTime(null).expirationTime(null)
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
}

我尝试使用硬编码的 RSA 公钥、私钥和密钥 ID,如下所示,但总是得到不同的令牌:

private RSAPublicKey publicKey() {
        String publicKeyPem = "-----BEGIN PUBLIC KEY-----\r\n"
                + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmbUIg34wINnYnjAkdmLj\r\n"
                + "4HtVpM7H0/T4aPAdoLLt9aNNgaBbQ0qS6iEtt1VHiua87Spp3DQpJ0Nyo65hYVgc\r\n"
                + "5KpalBbhH+Bz0Mj9TR/24XGV3VOfmy0pIRZJ4EJZS8WP7+lH0XUyhzVmAEYwaWok\r\n"
                + "LLpmmXHRxXk02KkEtgObL0TyEko3ygxf6O0d0+XuX9GchzBLMR+lt4gOoOld1w2L\r\n"
                + "7CJ9UmF1diesr979xs+nMcST1VkWvR3QBg2cchwZEe5VR/+FD7US1s/mPNxumAgi\r\n"
                + "uJzyLxLNeaCjWueZ4zdj4586YNxFOrLg6j6Ud3bpoX37rnNWWgnuZjNejS6MB5Ov\r\n"
                + "4QIDAQAB\r\n"
                + "-----END PUBLIC KEY-----";
        try {
            String publicKeyPemContent = publicKeyPem.replaceAll("-----BEGIN PUBLIC KEY-----", "").replaceAll("-----END PUBLIC KEY-----", "").replaceAll("\\s", "");
            byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyPemContent);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return (RSAPublicKey) keyFactory.generatePublic(spec);
        } catch (Exception e) {
            throw new RuntimeException("Failed to parse public key", e);
        }
    }

我还更改并硬编码了密钥 ID,但到目前为止还没有成功

我需要一种方法来实现此目的,该服务器的多个实例不会因身份验证失败,如果期望所有实例返回相同的令牌不是最好的主意,我愿意尝试其他方法

提前致谢

spring spring-boot spring-security spring-oauth2
1个回答
0
投票

正如您所想象的那样,每次启动新实例时都会生成一个新的密钥对。最好的方法是生成物理密钥对并将它们存储在文件中然后使用它们。您可以像这样继续:

1- 在终端中使用

openssl
生成 rsa 密钥对

# create keys
$ openssl genrsa -out keypair.pem 2048

# get the public key
$ openssl rsa -in keypair.pem -pubout -out public.pem

# get private key in PKCS#8 format
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out private.pem

然后

2- 在

rsakeys
文件夹下创建
resources
目录,放入
public.pem
private.pem
文件

3- 将配置属性添加到您的

application.propertie|yml

rsa-keys.public-key=classpath:rsakeys/public.pem
rsa-keys.private-key=classpath:rsakeys/private.pem

4- 创建一个配置类,其中将包含引用这些键的属性:

@ConfigurationProperties(prefix = "rsa-keys")
public record RsaKeyProperties(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
}

然后使用此类来配置您的

jwkSource
bean:

 @Bean
 public JWKSource<SecurityContext> jwkSource(RsaKeyProperties rsa) {
        
        RSAKey rsaKey = new RSAKey.Builder(rsa.publicKey())
                .privateKey(publicKey.privateKey())
                .keyID("secured-key-id")
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
}

有了这个,一切都应该正常。不要忘记将您的密钥对外部化到 Vault 这样的安全位置,它们在生产中不应该是可见的。

© www.soinside.com 2019 - 2024. All rights reserved.