释义
JSON Web Token(JWT)是一个开放标准,利用JSON对象安全地传输信息。大部分的语言或平台都可以按照其规定的标准去实现,本文以iOS为例。
而实际上,JWT就是一串字符串,不过包含了信息的token字符串。使用JWT就是为了安全,关于加密部分不清楚的可以查看这里。
组成
它由三部分组成:Header(头部)、Payload(载荷)、Signature(签名)
Header
json内容:
{
"typ": "JWT",
"alg": "HS256"
}
- typ:类型,一般固定写JWT
- alg:签名部分使用的算法,通常是2种,接收者可以解析此条信息来
- HS256:对称加密算法,双方使用同一个密钥加密解密
- RS256:非对称加密算法,私钥加密,公钥解密
将{ "typ": "JWT", "alg": "HS256" }进行Base64编码得到一串字符串:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
这串字符串就是Header,不同语言不同的平台得到的字符串可能不一样,但是解码之后应该是一样的。
Payload
这部分就是主体信息,是个JSON对象,包含以下内容:
{
"iss": "RC",
"iat": 1441593502,
"exp": 1441594722,
"aud": "Server",
"sub": "member",
"info_A": "这是需要传递的信息A",
"info_B": "这是需要传递的信息B",
...
}
以下7个字段是JWT的标准所定义的。
- iss(issuer):签发者
- iat(issued at):在什么时候签发的
- exp(expires):什么时候过期,一般这里是一个时间戳
- aud:接收者
- sub:该JWT所面向的用户
- nbf:生效时间
- jti:编号
在此主体的JSON对象中,内容可以自行约定或添加,因为会暴露,所以不应该在载荷里面添加入任何敏感的内容,或者将内容加密后再添加。
将主体的JSON对象进行Base64编码也得到一串字符串:
eyJlbmQiOiIxNjc3ODYyMzA5Iiwic3RhcnQiOiIxNjc3Nzc1OTA5IiwidXNlcmUiOiIxMDExIiwiaWF0IjoiMTY3Nzc3NTkwOSJ9
这串字符串就是Payload
Signature
这部分是JWT的签名,也是保证整个JWT在传递过程中不会被篡改。
将前面得到的Header和Payload用点.拼接在一起(头部在前),即:
Header.Payload
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbmQiOiIxNjc3ODYyMzA5Iiwic3RhcnQiOiIxNjc3Nzc1OTA5IiwidXNlcmUiOiIxMDExIiwiaWF0IjoiMTY3Nzc3NTkwOSJ9
将拼接的字符串使用HS256算法进行加密,当然还需要约定一个密钥,用密钥进行加密,得到字符串:
DI8b_CLI5MRikkp4fLRogSm-VNc0Fc1v2XZ6IgHZMMI
这串字符串就是Signature。
此时我们将加密后的字符串用点.拼接在最后(Header.Payload.Signature)得到了完整的JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbmQiOiIxNjc3ODYyMzA5Iiwic3RhcnQiOiIxNjc3Nzc1OTA5IiwidXNlcmUiOiIxMDExIiwiaWF0IjoiMTY3Nzc3NTkwOSJ9.DI8b_CLI5MRikkp4fLRogSm-VNc0Fc1v2XZ6IgHZMMI
实际上 JWT = Header.Payload.Signature。
整体:
关于其他加密方法看这里。
完整代码
struct JWT {
/// 生成JWT
func creatJWT() -> String {
// 头部
let preHeader = ["typ":"JWT","alg":"HS256"]
let header = base64String(data: preHeader)
// 载荷
// 当前时间
let nowDate = Date().now
// 当前时间戳
let nowDateTimeInterval = CLongLong(round(nowDate.timeIntervalSince1970))
// exp时间戳
let expDate = nowDate.addingTimeInterval(TimeInterval(3600))
let expDateTimeInterval = CLongLong(round(endDate.timeIntervalSince1970))
let prePayload = [
"iat" : "\(nowDateTimeInterval)",
"exp" : "\(expDateTimeInterval)",
"userid" : "001"
]
let payload = base64String(data: prePayload)
// 签名
let preSign = header + "." + payload
// 密钥
let key = "key=world peace"
let sign = hMac(string: preSign, key: key)
return preSign + "." + sign
}
/// 编码
private func base64String(data: [String : String]) -> String {
let data = try? JSONSerialization.data(withJSONObject: data,options: [])
let str = data!.base64EncodedString().replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
return str
}
/// 256哈希
private func hMac(string: String, key: String) -> String{
guard let dataString = string.data(using: .utf8, allowLossyConversion: false),
let datas = key.data(using: .utf8, allowLossyConversion: false) else {
return ""
}
let ctx = UnsafeMutablePointer<CCHmacContext>.allocate(capacity: 1)
defer { ctx.deallocate() }
datas.withUnsafeBytes({ CCHmacInit(ctx, CCHmacAlgorithm(kCCHmacAlgSHA256), $0.baseAddress, size_t(datas.count)) })
dataString.withUnsafeBytes({ CCHmacUpdate(ctx, $0.baseAddress, size_t(dataString.count)) })
var hmac = Array<UInt8>(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmacFinal(ctx, &hmac)
let strHmac = Data(hmac).base64EncodedString().
replacingOccurrences(of: "+", with: "-").
replacingOccurrences(of: "/", with: "_").
replacingOccurrences(of: "=", with: "")
return strHmac
}
}