详解 Json Web Token (如何为Flutter开发一个简单的JWT解析库)

栏目: IOS · Android · 发布时间: 5年前

内容简介:简单解释即:JWT是一个紧凑的、URL安全的、用于在双方之间传输claims的这样一个方法。而这个claims是用JSON格式编码的,并且进行了数字签名。claim - 声明;宣称;断言;(尤指对财产、土地等要求拥有的)所有权;(尤指向公司、政府等)索款,索赔。让我们看一看具体的JWT长什么样子,就知道这个单词用的其实是相当贴切的。
JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).

简单解释即:JWT是一个紧凑的、URL安全的、用于在双方之间传输claims的这样一个方法。而这个claims是用JSON格式编码的,并且进行了数字签名。

claim - 声明;宣称;断言;(尤指对财产、土地等要求拥有的)所有权;(尤指向公司、政府等)索款,索赔。让我们看一看具体的JWT长什么样子,就知道这个单词用的其实是相当贴切的。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpZDEyMyIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE2MjM5MDk5fQ.8HLeWIBn5d87r-XItgQJOnwqYGjJYrpKmz-2eC9fb8A

这个就是一个JWT的串,它分为3个部分,分别由两个 . 隔开,这三个部分分别是:

Header

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

// 是下面这个JSON object的base64编码,它是JWT的头信息
// 表明了这个JWT用的是哪种签名算法,以及类型(JWT)

{
  "alg": "HS256",
  "typ": "JWT"
}

复制代码

Payload

eyJpc3MiOiJpZDEyMyIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE2MjM5MDk5fQ

// 是羡慕这个JSON object的base64编码

{
  "iss": "id123",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516239099
}

复制代码

Payload里放的其实就是claims(声明),在使用中。JWT一般都是由服务器返回给客户端的,而客户端每次请求时都会带上这个JWT串,服务端通过解析这个Payload里的内容,就知道这个请求来自于哪个用户以及一些其他用户基本信息。

上面的这四个字段 iss , name , iat , exp ,除了 name 都是RFC7519定义的一些 Registered Claim Names, 可以认为标准的claim名字。 iss 表示这个token是由谁签发的, iat token签发的时间, exp token过期时间。我们也可以像 name 一样,加一些自身逻辑需要的字段。

那么为什么说claim这个单词用的很贴切呢?首先,payload里往往会是一些和身份认证、资源访问权限相关的内容;其次,payload字段经过base64解码之后,完全都是明文,任何人都可以修改,甚至伪造。也就是说任何人都可以通过伪造payload的内容来声称自己是谁、或者具有何种权限,这都只是单方面的声明,并不一定是有效/合法的。而要想知道是否合法,则需要第三个部分:

Signature

8HLeWIBn5d87r-XItgQJOnwqYGjJYrpKmz-2eC9fb8A

// 计算HMAC SHA-256签名值, 之后进行base64编码
base64urlsafeencode(
(HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)
)
复制代码

有了签名,只需要对收到的JWT再进行一次签名校验就能知道这个JWT是不是合法的了。HS256(HMACSHA256)是jwt支持的签名算法之一,其他的详见RFC7519

JSON in Flutter

接上文的Payload:

{
  "iss": "id123",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516239099
}
复制代码

要解析这条数据,我们需要dart:convert为提供的 jsonDecode 函数:

import 'dart:convert';

Map<String, dynamic> payload = jsonDecode(jsonString);

print('${payload["iss"]} and ${payload["name"]}');
复制代码

jsonDecode 函数签名返回值是一个 dynamic 类型, dynamic 可以是任意类型,字符串,数字,列表等。很明显, jsonDecode 需要能解析所有的合法 JSON 类型,它的返回值可以是多种不同的类型。而在我们的例子里, payload 是一个 Map<String, dynamic> ,因为我们知道 jsonString 是一个 JSON 的对象类型。

json编码也是同样简单:

var data = jsonEncode(payload);
// data == '{"iss":"id123","name":"John Doe","iat":1516239022,"exp":1516239099}'
复制代码

除了以上手动方法之外,我们还可以用json_serializable来自动生成这些解析代码。

自动生成解析

首先你的pubspec.yaml需要一些额外的库

dependencies:
  json_annotation: ^2.0.0

dev_dependencies:
  build_runner: ^1.0.0
  json_serializable: ^2.0.0
复制代码

定义Claim数据类(claim.dart)

import 'package:json_annotation/json_annotation.dart';

// 文件内容将会由 工具 自动生成
part 'claim.g.dart';


@JsonSerializable()
class Claim {
  Claim(this.iss, this.name, this.iat, this.exp);

  String iss;
  String name;
  int iat;
  int exp;

  // 从json创建类对象的工厂函数,_$ClaimFromJson将会被定义在 claim.g.dart 文件中
  factory Claim.fromJson(Map<String, dynamic> json) => _$ClaimFromJson(json);

  // 将对象转成json,_$ClaimToJson将会被定义在 claim.g.dart中
  Map<String, dynamic> toJson() => _$ClaimToJson(this);
}
复制代码

代码生成

运行 flutter packages pub run build_runner build 。 Done,你现在可以像这样使用Claim与json之间的转换了:

// decode
Map map = jsonDecode(jsonString);
var claim = Claim.fromJson(map);

// encode
String json = jsonEncode(claim);
复制代码

生成JWT串

import 'dart:convert';
import 'package:crypto/crypto.dart';

var header = jsonEncode(<String, dynamic>{
 "alg": "HS256",
 "typ": "JWT"
});

Claim claim = Claim("id123", "John Doe",1516239022,1516239099);
String payload = jsonEncode(claim);

var headerBase64 = base64Url.encode(utf8.encode(header));
var claimBase64 = base64Url.encode(utf8.encode(payload));

var key = utf8.encode('secret');
var bytes = utf8.encode(headerBase64 + "." + claimBase64);

var hmacSha256 = new Hmac(sha256, key); // HMAC-SHA256
var digest = hmacSha256.convert(bytes);

var signature = base64Url.encode(digest.bytes);


print("Just get a fresh new jwt: $headerBase64.$claimBase64.$signature");

复制代码

我们把这写功能稍微封装一下,就是一个简单的jwt编码函数,可以处理任何 @JsonSerializable() 注解的类实例。

String encodeJWT(dynamic obj) {
  var header = jsonEncode(<String, dynamic>{"alg": "HS256", "typ": "JWT"});

  String payload = jsonEncode(obj);

  var headerBase64 = base64Url.encode(utf8.encode(header));
  var claimBase64 = base64Url.encode(utf8.encode(payload));

  var key = utf8.encode('secret');
  var bytes = utf8.encode(headerBase64 + "." + claimBase64);

  var hmacSha256 = new Hmac(sha256, key); // HMAC-SHA256
  var digest = hmacSha256.convert(bytes);

  var signature = base64Url.encode(digest.bytes);

  return "$headerBase64.$claimBase64.$signature";
}
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网络素养

网络素养

[美]霍华德·莱茵戈德 / 张子凌、老卡 / 译言·东西文库/电子工业出版社 / 2013-8-1 / 49.80元

有人说Google让我们变得更笨,有人说Facebook出卖了我们的隐私,有人说Twitter将我们的注意力碎片化。在你担忧这些社会化媒体让我们变得“浅薄”的时候,有没问过自己,是否真正地掌握了使用社会化媒体的方式? 这本书将介绍五种正在改变我 们这个世界的素养:注意力、 对垃圾信息的识别能力、参与力、协作力和联网智慧。当有足够多的人学会并且能够熟练的使用这些技术,成为真正的数字公民后。健康......一起来看看 《网络素养》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码