JWT 3.0.0-beta.14

JWT 3.0.0-beta.14

测试已测试
语言语言 Objective-CObjective C
许可证 MIT
发布最新发布2021年6月

Klaas Pieter AnnemaDmitry Lobanov 维护。



JWT 3.0.0-beta.14

  • 作者:
  • Klaas Pieter Annema

JWT

Build Status Pod Version Pod Platform Gitter

JWT

一个 Objective-C 的 JSON Web Token 实现。

maater 和 bleeding edge 中新功能。

  • 支持 EC 算法。
  • 从 Pem 文件中提取密钥已更新。

支持 EC 算法。

前提条件。

  • 对应于公钥和私钥的证书和 P12。
  • 带有密钥的 Pem 文件,采用 ANSI X9.63 格式。

示例。

NSString *privateKeyString = @"<ANSI X9.63 formatted key>";
NSString *publicKeyString = @"<ANSI X9.63 formatted key>";

// Note: We should pass type of key. Default type is RSA.
NSDictionary *parameters = @{JWTCryptoKey.parametersKeyBuilder : JWTCryptoKeyBuilder.new.keyTypeEC};

id <JWTCryptoKeyProtocol> privateKey = [[JWTCryptoKeyPrivate alloc] initWithPemEncoded:privateKeyString parameters:parameters error:nil];
id <JWTCryptoKeyProtocol> publicKey = [[JWTCryptoKeyPublic alloc] initWithPemEncoded:publicKeyString parameters:parameters error:nil];

// Note: JWTAlgorithmRSFamilyDataHolder will be renamed to something more appropriate. It can holds any asymmetric keys pair (private and public).
id <JWTAlgorithmDataHolderProtocol> holder = [JWTAlgorithmRSFamilyDataHolder new].signKey(privateKey).verifyKey(publicKey).algorithmName(JWTAlgorithmNameES256);

3.0版本中的新功能

  • Fluent风格扩展。
  • 添加编码结果类型。
  • 算法和数据持有者。
  • 算法和数据持有者链。
  • 从Pem文件加载密钥。

算法数据持有者和链的介绍。

你有一个算法,一个秘密数据和未知jwt令牌。让我们试着解码它。

// create token
NSString *token = @"...";

// possible that algorithm could return error.
// you could try use algorithm and data chain.

NSString *firstSecret = @"first";
NSString *firstAlgorithmName = JWTAlgorithmNameHS384;

id <JWTAlgorithmDataHolderProtocol> firstHolder = [JWTAlgorithmHSFamilyDataHolder new].algorithmName(firstAlgorithmName).secret(firstSecret);

id <JWTAlgorithmDataHolderProtocol> errorHolder = [JWTAlgorithmNoneDataHolder new];

// chain together.
JWTAlgorithmDataHolderChain *chain = [[JWTAlgorithmDataHolderChain alloc] initWithHolders:@[firstHolder, errorHolder]];

// or add them in builder
[JWTDecodingBuilder decodeMessage:token].addHolder(firstHolder).addHolder(errorHolder);

// or add them as chain
[JWTDecodingBuilder decodeMessage:token].chain(chain);

也许你想尝试不同的秘密。

// possible that your algorithm has several secrets.
// you don't know which secret to use.
// but you want to decode it.
NSString *firstSecret = @"first";
NSArray *manySecrets = @[@"second", @"third", @"forty two"];
// translate to data
NSArray *manySecretsData = @[];
for (NSString *secret in manySecrets) {
    NSData *secretData = [JWTBase64Coder dataWithBase64UrlEncodedString:secret];
    if (secret) {
        manySecretsData = [manySecretsData arrayByAddingObject:secretData];
    }
}

NSString *algorithmName = JWTAlgorithmNameHS384;

id <JWTAlgorithmDataHolderProtocol> firstHolder = [JWTAlgorithmHSFamilyDataHolder new].algorithmName(algorithmName).secret(firstSecret);

// lets create chain
JWTAlgorithmDataHolderChain *chain = [JWTAlgorithmDataHolderChain chainWithHolder:firstHolder];

// and lets populate chain with secrets.
NSLog(@"chain has: %@", chain.debugDescription);

JWTAlgorithmDataHolderChain *expandedChain = [chain chainByPopulatingAlgorithm:firstHolder.currentAlgorithm withManySecretData:manySecretsData];

// now we have expanded chain with many secrets and one algorithm.
NSLog(@"expanded chain has: %@", expandedChain.debugDescription);

通过链解码和编码。

JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init];
// fill it
claimsSet.issuer = @"Facebook";
claimsSet.subject = @"Token";
claimsSet.audience = @"https://jwt.net.cn";

// encode it
NSString *secret = @"secret";
NSString *algorithmName = @"HS384";
NSDictionary *headers = @{@"custom":@"value"};

id<JWTAlgorithmDataHolderProtocol>holder = [JWTAlgorithmHSFamilyDataHolder new].algorithmName(algorithmName).secret(secret);

JWTCodingResultType *result = [JWTEncodingBuilder encodeClaimsSet:claimsSet].headers(headers).addHolder(holder).result;

NSString *encodedToken = result.successResult.encoded;
if (result.successResult) {
    // handle encoded result
    NSLog(@"encoded result: %@", result.successResult.encoded);
}
else {
    // handle error
    NSLog(@"encode failed, error: %@", result.errorResult.error);
}

// decode it
// you can set any property that you want, all properties are optional
JWTClaimsSet *trustedClaimsSet = [claimsSet copy];

NSNumber *options = @(JWTCodingDecodingOptionsNone);
NSString *yourJwt = encodedToken; // from previous example
JWTCodingResultType *decodedResult = [JWTDecodingBuilder decodeMessage:yourJwt].claimsSet(claimsSet).addHolder(holder).options(options).and.result;

if (decodedResult.successResult) {
    // handle decoded result
    NSLog(@"decoded result: %@", decodedResult.successResult.headerAndPayloadDictionary);
    NSLog(@"headers: %@", decodedResult.successResult.headers);
    NSLog(@"payload: %@", decodedResult.successResult.payload);
}
else {
    // handle error
    NSLog(@"decode failed, error: %@", decodedResult.errorResult.error);
}

从Pem文件加载的密钥。

你在pem文件中有一个密钥。你想要直接用它来签名/验证。假设"public_rsa.pem"和"private_rsa.pem"是pem格式的公开和私有密钥。

// Load keys
- (NSString *)pemKeyStringFromFileWithName:(NSString *)string inBundle:(NSBundle *)bundle {
    NSURL *fileURL = [bundle URLForResource:name withExtension:@"pem"];
    NSError *error = nil;
    NSString *fileContent = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:&error];
    if (error) {
        NSLog(@"%@ error: %@", self.debugDescription, error);
        return nil;
    }
}

// Sign and verify
- (void)signAndVerifyWithPrivateKeyPemString:(NSString *)privateKey publicKeyPemString:(NSString *)publicKey privateKeyPassphrase:(NSString *)passphrase {
    NSString *algorithmName = @"RS256";

    id <JWTAlgorithmDataHolderProtocol> signDataHolder = [JWTAlgorithmRSFamilyDataHolder new].keyExtractorType([JWTCryptoKeyExtractor privateKeyWithPEMBase64].type).privateKeyCertificatePassphrase(passphrase).algorithmName(algorithmName).secret(privateKey);

    id <JWTAlgorithmDataHolderProtocol> verifyDataHolder = [JWTAlgorithmRSFamilyDataHolder new].keyExtractorType([JWTCryptoKeyExtractor publicKeyWithPEMBase64].type).algorithmName(algorithmName).secret(publicKey);

    // sign
    NSDictionary *payloadDictionary = @{@"hello": @"world"};

    JWTCodingBuilder *signBuilder = [JWTEncodingBuilder encodePayload:payloadDictionary].addHolder(signDataHolder);
    JWTCodingResultType *signResult = signBuilder.result;
    NSString *token = nil;
    if (signResult.successResult) {
        // success
        NSLog(@"%@ success: %@", self.debugDescription, signResult.successResult.encoded);
        token = signResult.successResult.encoded;
    }
    else {
        // error
        NSLog(@"%@ error: %@", self.debugDescription, signResult.errorResult.error);
    }

    // verify
    if (token == nil) {
        NSLog(@"something wrong");
    }

    JWTCodingBuilder *verifyBuilder = [JWTDecodingBuilder decodeMessage:token].addHolder(verifyDataHolder);
    JWTCodingResultType *verifyResult = verifyBuilder.result;
    if (verifyResult.successResult) {
        // success
        NSLog(@"%@ success: %@", self.debugDescription, verifyResult.successResult.payload);
        token = verifyResult.successResult.encoded;
    }
    else {
        // error
        NSLog(@"%@ error: %@", self.debugDescription, verifyResult.errorResult.error);
    }
}

2.0版本中的实验

白名单中的可能算法。

当您需要用多个算法解码JWT时,可以在白名单中指定它们的名字。稍后此功能可能将迁移到选项。例如,有人返回结果或错误。

限制

由于唯一 secret 的限制,只限于配对(算法或无)。

NSString *jwtResultOrError = /*...*/;
NSString *secret = @"secret";
JWTBuilder *builder = [JWT decodeMessage:jwtResultOrError].secret(@"secret").whitelist(@[@"HS256", @"none"]);
NSDictionary *decoded = builder.decode;
if (builder.jwtError) {
    // oh!
}
else {
    NSDictionary *payload = decoded[@"payload"];
    NSDictionary *header = decoded[@"header"];
    NSArray *tries = decoded[@"tries"]; // will be evolded into something appropriate later.
}

版本2.0的新特性

  • 旧的普通样式已弃用。
  • 请改为使用现代流畅风格。
NSDictionary *payload = @{@"foo" : @"bar"};
NSString *secret = @"secret";
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];
// Deprecated
[JWT encodePayload:payload withSecret:secret algorithm:algorithm];

// Modern
[JWTBuilder encodePayload:payload].secret(secret).algorithm(algorithm).encode;

安装

将以下内容添加到您的 CocoaPods Podfile

pod "JWT"

通过 Cartfile 进行安装

github "yourkarma/JWT" "master"

import JWT

文档

用法

JWTBuilder

要编码和解码JWT,请使用带有 JWTBuilder 接口的流畅风格

+ (JWTBuilder *)encodePayload:(NSDictionary *)payload;
+ (JWTBuilder *)encodeClaimsSet:(JWTClaimsSet *)claimsSet;
+ (JWTBuilder *)decodeMessage:(NSString *)message;

如您所见,JWTBuilder同时提供了解码和解码的接口。

注意:一些属性仅用于编码或解码。

#pragma mark - Encode only
*payload;
*headers;
*algorithm;

#pragma mark - Decode only
*message
*options // as forcedOption from jwt decode functions interface.
*whitelist  //optional array of algorithm names to whitelist for decoding

您可以通过前缀为jwt的属性检查JWTBuilder。

您可以通过流畅样式(块接口)设置JWTBuilder属性。

您可以这样编码任意负载

NSDictionary *payload = @{@"foo" : @"bar"};
NSString *secret = @"secret";
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];

[JWTBuilder encodePayload:payload].secret(@"secret").algorithm(algorithm).encode;

如果您正在使用保留声明名称,您可以像这样编码声明集(所有属性都是可选的)

JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init];
claimsSet.issuer = @"Facebook";
claimsSet.subject = @"Token";
claimsSet.audience = @"http://yourkarma.com";
claimsSet.expirationDate = [NSDate distantFuture];
claimsSet.notBeforeDate = [NSDate distantPast];
claimsSet.issuedAt = [NSDate date];
claimsSet.identifier = @"thisisunique";
claimsSet.type = @"test";

NSString *secret = @"secret";
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];

[JWTBuilder encodeClaimsSet:claimsSet].secret(secret).algorithm(algorithm).encode;

您可以像这样解码JWT

NSString *jwtToken = @"header.payload.signature";
NSString *secret = @"secret";
NSString *algorithmName = @"HS256"; //Must specify an algorithm to use

NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secret(secret).algorithmName(algorithmName).decode;

如果您在解码时想检查声明,可以使用以下代码示例(所有属性都是可选的)

// Trusted Claims Set
JWTClaimsSet *trustedClaimsSet = [[JWTClaimsSet alloc] init];
trustedClaimsSet.issuer = @"Facebook";
trustedClaimsSet.subject = @"Token";
trustedClaimsSet.audience = @"http://yourkarma.com";
trustedClaimsSet.expirationDate = [NSDate date];
trustedClaimsSet.notBeforeDate = [NSDate date];
trustedClaimsSet.issuedAt = [NSDate date];
trustedClaimsSet.identifier = @"thisisunique";
trustedClaimsSet.type = @"test";

NSString *message = @"encodedJwt";
NSString *secret = @"secret";
NSString *algorithmName = @"chosenAlgorithm"

JWTBuilder *builder = [JWTBuilder decodeMessage:jwt].secret(secret).algorithmName(algorithmName).claimsSet(trustedClaimsSet);
NSDictionary *payload = builder.decode;

if (builder.jwtError == nil) {
    // do your work here
}
else {
    // handle error
}

如果您想强制使用有效的算法列表

NSArray *whitelist = @[@"HS256", @"HS512"];
NSString *jwtToken = @"header.payload.signature";
NSString *secret = @"secret";
NSString *algorithmName = @"HS256";

//Returns nil
NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secret(secret).algorithmName(algorithmName).whitelist(@[]).decode;

//Returns the decoded payload
NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secret(secret).algorithmName(algorithmName).whitelist(whitelist).decode;

编码/解码示例

// suppose, that you create ClaimsSet
JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init];
// fill it
claimsSet.issuer = @"Facebook";
claimsSet.subject = @"Token";
claimsSet.audience = @"http://yourkarma.com";

// encode it
NSString *secret = @"secret";
NSString *algorithmName = @"HS384";
NSDictionary *headers = @{@"custom":@"value"};
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:algorithmName];

JWTBuilder *encodeBuilder = [JWT encodeClaimsSet:claimsSet];
NSString *encodedResult = encodeBuilder.secret(secret).algorithm(algorithm).headers(headers).encode;

if (encodeBuilder.jwtError == nil) {
    // handle encoded result
    NSLog(@"encoded result: %@", encodedResult);
}
else {
    // handle error
    NSLog(@"encode failed, error: %@", encodeBuilder.jwtError);
}

// decode it
// you can set any property that you want, all properties are optional
JWTClaimsSet *trustedClaimsSet = [claimsSet copy];

// decode forced ? try YES
BOOL decodeForced = NO;
NSNumber *options = @(decodeForced);
NSString *yourJwt = encodedResult; // from previous example
NSString *yourSecret = secret; // from previous example
NSString *yourAlgorithm = algorithmName; // from previous example
JWTBuilder *decodeBuilder = [JWT decodeMessage:yourJwt];
NSDictionary *decodedResult = decodeBuilder.message(yourJwt).secret(yourSecret).algorithmName(yourAlgorithm).claimsSet(trustedClaimsSet).options(options).decode;
if (decodeBuilder.jwtError == nil) {
    // handle decoded result
    NSLog(@"decoded result: %@", decodedResult);
}
else {
    // handle error
    NSLog(@"decode failed, error: %@", decodeBuilder.jwtError);
}

NSData

您还可以使用表示为NSData对象的秘密进行编码/解码。

//Encode
NSData *secretData = "<your data>";
NSString *algorithmName = @"HS384";
NSDictionary *headers = @{@"custom":@"value"};
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:algorithmName];

JWTBuilder *encodeBuilder = [JWT encodeClaimsSet:claimsSet];
NSString *encodedResult = encodeBuilder.secretData(secretData).algorithm(algorithm).headers(headers).encode;

//Decode
NSString *jwtToken = @"header.payload.signature";
NSData *secretData = "<your data>"
NSString *algorithmName = @"HS256"; //Must specify an algorithm to use

NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secretData(secretData).algorithmName(algorithmName).decode;

算法

以下算法得到支持

  • RS256
  • HS512 - 使用SHA-512的HMAC。
  • HS256 / HS384 / HS512
  • None

RS256用法。

例如,您有一个包含私钥的文件:file.p12。并且您为此文件有一个秘密通行短语:secret

// Encode
NSDictionary *payload = @{@"payload" : @"hidden_information"};
NSString *algorithmName = @"RS256";

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"secret_key" ofType:@"p12"];
NSData *privateKeySecretData = [NSData dataWithContentsOfFile:filePath];

NSString *passphraseForPrivateKey = @"secret";

JWTBuilder *builder = [JWTBuilder encodePayload:payload].secretData(privateKeySecretData).privateKeyCertificatePassphrase(passphraseForPrivateKey).algorithmName(algorithmName);
NSString *token = builder.encode;

// check error
if (builder.jwtError == nil) {
    // handle result
}
else {
    // error occurred.
}

// Decode
// Suppose, that you get token from previous example. You need a valid public key for a private key in previous example.
// Private key stored in @"secret_key.p12". So, you need public key for that private key.
NSString *publicKey = @"..."; // load public key. Or use it as raw string.

algorithmName = @"RS256";

JWTBuilder *decodeBuilder = [JWTBuilder decodeMessage:token].secret(publicKey).algorithmName(algorithmName);
NSDictionary *envelopedPayload = decodeBuilder.decode;

// check error
if (decodeBuilder.jwtError == nil) {
    // handle result
}
else {
    // error occurred.
}

可以通过实现JWTAlgorithm协议添加其他算法。

在提交请求之前

在提交请求之前,请阅读贡献说明

由...支持

JetBrains