1. 简介 105
使用Token存储用户信息,认证用户。 Token是一个访问系统的令牌(字符串)。Token 是在服务端产生的。前端可以使用用户名/密码向服务端请求认证(登录),服务端认证成功,服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的身份。服务器端在处理请求之前,先验证Token,验证通过,可以访问系统,执行业务请求处理。
Token可以使用自己的算法自定义,比如给每个用户分配一个UUID ,UUID值就看做是一个Token 。
Token统一的规范是JWT( json web token ) 。用json表示token。JWT定义了一种紧凑而独立的方法,用于在各方之间安全地将信息作为JSON对象传输。
1.1 常使用的Token库是 jjwt 106
2. JWT组成 106
JWT这是一个很长的字符串,中间用点(`.`)分隔成三个部分。JWT 内部是没有换行的,一行数据。
JWT 的三个部分依次如下。
> Header(头部)
> Payload(负载)
>-Signature(签名)
2.1 Header 106
Header: 是一个json对象,存储元数据
{
"alg": "HS256",
"typ": "JWT"
}
alg:是签名的算法名称(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。元数据的json对象使用base64URL编码.
2.2 Payload 106
Payload :负载,是一个json对象。是存放传递的数据 ,数据分为Public的和Private的。
Public是JWT中规定的一些字段,可以自己选择使用。
◾iss (issuer):签发人
◾exp (expiration time):过期时间
◾sub (subject):该JWT所面向的用户
◾aud (audience):受众,接收该JWT的一方
◾nbf (Not Before):生效时间
◾iat (Issued At):签发时间
◾jti (JWT ID):编号
Private是自己定义的字段
{
"role": "经理",
"name": "张凡",
"id": 2345
}
Playload默认是没有加密的, 以上数据都明文传输的,所以不要放敏感数据.此部分数据也是json对象使用base64URL编码,是一个字符串
2.3 Signature 107
Signature:签名。签名是对Header和Payload两部分的签名,目的是防止数据被篡改
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
签名算法:先指定一个 secret秘钥, 把base64URL的header , base64URL的payload 和 secret秘钥 使用 HMAC SHA256 生成签名字符串
3. JWT的使用方式 107
1)首先用户使用类似像用户名,密码登录服务器,校验用户身份,服务器返回jwt token
2)客户端获取 jwt token , 使用cookie或者localStorage存储jwt token,但这样不能跨域, 常用的方式放在 请求header 的 Authorization中
Authorization: Bearer <token>
也可以放在 get 或者 post请求的参数中
3)客户端访问其他api接口, 传递token给服务器, 服务器认证token后,返回给请求的数据
3.1 引入JJWT库依赖 107
jjwt 旨在成为最容易使用和理解的库,用于在 JVM 和 Android 上创建和验证 JSON Web 令牌 (JWT)
github地址: https://github.com/jwtk/jjwt
micr-parent
pom.xml 109
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
micr-common
pom.xml 109
<!--jwt 109-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
</dependency>
在加入单元测试
<!--单元测试 109-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
3.2 在测试方法中创建生成jwt方法 109
micr-common
JwtTest
//创建jwt 109
//342903934cb944808920b642616b3e76
@Test
public void testCreateJwt() {
String key = "342903934cb944808920b642616b3e76";
//创建SeceretKey
SecretKey secretKey = Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8));
Date curDate = new Date();
//创建私有数据
Map<String,Object> data = new HashMap<>();
data.put("userId",1001);
data.put("name","李四");
data.put("role","经理");
//创建Jwt,使用Jwts类
String jwt = Jwts.builder().signWith(secretKey, SignatureAlgorithm.HS256)
.setExpiration(DateUtils.addMinutes(curDate, 10))//设置过期时间
.setIssuedAt(curDate)//设置签发时间
.setId(UUID.randomUUID().toString())//唯一标识,可以通过它认识jwt
.addClaims(data).compact();
//eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDY5NjQzNjMsImlhdCI6MTY0Njk2Mzc2MywianRpIjoiMjdiMGEyYjgtY2E4Yy00ZGM2LWE3OWItNDdkMGZjNTgwYmJlIiwicm9sZSI6Iue7j-eQhiIsIm5hbWUiOiLmnY7lm5siLCJ1c2VySWQiOjEwMDF9.csFmqoYHl_jylijp5dlWHdzRB0Uce5c71ZtgVv77Id0
System.out.println("jwt=="+jwt);
}
3.3 读jwt 110
micr-common
JwtTest
//读jwt 110
@Test
public void testReadJwt(){
String jwt="eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2OTA1NDc3NzksImlhdCI6MTY5MDU0NzE3OSwianRpIjoiNWJlOGY2YzMtMTQyOS00MWMwLWFhYTMtMTRhMTcwY2U0NmRhIiwicm9sZSI6Iue7j-eQhiIsIm5hbWUiOiLmnY7lm5siLCJ1c2VySWQiOjEwMDF9.ULIZTz61XH25Sr3qZK8bIeNLHDIslSCYEQhxsD1MyX8";
String key = "342903934cb944808920b642616b3e76";
//创建SeceretKey
SecretKey secretKey = Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8));
//解析jwt , 没有异常,解析成功
Jws<Claims> claims = Jwts.parserBuilder().setSigningKey(secretKey).build()
.parseClaimsJws(jwt);
//读数据
Claims body = claims.getBody();
Integer userId = body.get("userId", Integer.class);
System.out.println("userId="+userId);
Object uid = body.get("userId");
System.out.println("uid="+uid);
Object name = body.get("name");
if( name != null ){
String str= (String)name;
System.out.println("str = " + str);
}
String jwtId = body.getId();
System.out.println("jwtId="+jwtId);
Date expiration = body.getExpiration();
System.out.println("过期时间:"+expiration);
}