大家好,我是IT修真院深圳分院第11期学员,一枚正直善良的JAVA程序员。
今天给大家分享一下,修真院官网JAVA任务5中需要使用的JWT
1.背景介绍
HTTP 是一种没有状态的协议,一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。也就导致服务器无法分辨是谁浏览了网页。为了维持用户在网站的状态,比如登陆、购物车等,出现了先后出现了四种技术。为了保证数据安全可靠地在用户与服务端之间传输,实现服务端的认证就显得极为必要。
常见的服务端认证方法有基于 Cookie-Session的认证;以及 Token (令牌)认证,如 JWT。前者客户端在每次请求时都需要带上 cookie ,取出相应字段并与服务器端的进行对比,以实现身份的认证。而后者仅仅需要在 HTTP 的头部或者使用Cookie附上 token,由服务器 check signature 即可实现,无须担心 cookie 存在的 CORS 问题。
2.知识剖析
什么是JWT
JWT 是一套开放的标准(RFC 7519),它定义了一套简洁的(compact)、自包含的(self-contained)方案,来让我们安全地在客户端和服务器之间传输 JSON 格式的信息。
Token身份验证基本流程
1). 客户端使用自己的账号密码发送 post 请求登录
2). 由于这是首次接触,服务器会校验账号与密码是否合法
3). 如果一致,则根据密钥生成一个 JWT 并返回
4). 客户端收到这个 token 并保存在本地
5). 客户端需要访问一个受保护的资源时,只要附加上JWT发送到服务器
6). 服务器就会检查这个 token 是否有效,并做出响应
JWT的组成
JWT 标准的 Token 有三个部分:header、payload、signature。
JWT = Base64(Header) + "." + Base64(Payload) + "." + $Signature
$Signature:HS256(Base64(Header) + "." + Base64(Payload), secretKey)
Payload 部分(载荷)
1、 sub: 该JWT所面向的用户;
2、 iss: 该JWT的签发者;
3、 iat(issued at): 在什么时候签发的token;
4、 exp(expires): token什么时候过期;
5、 nbf(not before):token在此时间之前不能被接收处理
6、 jti:JWT ID为web token提供唯一标识。
Signature 部分(签名)
1、 用 Base64 编码的 header.payload
2、 HS256加密算法加密(将密钥存储在服务端)
3.编码实战
1.搜索找到jar包和工具类;
2.分析工具类:
//生成JWT
//公有 静态 返回值:String sign:签名 (形参:1:泛型,2:long)
public static <T> String sign(T object, long maxAge) {
try {
//设置密钥
final JWTSigner signer = new JWTSigner(SECRET);
//Map集合(字符串:形参1:泛型)
final Map<String, Object> claims = new HashMap<String, Object>();
//JSON和对象的转换类
ObjectMapper mapper = new ObjectMapper();
//将参数1:泛型,转换为字符串JSON形式
String jsonString = mapper.writeValueAsString(object);
//Map集合添加元素:荷载,参数1的泛型
claims.put(PAYLOAD, jsonString);
//Map集合添加元素:有效时间,当前时间戳+形参2:有效时间
claims.put(EXP, System.currentTimeMillis() + maxAge);
//返回加密后的密钥
return signer.sign(claims);
} catch(Exception e) {
return null;
}
}
//解析JWT,验证JWT,验证成功获得荷载
//公有 静态 解密钥 参数(参数1:字符串jwt,参数2:泛型类)
public static<T> T unsign(String jwt, Class<T> classT) {
//常量:设置密钥
final JWTVerifier verifier = new JWTVerifier(SECRET);
final JWTSigner signer = new JWTSigner(SECRET);
try {
//常量:Map<字符串:Object> 参数1.解密
final Map<String,Object> claims= verifier.verify(jwt);
//如果Map集合中有"EXP"和"PAYLOAD"的键值对
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
//获得当时的时间戳+有效时间
long exp = (Long)claims.get(EXP);
//获得当前时间戳
long currentTimeMillis = System.currentTimeMillis();
//判断是否有效
if (exp > currentTimeMillis) {
//有效的话,获得荷载中的内容
String json = (String)claims.get(PAYLOAD);
if(signer.sign(claims).equals(jwt)){
//对象与JSON转换类
ObjectMapper objectMapper = new ObjectMapper();
//将荷载中的JSON数据转为形参2:泛型
return objectMapper.readValue(json, classT);
}
}
}
return null;
} catch (Exception e) {
return null;
}
}
编写测试代码:
public static void main(String[] args) {
Student student=new Student();
student.setName("希尔瓦娜斯");
student.setId(1);
student.setOid(0);
//将学员对象作为荷载,有效时间30分钟
String jwt=JWT.sign(student,1000L*60*30);
//输出jwt
System.out.println("JWT: "+jwt);
//解析jwt获得荷载中的对象
Student s=JWT.unsign(jwt,Student.class);
//输出对象
System.out.println("荷载: "+s);
}
运行:
4.常见问题
1、 用户匹配
服务端在生成token时,加入少量的用户信息,比如用户的id。服务端接收到token之后,可以解析出这些数据,从而将token和用户关联了起来。
2、 防伪造
建议放入token的数据是不敏感的数据,这样只要服务端使用私钥对数据生成签名,然后和数据拼接起来,作为token的一部分即可
基于加密的算法,对数据进行加密,把加密的结果作为token
3、 防冒充
1) 加干扰码
服务端在生成token时,使用了客户端的UA作为干扰码对数据加密,客户端进行请求时,会同时传入token、UA,服务端使用UA对token解密,从而验证用户的身份。
2) 有效期
给token加上有效期,即使被冒充也只是在一定的时间段内有效。每次服务端接收到请求,解析token之后,判断是否已过期,如果过期就拒绝服务。
5.参考文献
百度
CSDN
6.拓展思考
JWT应用场景
一次性验证
比如用户注册后需要发一封邮件让其激活账户,通常邮件中需要有一个链接,这个链接需要具备以下的特性:能够标识用户,该链接具有时效性(通常只允许几小时之内激活),不能被篡改以激活其他可能的账户…这种场景就和 jwt 的特性非常贴近,jwt 的 payload 中固定的参数:iss 签发者和 exp 过期时间正是为其做准备的。
restful api 的无状态认证
使用 jwt 来做 restful api 的身份认证也是值得推崇的一种使用方案。客户端和服务端共享 secret;过期时间由服务端校验,客户端定时刷新;签名信息不可被修改…spring security oauth jwt 提供了一套完整的 jwt 认证体系,以笔者的经验来看:使用 oauth2 或 jwt 来做 restful api 的认证都没有大问题,oauth2 功能更多,支持的场景更丰富,后者实现简单。
7.更多讨论
8.感谢观看
鸣谢,感谢观看