swift 添加JWT验证

简要

因项目请求安全问题,需要加入JWT验证,而相关的文档上给出的说明都是sh256的加密方式,但是对应道swift中或者OC中加密出来的终是与服务器验证通不过,很恼火,网上各种说法,以及代码,坑特别多
此地的sha256加密不纯粹是HMACSHa256加密,若是网上单纯的HMACSHa256加密,那你就进入误区,出不来了。
还有一个就是base64UrlEncode转码后的符号转换问题,不是所有的符号都转换为下横线(_)的切记切记

什么是JSON Web令牌?

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑和自包含的方式,用于将各方之间安全地将信息作为JSON对象传输。这些信息可以被验证和信任,因为它是数字签名的。JWT可以使用秘密(使用HMAC算法)或使用RSAECDSA对公钥/私钥对进行签名。

虽然JWT可以加密,以提供各方之间的保密性,但我们将专注于签名令牌。签名令牌可以验证其中包含的索赔的完整性,而加密令牌则向其他方隐藏这些索赔。当令牌使用公钥/私钥对签名时,签名还证明只有持有私钥的一方才是签署私钥的一方。

您应该何时使用JSON Web令牌?

以下是JSON Web令牌有用的一些场景:

  • 授权:这是使用JWT最常见的场景。用户登录后,每个后续请求都将包括JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是目前广泛使用JWT的一项功能,因为它的开销很小,并且能够轻松在不同领域使用。
  • 信息交换:JSON Web令牌是各方之间安全传输信息的好方法。因为JWT可以签名——例如,使用公钥/私钥对——您可以确定发件人就是他们所说的那个人。此外,由于签名是使用标题和有效负载计算的,您还可以验证内容是否未被篡改。

JSON Web令牌结构是什么?

JSON Web令牌以紧凑的形式由三个部分组成,由点(.)分隔,即:

  • 标题
  • 有效载荷
  • 签名

因此,JWT通常如下所示。

xxxxx.yyyyy.zzzzz

标题header

标头通常由两部分组成:令牌的类型(即JWT)和使用的签名算法(如HMAC SHA256或RSA)。

例如:

{
  "alg": "HS256",
  "typ": "JWT"
}
#注意 若是其他加密方式 替换 alg

然后,此JSON是Base64Url编码的,以形成JWT的第一部分。

有效载荷payload

令牌的第二部分是有效负载,其中包含索赔。索赔是关于实体(通常是用户)和其他数据的陈述。有三种类型的索赔:注册索赔、公共索赔和私人索赔。

也就是鉴权信息实体

{
  "uid": "20210001",
  "source": "ios",
  "token": "7ab8baac69b518faccc78***d5ee9cba",
  "exp": 1636290555
}
#根据实际情况,各自需求

然后对有效负载进行Base64Url编码,以形成JSON Web令牌的第二部分。

请注意,对于签名令牌,这些信息虽然可以防止篡改,但任何人都可以阅读。除非加密,否则不要将秘密信息放入JWT的有效负载或标头元素中。

签名autograph

要创建签名部分,您必须使用编码标头、编码有效负载、秘密、标头中指定的算法并签名。

例如,如果您想使用HMAC SHA256算法,签名将以以下方式创建:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

注意

1.此地的 HMACSHA256方法在swift中是没有的,所以需要自己定义,方法如下
2. header与payload都要转换为base64UrlEncode
base64 与 base64UrlEncode 虽然都是string 但是不是一个东西
extension Dictionary {
    /// 字典转换为data
    /// - Returns: data
    func toData() -> Data? {
        if !JSONSerialization.isValidJSONObject(self) {
            return nil
        }
        do {
            let data = try JSONSerialization.data(withJSONObject: self, options: [])
            return data
        } catch let error {
            DLog(error.localizedDescription)
            return nil
        }
    }
    func base64String()->String{
      return  ((toData()?.base64EncodedString() ?? "")).replacingOccurrences(of: "+", with: "-")
            .replacingOccurrences(of: "/", with: "_")
            .replacingOccurrences(of: "=", with: "")
    }
}



import CommonCrypto
enum CryptoAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

    var HMACAlgorithm: CCHmacAlgorithm {
        var result: Int = 0
        switch self {
        case .MD5: result = kCCHmacAlgMD5
        case .SHA1: result = kCCHmacAlgSHA1
        case .SHA224: result = kCCHmacAlgSHA224
        case .SHA256: result = kCCHmacAlgSHA256
        case .SHA384: result = kCCHmacAlgSHA384
        case .SHA512: result = kCCHmacAlgSHA512
        }
        return CCHmacAlgorithm(result)
    }

    var digestLength: Int {
        var result: Int32 = 0
        switch self {
        case .MD5: result = CC_MD5_DIGEST_LENGTH
        case .SHA1: result = CC_SHA1_DIGEST_LENGTH
        case .SHA224: result = CC_SHA224_DIGEST_LENGTH
        case .SHA256: result = CC_SHA256_DIGEST_LENGTH
        case .SHA384: result = CC_SHA384_DIGEST_LENGTH
        case .SHA512: result = CC_SHA512_DIGEST_LENGTH
        }
        return Int(result)
    }
}

extension String {
    func jwtHmac(algorithm: CryptoAlgorithm, key: String) -> String {
        guard let dataAB = data(using: .utf8, allowLossyConversion: false),
              let datas = key.data(using: .utf8, allowLossyConversion: false) else {
            return ""
        }

        let ctx = UnsafeMutablePointer.allocate(capacity: 1)
        defer { ctx.deallocate() }
        datas.withUnsafeBytes({ CCHmacInit(ctx, algorithm.HMACAlgorithm, $0.baseAddress, size_t(datas.count)) })
        dataAB.withUnsafeBytes({ CCHmacUpdate(ctx, $0.baseAddress, size_t(dataAB.count)) })
        var hmac = Array(repeating: 0, count: algorithm.digestLength)
        CCHmacFinal(ctx, &hmac)

        let strHmac = Data(hmac).base64EncodedString().replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "=", with: "")
        return strHmac
    }
}

使用方法:

      #根据你实际的需求来操作
            let dic:[String:Any] = [
                "sub": "1234567890",
                "name":"John Doe",
                "iat":1516239022,
            ]
           
          #得到 base64UrlEncode
            let playload = dic.base64String()
            #得到 base64UrlEncode
            let header = ["typ":"JWT","alg":"HS256"].base64String()
            #拼接 以(.)隔开
            let strAB = "\(header).\(playload)"
          # 根据服务器给的密钥进行加密 
            let sha256 = strAB.jwtHmac(algorithm: .SHA256, key: APPJWt)
            #拼接 以(.)隔开
            let autograph = (strAB + "." + sha256)
           #得到发送给服务器的三段式如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
            headers.add(name: "Member", value: autograph)

JWT官网有详细的介绍

你可能感兴趣的:(swift 添加JWT验证)