Oauth2系列7:授权码和访问令牌的颁发流程是怎样实现的?
官方网站地址:JSON Web Tokens - jwt.io
如果接触过jwt,可能没有看过这个网址,但是应该见过下面这个图(很多网上的文章都会引用);这个地址是用来解析jwt_token结构体的工具
所以,就正式来看下官方给的定义:
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
翻译过来大致就是
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。
从上面可以看到jwt令牌定义成3部分:HEADER,PAYLOAD,VERIFY SIGNATURE,它是怎么划分的呢?贴下官网的例子
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
从这个字符串可以看到,一个jwt令牌分为3段,用.分隔,按照HEADER,PAYLOAD,VERIFY SIGNATURE顺序拼接的
- {
- "alg": "HS256",
- "typ": "JWT"
- }
表示装载令牌类型和算法等信息,是 JWT 的头部。其中,typ 表示第二部分 PAYLOAD 是 JWT 类型,alg 表示使用 HS256 对称签名的算法
- {
- "sub": "1234567890",
- "name": "John Doe",
- "iat": 1516239022
- }
表示是 JWT 的数据体,代表了一组数据。其中,sub(令牌的主体,一般设为资源拥有者的唯一标识)、iat(令牌颁发的时间戳)是 JWT 规范性的声明,代表的是常规性操作。除了这几个之外,还可以定义:
更多的细节可以在JSON Web Token (JWT)查看
不过,在一个 JWT 内可以包含一切合法的 JSON 格式的数据,也就是说,PAYLOAD 表示的一组数据允许我们自定义声明。 比如:name(用户名称)。因为PAYLOAD里面往往是填充的业务数据,所以可以不局限于上面的几个标准字段。而且通常业务系统是会直接从这里获取数据的,所以有些字段是双方约定好的带有业务含义的,比如"age","birthday"等等。
表示对 JWT 信息的签名。那么,它有什么作用呢?其实就是做验签:需要对其进行加密签名处理,而 SIGNATURE 就是对信息的签名结果,当受保护资源接收到第三方软件的签名后需要验证令牌的签名是否合法。我们可能认为,有了 HEADER 和 PAYLOAD 两部分内容后,就可以让令牌携带信息了,似乎就可以在网络中传输了,但是在网络中传输这样的信息体是不安全的,因为你在“裸奔”。
虽然名字叫jwt令牌,其它感觉把它作为一种协议感觉理合理些。
就是jwt跟oauth2是同一级别的,都是可以用于授权!
那么为什么有了oauth这种成熟且强大的授权协议,还会出现jwt呢?
回顾一下前面的Oauth2系列7:授权码和访问令牌的颁发流程是怎样实现的?
里面提到了令牌的生成及使用:一般的令牌格式是一个字符串,对于它没有什么格式化的要求;而且客户端拿到令牌之后,需要获取其它资源信息,需要通过token到资源服务器去再次获取。
就是说:
那么,有没有一种方式,让用户拿到token之后,直接获取业务数据呢?这样授权流程更简单,也没有了授权服务器存储的要求,为此JWT应运而生!
在刚才的官网下面有一个警告提示,也反应了它的使用场景:
至此,JWT的应用场景呼之欲出,可以单独用做授权,也可以做为oauth2的令牌发放!
利用jwt可以带来几个好处:
但是,肯定是有它的局限性:
官方网站地址:JSON Web Token Libraries - jwt.io
提供了不同语言,不同的实现
找一个JAVA版本的jjwt来试试
-
io.jsonwebtoken -
jjwt-api -
0.11.5 -
io.jsonwebtoken -
jjwt-impl -
0.11.5 -
runtime -
io.jsonwebtoken -
jjwt-jackson -
0.11.5 -
runtime
- public static void main(String[] args)
- {
- // 生成jwt令牌
- Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
- String jwtToken = Jwts.builder().setSubject("Joe").signWith(key).compact();
-
- // 检验jwt令牌
- String name = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwtToken).getBody().getSubject();
- System.out.println("subject:" + name);
- }
直接用jjwt库完成jwt令牌的生成与检验很简单。一般来说,生成是在授权服务器,也就是
- // 生成jwt令牌
- Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
- String jwtToken = Jwts.builder().setSubject("Joe").signWith(key).compact();
而检验一般是客户端里面实现的,即
- // 检验jwt令牌
- String name = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwtToken).getBody().getSubject();
这样,就涉及密钥,即上面的Key,这个理论上是授权服务器颁发,下发给客户端,需要加密传输。即生成+检验要用同样的Key
打印一下刚才生成的token:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.u3U3CnkpdJyPqmwcrLt50yxozKVupV-piuVHpWqwPqw
可以发现明显的分为3段,用.分隔,并且将它放到jwt的工具网站里面
可以直观的看到完整的数据结构了!
JJwt也支持公/私钥这种密钥对的方式,如下
- // 密钥对
- KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
- // 私钥加密
- String jwtToken2 = Jwts.builder().setSubject("James").signWith(keyPair.getPrivate()).compact();
-
- // 检验jwt令牌,公钥解密
- String name2 = Jwts.parserBuilder().setSigningKey(keyPair.getPublic()).build().parseClaimsJws(jwtToken2).getBody().getSubject();
- System.out.println("subject:" + name2);
Keys.keyPairFor(SignatureAlgorithm.RS256),工具方法生成密钥对
同上操作,打印一下刚才生成的token:
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJKYW1lcyJ9.ZfiWiXCcmYBtskj_zZrxsPD8_4-ADQGd2C6Sn1lsTE-47eg6a2jY8H_uc8BPJ8AQXwHYxGw75EOir8tco4Q3xVXHXaRxMcuLsp7B4HULc6CujwRjCvFJhyBe2IjADHWVWRgQ4zKOsmX2poUQnOiQ5BgBVR2GUbxcSCEZ5L649jc43mJHHyuzPs6IMUOhqzpliAcxVg5Fh-AtVsxneYWYks9wXlRIqYarGxtgR5LgSrGs3fTfALMZy-qYsSjHm0VaBg9vSOWmSfgne8xVdsy2Z6FF7K9RNES1e5_XKaY6k0vfPvVnYDMIlOW6BAgbxHclgiEy43vHbNglaM4mIcoh7Q
如果Key错误,理论上是通过异常的方式,把上面例子的key在检验之前,随便修改一下运行
- Exception in thread "main" io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
- at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:399)
- at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:529)
- at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:589)
- at io.jsonwebtoken.impl.ImmutableJwtParser.parseClaimsJws(ImmutableJwtParser.java:173)
- at com.tw.tsm.jwt.JjwtDemo.main(JjwtDemo.java:19)
如果抛出这个异常SignatureException就表示,jwt令牌不合法
更多的用法可以研究API了