博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详解 Json Web Token (如何为Flutter开发一个简单的JWT解析库)
阅读量:6446 次
发布时间:2019-06-23

本文共 4597 字,大约阅读时间需要 15 分钟。

什么是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都是定义的一些 Registered Claim Names, 可以认为标准的claim名字。iss表示这个token是由谁签发的, iat token签发的时间, exptoken过期时间。我们也可以像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支持的签名算法之一,其他的详见

JSON in Flutter

接上文的Payload:

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

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

import 'dart:convert';Map
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}'复制代码

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

自动生成解析

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

dependencies:  json_annotation: ^2.0.0dev_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
json) => _$ClaimFromJson(json); // 将对象转成json,_$ClaimToJson将会被定义在 claim.g.dart中 Map
toJson() => _$ClaimToJson(this);}复制代码

代码生成

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

// decodeMap map = jsonDecode(jsonString);var claim = Claim.fromJson(map);// encodeString json = jsonEncode(claim);复制代码

生成JWT串

import 'dart:convert';import 'package:crypto/crypto.dart';var header = jsonEncode(
{ "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-SHA256var 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(
{
"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";}复制代码

尾声

离真正的库还差一段距离,至少还缺少jwt验证(应该是超级简单的)、支持其他的签名算法(HS384/HS512/RS*/ES*)。就在我正在想应该怎么去写的时候,我突然想到,前端APP好像并没有处理/解析JWT的场景......

那我就写到这里吧,希望有人能觉得有用╮(╯▽╰)╭

转载于:https://juejin.im/post/5cc5a4766fb9a032321986ad

你可能感兴趣的文章
自己做一款简易的chrome扩展--清除页面广告
查看>>
node中非常重要的process对象,Child Process模块
查看>>
Webserver管理系列:3、Windows Update
查看>>
Linux内核源码详解——命令篇之iostat[zz]
查看>>
Sqlserver2000联系Oracle11G数据库进行实时数据的同步
查看>>
明年计划
查看>>
ORACLE功能GREATEST功能说明具体实例
查看>>
DataGridView 输入数据验证格式(实例)
查看>>
HDOJ 2151
查看>>
Foundation框架 - 快速创建跨平台的网站页面原型
查看>>
Intel 82599网卡异常挂死原因
查看>>
open-falcon
查看>>
三菱plc输出指示灯不亮怎么办(转载)
查看>>
doc2vec使用说明(一)gensim工具包TaggedLineDocument
查看>>
App测试中ios和Android的区别
查看>>
java.lang.NullPointerException&com.cb.action.LoginAction.execute(LoginAction.java:48)
查看>>
理解Docker :Docker 网络
查看>>
通过Application存取公共数据比如登录信息等..
查看>>
intellij maven配置与使用
查看>>
SpringMVC文件下载与JSON格式
查看>>