更新时间:2024-11-11 GMT+08:00
分享

快速开始

本章节以一个被调用API为例,进行检验API中token的合法性。

操作步骤

在一个待集成的应用系统中,找到一个API接口,如/testApi

  1. 在工程中增加配置信息,具体配置获取方式见准备工作中收集信息章节。

    jwt.service.publickey.url: 公钥获取地址
    jwt.secret.id: 密钥ID
    jwt.secret.appName: 集成应用名称
    jwt.secret.key: 私钥
    jwt.secret.publicKey: 公钥

  2. 定时加载公钥代码。

    public void autoRefreshPublicKey() {
        Calendar calendar = Calendar.getInstance();
        Date currentTime = calendar.getTime();
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    //定时从IPDCenter获取所有公钥信息
                    List<String> publicKeys = HttpClientUtil.post(url, token);
                    for (String publicKeyStr : publicKeys) {
                        // 公钥加载到内存中
                        PublicKey publicKey = getPublicKey(publicKeyStr);
                    }
                } catch (Exception e) {
                    log.error("autoLoadPublicKey error", e);
                }
            }
        }, currentTime, refreshTime);
    }
    
    public PublicKey getPublicKey(String publicKeyStr) {
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory;
        try {
            keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePublic(x509EncodedKeySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            log.error("Failed to load the public key to the JWT token. Exception: {}", e.getMessage());
        }
        return null;
    }

  3. 应用启动时加载私钥。

    public PrivateKey getPrivateKey(String privateKeyStr) {
        try {
            byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            log.error("Failed to load the private key to the JWT token. Exception: {}", e.getMessage());
        }
        return null;
    }

  4. 检验token的方法。

    private static boolean verify(HttpServletRequest httpServletRequest, String publicKeyId, String token) {
        // 从公钥缓存中获取对应的密钥编号的公钥
        PublicKey publicKey = PublicKeyCache.getInstance().publicKeysCache.get(publicKeyId);
        try {
            Algorithm algorithm =  Algorithm.RSA256((RSAPublicKey) publicKey, null);
            DecodedJWT decodedJwt = JWT.require(algorithm).ignoreIssuedAt().build().verify(token);
            Date expiresAt = decodedJwt.getExpiresAt();
            // 验证token是否过期
            if (expiresAt.before(new Date())) {
                LOGGER.error("The token has expired.");
                return false;
            }
            // 验证自己是否为接收方
            List<String> audience = decodedJwt.getAudience();
            if (audience != null && !audience.isEmpty() && !validAudience(httpServletRequest, audience)) {
                return false;
            }
        } catch (JWTVerificationException | IllegalArgumentException | SdkException e) {
            LOGGER.error("Verification failed. Jwt Token verification exception: {}", e.getMessage());
            return false;
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Token has verify successfully.");
        }
        return true;
    }

  5. 生成token的方法。

    public static String generateToken(JwtToken jwtToken, boolean isUnique) {
        // 私钥从缓存中通过编号获取
        PrivateKey privateKey = PrivatekeyCache.getInstance().privateKeysCache.get(jwtToken.getKeyId());
        Algorithm algorithm = Algorithm.RSA256(null, (RSAPrivateKey) privateKey);
        Date issuedAt = new Date();
        // 未设置过期时间,则默认签发时间后5分钟内有效
        Date expiresAt =
            new Date(issuedAt.getTime() + (jwtToken.getExpirationTime() <= 0 ? 300000 : jwtToken.getExpirationTime()));
        return "Internal:" + JWT.create()
            .withKeyId(jwtToken.getKeyId())  //密钥ID
            .withIssuer(jwtToken.getIssuer())  //用户名:用户ID
            .withSubject(jwtToken.getSubject())  //appName
            .withExpiresAt(expiresAt)
            .withIssuedAt(issuedAt)
            .withJWTId(jwtToken.getJwtId())  //随机UUID
            .withClaim("claims", jwtToken.getClaimMap())  //保持默认值
            .sign(algorithm);
    }

  6. “/testApi”接口中获取token。

    String token = request.getHeader("Authorization");

  7. 调用检验方法,检验token的合法性,并获取调用者信息。

    String issuer = verify(token);

结果验证

  1. 调用SDK中的方法生成一个临时的token。

    String token = generateToken();

  2. 调用API调试工具调用“/testApi”接口,调用时将步骤1中生成的token放入header中。

    curl http://ip:port/xx/testApi -H "Authorization:token"

相关文档