如何在 Swift 中解码 JWT(JSON Web 令牌)令牌?

Posted

技术标签:

【中文标题】如何在 Swift 中解码 JWT(JSON Web 令牌)令牌?【英文标题】:How can I decode JWT (JSON web token) token in Swift? 【发布时间】:2017-04-16 09:13:24 【问题描述】:

我有一个这样的 JWT 令牌

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

我怎样才能解码这个,以便我可以得到这样的有效载荷

"sub": "1234567890", "name": "John Doe", "admin": true

【问题讨论】:

感谢@SiddharthaChikatamalla 的提问……让我的谷歌搜索时间大大缩短! 【参考方案1】:

如果您可以使用库,我建议您使用 https://github.com/auth0/JWTDecode.swift

然后导入库import JWTDecode并执行。

let jwt = try decode(jwt: token)

由于您不想包含此库,因此我带出了所需的部分以使其正常工作。

func decode(jwtToken jwt: String) -> [String: Any] 
  let segments = jwt.components(separatedBy: ".")
  return decodeJWTPart(segments[1]) ?? [:]


func base64UrlDecode(_ value: String) -> Data? 
  var base64 = value
    .replacingOccurrences(of: "-", with: "+")
    .replacingOccurrences(of: "_", with: "/")

  let length = Double(base64.lengthOfBytes(using: String.Encoding.utf8))
  let requiredLength = 4 * ceil(length / 4.0)
  let paddingLength = requiredLength - length
  if paddingLength > 0 
    let padding = "".padding(toLength: Int(paddingLength), withPad: "=", startingAt: 0)
    base64 = base64 + padding
  
  return Data(base64Encoded: base64, options: .ignoreUnknownCharacters)


func decodeJWTPart(_ value: String) -> [String: Any]? 
  guard let bodyData = base64UrlDecode(value),
    let json = try? JSONSerialization.jsonObject(with: bodyData, options: []), let payload = json as? [String: Any] else 
      return nil
  

  return payload

这样称呼它:

decode(jwtToken: TOKEN)

【讨论】:

感谢您的回复。但是我不能使用 JWT 库,我在一个框架中实现它,我想要一个简单的实现。在 android 我使用 String[] split = JWTEncoded.split("\\.");字符串主体 = getJson(split[1]); Log.d("JWT_DECODED", "正文:" + 正文);私有静态字符串 getJson(String strEncoded) 抛出 UnsupportedEncodingException byte[] decodedBytes = Base64.decode(strEncoded, Base64.URL_SAFE);返回新字符串(decodedBytes,“UTF-8”); 任何其他至少有 Swift 4 的库? 不错的代码 sn-p。我从中创建了一个带有静态函数的JWTDecoder struct 并将throws 添加到需要的位置。无需使用不必要的框架阻塞您的项目。【参考方案2】:

迭代 Viktor 的代码:

使用嵌套函数来保持更多模块化 如果通过了错误的令牌或其他错误,则使用异常。 更简单的填充计算和填充函数的利用。

希望有用:

func decode(jwtToken jwt: String) throws -> [String: Any] 

    enum DecodeErrors: Error 
        case badToken
        case other
    

    func base64Decode(_ base64: String) throws -> Data 
        let base64 = base64
            .replacingOccurrences(of: "-", with: "+")
            .replacingOccurrences(of: "_", with: "/")
        let padded = base64.padding(toLength: ((base64.count + 3) / 4) * 4, withPad: "=", startingAt: 0)
        guard let decoded = Data(base64Encoded: padded) else 
            throw DecodeErrors.badToken
        
        return decoded
    

    func decodeJWTPart(_ value: String) throws -> [String: Any] 
        let bodyData = try base64Decode(value)
        let json = try JSONSerialization.jsonObject(with: bodyData, options: [])
        guard let payload = json as? [String: Any] else 
            throw DecodeErrors.other
        
        return payload
    

    let segments = jwt.components(separatedBy: ".")
    return try decodeJWTPart(segments[1])

【讨论】:

最佳答案 IMO。 JWT 使用“base64url”编码(不是常规的“base64”编码),这就是为什么需要替换 -_ 的原因。如果您只解析声明部分(它们通常不以纯文本形式出现),从统计上看不太可能看到它们,但如果您正在解析原始数据(如签名),您会一直看到它们。 好的,感谢您的反馈,更新了代码。【参考方案3】:
    func decode(_ token: String) -> [String: AnyObject]? 
    let string = token.components(separatedBy: ".")
    let toDecode = string[1] as String


    var stringtoDecode: String = toDecode.replacingOccurrences(of: "-", with: "+") // 62nd char of encoding
    stringtoDecode = stringtoDecode.replacingOccurrences(of: "_", with: "/") // 63rd char of encoding
    switch (stringtoDecode.utf16.count % 4) 
    case 2: stringtoDecode = "\(stringtoDecode)=="
    case 3: stringtoDecode = "\(stringtoDecode)="
    default: // nothing to do stringtoDecode can stay the same
        print("")
    
    let dataToDecode = Data(base64Encoded: stringtoDecode, options: [])
    let base64DecodedString = NSString(data: dataToDecode!, encoding: String.Encoding.utf8.rawValue)

    var values: [String: AnyObject]?
    if let string = base64DecodedString 
        if let data = string.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: true) 
            values = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String : AnyObject]
        
    
    return values

【讨论】:

一般来说,如果答案包含对代码的用途的解释,以及为什么在不介绍其他人的情况下解决问题的原因,答案会更有帮助。 不想使用外部库,这就像一个魅力,谢谢:)【参考方案4】:

我已经找到了解决方案。

 static func getJwtBodyString(tokenstr: String) -> NSString 

    var segments = tokenstr.components(separatedBy: ".")
    var base64String = segments[1]
    print("\(base64String)")
    let requiredLength = Int(4 * ceil(Float(base64String.characters.count) / 4.0))
    let nbrPaddings = requiredLength - base64String.characters.count
    if nbrPaddings > 0 
        let padding = String().padding(toLength: nbrPaddings, withPad: "=", startingAt: 0)
        base64String = base64String.appending(padding)
    
    base64String = base64String.replacingOccurrences(of: "-", with: "+")
    base64String = base64String.replacingOccurrences(of: "_", with: "/")
    let decodedData = Data(base64Encoded: base64String, options: Data.Base64DecodingOptions(rawValue: UInt(0)))
  //  var decodedString : String = String(decodedData : nsdata as Data, encoding: String.Encoding.utf8)

    let base64Decoded: String = String(data: decodedData! as Data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))!
    print("\(base64Decoded)")
    return base64String as NSString

这对我很有用。谢谢你。

【讨论】:

【参考方案5】:

有一个快速的实施。如果您正在使用 CocoaPods,请将其添加到您的 Podfile 中,或者克隆项目并直接使用它。

JSONWebToken

do 
  // the token that will be decoded
  let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
  let payload = try JWT.decode(token, algorithm: .hs256("secret".data(using: .utf8)!))
  print(payload)
 catch 
  print("Failed to decode JWT: \(error)")

【讨论】:

【参考方案6】:

如果你想使用一个库,我建议你使用一些大人物流行的东西。 IBM 正在做 Kitura – Swift 后端框架,所以它的 JWT 编解码实现一定是一流的:

链接: https://github.com/IBM-Swift/Swift-JWT

带有有效期的令牌的简单用法

import SwiftJWT

struct Token: Decodable 
    let jwtString: String

    func abc() 

        do 
            let newJWT = try JWT<MyJWTClaims>(jwtString: jwtString)
            print(newJWT.claims.exp)
         catch 
            print("OH NOES")
        


    


struct MyJWTClaims: Claims 
    let exp: Date

【讨论】:

【参考方案7】:

Objective-C 版本的老歌:

NSLog(@"credential is %@", credential.identityToken);
NSString * string = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding];
NSArray * segments = [string componentsSeparatedByString:@"."];
NSMutableDictionary * JSON = [NSMutableDictionary new];
for (int n = 0; n < segments.count; n++)
    
    NSString * value = segments[n];
    NSString * base64 = [value stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
    base64 = [base64 stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
    NSUInteger length = [base64 lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    int requiredLength = 4 * ceil((float)length/4.0f);
    int paddingLength = requiredLength - (int)length;
    for (int n = 0; n < paddingLength; n++)
        base64 = [base64 stringByAppendingString:@"="];
    
    NSData * data = [[NSData alloc] initWithBase64EncodedString:base64 options:0];
    
    NSError * error;
    NSDictionary * local = [NSJSONSerialization JSONObjectWithData:data
                                                           options:NSJSONReadingAllowFragments
                                                             error:&error];
    [JSON addEntriesFromDictionary:local];


NSLog(@"JSON is %@", JSON);

【讨论】:

以上是关于如何在 Swift 中解码 JWT(JSON Web 令牌)令牌?的主要内容,如果未能解决你的问题,请参考以下文章

如何解码我在 Swift 中收到 REST API 的 JWT 令牌?

如何在 Swift 4 中解码 JSON(JSON Web 令牌)?

如何在 Swift 中解码这个 JSON 数据?

如何在 Swift 中解码具有许多唯一键的嵌套 JSON?

如何在 Swift 中使用动态键(在根级别)解码此 JSON?

如何在 Swift 中解码一个命名的 json 对象数组