1.项目概要

传统的web应用中,我们通常通过cookie+session机制来保证调用的安全,在没有认证的情况下自动重定向到登录页面或者调用失败页面,而现在整个架构编程微服务模式了,cookie和session机制已经不能很好的满足保护API的需求了,更多的情况下采用token的验证机制,JWT的本质也是一种token。

JWT:JSON Web Token,是JSON风格的轻量级授权和认证规范,可以实现无状态,分布式的web应用授权。JWT的内容由三部分组成,分别是Header,Payload,Signature,三个部分之间通过.分割,举例

xxxxx.yyyyyy.zzzzz
 
Header
 
头部Header一般由2个部分组成alg和typ,alg是加密算法,如HMAC或SHA256,typ是token类型,取值为jwt,一个Header的例子
 
{
 
"alg": "HS256",
 
"typ": "JWT"
 
}
 
然后对Header部分进行Base64编码,得到第一部分的值
 
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.{PAYLOAD}.{SIGNATURE}
 
Payload

内容部分Payload是JWT存储信息的主体,包含三类内容

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

标准中注册的声明

  • iss:jwt签发者
  • sub:jwt所面向的用户
  • aud:接收jwt的一方
  • exp:jwt的过期时间
  • nbf:定义在什么时间之前该jwt是不可用的
  • iat:jwt的签发时间
  • jti:jwt的唯一标识,主要用作一次性token,避免重放攻击

公共的声明:

可以存放任何信息,根据业务实际需要添加,如用户id,名称等,但不要存放敏感信息

私有的声明:

私有声是提供者和消费者所共同定义的声明,不建议存放敏感信息

举例:定义一个payload:

{ "sub": "1234567890", "name": "John Doe", "admin": true }对其进行Base64编码,得到第二部分 

 

  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.{SIGNATURE} 

 
Signature
 

  token的签名部分由三部分组成256签名 

 

  var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); 

 

  var signature = HMACSHA256(encodedString, 'secret'); 

 

  得到最终的token串 

 

  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 

 
2.Oauth2-Server中生成JWT Token
 
a)通过keytool生成证书
 

  keytool -genkeypair -alias kevin_key -keyalg RSA -keypass 123456 -keystore kevin_key.jks -storepass 123456

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_java


查看证书信息:

keytool -list -v -keystore kevin_key.jks -storepass 123456

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_java_02


查看公钥信息

keytool -list -rfc -keystore kevin_key.jks -storepass 123456

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_登录页面_03


b)将生成的kevin_key.jks文件放到oauth2-server工程的srce/main/resources目录下

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_登录页面_04

c)添加jwt相关jar包依赖

<dependency> 

 

  <groupId>org.springframework.security</groupId> 

 

  <artifactId>spring-security-jwt</artifactId> 

 

  </dependency>

d)在OAuth2服务器端配置核心类AuthorizationServerConfiguration中增加jwt token相关配置

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_ci_05

3.测试Oauth2服务

http://localhost:8888/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://baidu.com&state=123

出现登录页面,输入用户名:admin 密码;123456

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_登录页面_06

点击Submit按钮,进入用户授权确认页面

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_登录页面_07


点击Approve,跳转到baidu页面,后面携带了code和state参数

https://www.baidu.com/?code=F7LsMB&state=123

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_spring_08


根据code换取access_code,注意使用post方法

http://localhost:8888/oauth/token?client_id=client&grant_type=authorization_code&redirect_uri=http://baidu.com&code=  F7LsMB

注意这个code要和上个步骤中获得的code保持一致

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_ci_09


用户名输入client,密码是secret,点击确定,可以看到access_token已经是jwt格式的字符了

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_登录页面_10



eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTAxNzQ2MDYsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNTJhOGM5MTUtMTE2OS00YzU5LWI0MmEtZGY4ZDM0Y2QwZWU0IiwiY2xpZW50X2lkIjoiY2xpZW50Iiwic2NvcGUiOlsiYXBwIl19.GecJM-FHApwznyYl-D3IjB0TpjhdhUXfYv782kfS9vdT0VZsu2HN-MGb-N-6Hf0efZ_mmz54IahJaq3KTw251v4L2O5A1r_iMuUP7GXs_qPHAGn3K1b4l-mNnpJdH5hhS5zYIRqOX2a8DXyI4zD7g8BQL-9PiR3kj9k_z9nW8vY9l2_x5Kyoc-sehxxQ5uQHM3xu6DzOwBpbbER7U_NnUwmcz5nS9YyAexSDnBbZAVpQavL2s1yYQVMJ5Dreq2asXHFbeQHXu5UqVbbTFuOgAylbFJ9K-3nsGAKT9NbzqBPRovI3s_X9HgjrzJHAuojBMeK0QMbvYSbUg2HB7MNNJw","token_type":"bearer","expires_in":43199,"scope":"app","user_name":"admin","jti":"52a8c915-1169-4c59-b42a-df8d34cd0ee4"}

4.access_token信息解析

我们通过上个步骤得到的token信息是不可读的,但是因为header,body都是经过base64转码过的,因此我们可以通过Base64将其解码,spring cloud里也提供了jwt相关的工具类帮我们来反解析这个串

package com.pachiraframework.springcloud.oauth2.config; 

 
  
 

  import org.junit.Test; 

 

  import org.springframework.security.jwt.Jwt; 

 

  import org.springframework.security.jwt.JwtHelper; 

 
  
 

  public class JwtTest { 

 

  @Test 

 

  public void test() { 

 
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTAxNzQ2MDYsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNTJhOGM5MTUtMTE2OS00YzU5LWI0MmEtZGY4ZDM0Y2QwZWU0IiwiY2xpZW50X2lkIjoiY2xpZW50Iiwic2NvcGUiOlsiYXBwIl19.GecJM-FHApwznyYl-D3IjB0TpjhdhUXfYv782kfS9vdT0VZsu2HN-MGb-N-6Hf0efZ_mmz54IahJaq3KTw251v4L2O5A1r_iMuUP7GXs_qPHAGn3K1b4l-mNnpJdH5hhS5zYIRqOX2a8DXyI4zD7g8BQL-9PiR3kj9k_z9nW8vY9l2_x5Kyoc-sehxxQ5uQHM3xu6DzOwBpbbER7U_NnUwmcz5nS9YyAexSDnBbZAVpQavL2s1yYQVMJ5Dreq2asXHFbeQHXu5UqVbbTFuOgAylbFJ9K-3nsGAKT9NbzqBPRovI3s_X9HgjrzJHAuojBMeK0QMbvYSbUg2HB7MNNJw"; 

 

  Jwt jwt = JwtHelper.decode(token); 

 

  System.out.println(jwt.toString()); 

 

  } 

 

  }

springcloud集成oauth2 登录口在哪里 springcloud gateway oauth2 jwt_springcloud_11



通过这个测试我们可以看出来,token中已经包含了当前用户的信息了,包括我们在accessTokenConvertor()方法中给token中添加的额外信息user_name