文章目录
- Session + Cookie的使用
- Token的使用
Session + Cookie的使用
1. Session存储数据
HttpSession session = request.getSession(); //Servlet底层通过的SESSIONID,获取Session对象。
session.setAttribute("loginTime",new Date());
out.println("登录时间为:"+(Date) session.getAttribute("loginTime"));
- void setAttribute(String attribute,Object value): 设置Session属性。value信息不宜过大
- String getAttribute(String attribute): 返回Session属性
- Enumeration getAttributeNames(): 返回Session中存在的属性名
- void removeAttribute(String attribute): 移除Session属性
- String getId(): 返回Session的ID
- long getCreationTime(): 返回Session的创建日期
- long getLastAccessedTime(): 返回Session的最后活跃时间
- int getMaxInactiveInterval(): 返回Session的超时时间
- int setMaxInactiveInterval(int second): 设置Session的超时时间
- boolean isNew(): 返回该Session是否新创建的
- void invalidate(): 使Session失效
2. Cookie存储数据
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
cookie.setMaxAge(0); // 设置生命周期为0,不能为负数
response.addCookie(cookie); // 必须执行这一句
3. Session + Cookie 的区别
- 生存周期:Session从IE启动到IE关闭.,Cookie是用户可以预先设置的生存周期。
- 存储地方:Session数据放在服务器上,Cookie数据存放在客户的浏览器中
4. Session + Cookie的配合使用
我们知道了 Cookie 由于存储的内存空间只有 4kb,因此存储的主要是一个用户 id,其他的用户信息都存储在服务器的 Session
- 服务器接收到请求后,通过request.getSession()方法创建会话对象。此方法第一次调用是创建session会话,以后在session没有被销毁前,再次调用都是获取前面创建的session。
- 服务器在每次创建session的时候,也会创建cookie,这个cookie的key永远是JESSIONID。value是创建的session的id。
- 通过响应将新创建的session的id,放在cookie里,传给浏览器。Set-Cookie:JESSIONID=XXX
- 浏览器解析获取到的数据,就马上创建一个cookie对象。有了cookie之后,再次请求服务器,就会把含有session的id的cookie,传给服务器Cookie:JESSIONID=XXX
- 服务器通过request.getSession方法,通过cookie里面的session的id,找到之前创建好的session对象,返回相应的数据。
Token的使用
1. Token出现原因
通过Session + Cookies将数据存储在服务器中,当用户数量急剧时,服务器需要耗费大量的sessionId
。这显然对服务器说是一个巨大的开销,这就使用到了Token。
2. Token的结构
头部(header)
Token头部承载两部分信息:声明类型、声明加密算法
{
"typ": "JWT",
"alg": "HS256"
}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
载荷(payload)
有效信息包含三个部分:标准中注册的声明、公共的声明、私有的声明。
1. 标准中注册的声明 (建议但不强制使用)
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
2. 公共的声明
- 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密
3. 私有的声明
- 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个payload
{
"uid":"e12a34b56c78d9e0f",
"name":"ramostear",
"role":"admin"
}
然后将其进行base64加密
eyJvcmciOiLku4rml6XlpLTmnaEiLCJuYW1lIjoiRnJlZeeggeWGnCIsImV4cCI6MTUxNDM1NjEwMywiaWF0IjoxNTE0MzU2MDQzLCJhZ2UiOiIyOCJ9
签证(signature)
这个部分需要base64加密后的header和base64加密后的payload,连接组成的字符串,然后通过header中声明的加密方式进行secret密钥组合加密,然后就构成了第三部分:
49UF72vSkj-sA4aHHiYN5eoZ9Nb4w5Vb45PsLF7x_NY
3. Token如何生成
Token怎么制作呢
- 将荷载payload,Header信息进行Base64加密,形成payload密文,header密文。
- 将形成的密文用句号链接起来,用服务端秘钥进行HS256加密,生成签名.
- 将前面的两个密文后面用句号链接签名形成最终的token返回给服务端
服务器如何检验
- 服务端使用原来的秘钥与密文(header密文+“.”+payload密文)同样进行HS256运算,然后用生成的签名与token携带的签名进行对比,若一致说明token合法,不一致说明原文被修改。
- 判断是否过期,客户端通过用Base64解密第二部分(payload密文),可以知道荷载中授权时间,以及有效期。通过这个与当前时间对比发现token是否过期。
4. Token的C/S交互
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端,客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token,服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
- APP登录的时候发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果成功,以某种方式比如随机生成32位的字符串作为token,存储到服务器中,并返回token到APP,以后APP请求时,凡是需要验证的地方都要带上该token,然后服务器端验证token,成功返回所需要的结果,失败返回错误信息,让他重新登录。其中服务器上token设置一个有效期,每次APP请求的时候都验证token和有效期。
5. Token与Cookie的区别
- 内存空间:Cookie 由于存储的内存空间只有 4kb,因此存储的主要是一个用户 id,其他的用户信息都存储在服务器的 Session 中,而 Token 没有内存限制,用户信息可以存储 Token 中,返回给用户自行存储,因此可以看出,采用 Cookie 的话,由于所有用户都需要在服务器的 Session 中存储相对应的用户信息,所以如果用户量非常大,这对于服务器来说,将是非常大的性能压力,而Token 将用户信息返回给客户端各自存储,也就完全避开这个问题了。
6. Token与Cookie的联系
客户端将Token存储到Cookie中,可以设置token的有效时间
7. Token的定制化生成
生成Token
主要是填充载荷部分
// 生成token方法
public String generateToken(Map<String, Object> claims) { returnJwts.builder().setClaims(claims) //设置载荷.setExpiration(generateExpirationDate()) //设置过期时间.signWith(SignatureAlgorithm.forName(alg), secret) //设置签名.compact();
}
获取Token
/** * 获得token内的内容 * @param token * @return */
public Claims getClaimsFromToken(String token) { try { Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();} catch (Exception e) { claims = new DefaultClaims();log.warn("{}", e.getMessage(), e);}return claims;
}