保持JWT与多个客户端分离的更好方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了保持JWT与多个客户端分离的更好方法相关的知识,希望对你有一定的参考价值。

我有多个应用程序(每个都可以被视为资源服务器)和单个授权服务器。授权服务器在成功验证时发出JWT。在这个RFC中,我读到了关于aud的声明,这是通知客户一个令牌不适合它的好方法,即收件人可以检查JWT中的aud声明并丢弃不适合它的JWT

但是,我想要找出的是如何为每个资源服务器维护一个单独的key/secret,这些资源服务器对其他资源服务器隐藏但授权服务器已知。

我想在资源服务器上保持密钥/秘密的原因是因为我想避免每次都向授权服务器发送请求以验证令牌。(性能命中)。

我想保留一个单独的密钥/秘密的原因是安全性。

我正在使用this jsonWebToken库。

是否有可能做到这一点?

答案

首先,您可以使用缓存来避免每次都检查授权服务器。您可以将其缓存为令牌生存期。

然后,您可以更改为RS256并在端点上公开公钥,资源服务器可以在启动时缓存这些公钥,并在资源服务器中进行检查。但是对于性能,缓存验证结果也是很好的。

有关如何处理签名和验证的快速示例:

public class JwtFactory {

    private Map<String, Key> keys = new HashMap<>();

    public JwtFactory(String keyStore, String keyStorePassword, Map<String, String> resourcesPassword) {
        try {
            KeyStore keystore = KeyStore.getInstance("PKCS12");
            try (InputStream is = Files.newInputStream(Paths.get("jwt.pkcs12"))) {
                keystore.load(is, keyStorePassword.toCharArray());
            }
            for (Map.Entry<String, String> resource : resourcesPassword.entrySet()) {
                keys.put(resource.getKey(), keystore.getKey(resource.getKey(), resource.getValue().toCharArray()));
            }
        } catch (Exception e) {
            throw new RuntimeException("Unable to load key", e);
        }
    }

    public String createToken(String resourceId, String subject, String id, Map<String, Object> claims) {
        return Jwts.builder()
                .setSubject(subject)
                .setId(id)
                .setIssuedAt(Date.from(Instant.ofEpochSecond(System.currentTimeMillis() - 3600)))
                .setExpiration(Date.from(Instant.ofEpochSecond(System.currentTimeMillis() + 3600)))
                .addClaims(claims)
                .signWith(SignatureAlgorithm.RS256, keys.get(resourceId))
                .compact();
    }

    public static void main(String[] args) throws Exception {
        final Map<String, String> resources = new HashMap<>();
        resources.put("resource1", "password");
        resources.put("resource2", "password");

        final Map<String, Object> claims = new HashMap<>();
        claims.put("name", "John Doe");
        claims.put("admin", true);


        JwtFactory factory = new JwtFactory("jwt.pkcs12", "password", resources);
        String token1 = factory.createToken("resource1", "1234567890", "a8070da2-3497-4a51-a932-daa9ae53bddd", claims);
        String token2 = factory.createToken("resource2", "1234567890", "a8070da2-3497-4a51-a932-daa9ae53bddd", claims);

        System.out.println(token1);
        System.out.println(token2);

        final String resource1Public = new String(Files.readAllBytes(Paths.get("resource1.pem")), StandardCharsets.ISO_8859_1)
            .replaceAll("-----BEGIN PUBLIC KEY-----\n", "")
            .replaceAll("-----END PUBLIC KEY-----", "");

        final X509EncodedKeySpec specKey1 = new X509EncodedKeySpec(Base64.decodeBase64(resource1Public.getBytes(StandardCharsets.ISO_8859_1)));

        Jwt jwt = Jwts.parser().setSigningKey(KeyFactory.getInstance("RSA").generatePublic(specKey1)).parse(token1);
        System.out.println("Validation Ok with resource 1");
        System.out.println(jwt);
        try {
            Jwts.parser().setSigningKey(KeyFactory.getInstance("RSA").generatePublic(specKey1)).parse(token2);
        } catch (Exception e) {
            System.out.println("Validation fail with resource 2");
        }
    }
}

生成密钥对:

keytool -genkeypair -alias resource1 -keyalg RSA -keypass password -keystore jwt.pkcs12 -storepass password

提取公钥:

keytool -list -rfc -keystore jwt.pkcs12 -alias resource1 | openssl x509 -inform pem -pubkey -noout

以上是关于保持JWT与多个客户端分离的更好方法的主要内容,如果未能解决你的问题,请参考以下文章

springboot集成jwt令牌前后端分离模式下的身份认证方式

JWT-token—前后端分离架构的api安全问题

Django 前后端分离 JWT 登录

更好的 C# Poker 框架设计?

spring boot整合jwt 实现前后端分离登录认证及授权

nodejs--JWT 在前后端分离中的应用与实践