cookie机制
关于cookie和seesion的联系
cookie中会包含那些信息
名字,值,过期时间,路径,域
cookie会带到http请求头中发送给服务端
如果cookie没有设置过期时间的话,那么cookie的默认生命周期是浏览器的会话
session机制
1,session是容器对象,客户端在请求服务端的时候,服务端会根据客户端的请求判断是否包含了jsessionId的标识
2,如果已经包含了,说明客户端之前已经创建了会话。sessionId是一个唯一的值
3,如果sessionid不存在,那么服务端为这个客户端生成一个sessionid. JESSIONID
session cookie 存储的是JSESSIONID
session存储在服务器端 cookie存储在浏览器端
服务器端(Tomcat) 会生成一个唯一的sessionId号存储在cookie中 叫 jessionid
在服务器端(tomcat)中存储serssion 使用concurrentMap (ConcurrentMap key JSESSIONID values session)
浏览器端下次请求服务器端是将jsessionId带过来 找到对应的session 获取session中存储的信息(用户信息)
客户端浏 览器禁用了cookie怎么办?
[如果客户端浏 览器禁用了cookie,一般会通过URL重写的方式来进行会 话会话嗯个总,也就是在url中携带sessionid]
解决session跨域共享问题
1. session sticky :会话保存在单机上 保证会话请求落在同一台服务器上
采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列 表不变时,它每次都会映射到同一台后端服务器进行访问
根据获取客户端的IP地址,通过哈希函数计算得到的一个 数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希 法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问
这种实现方式会有些问题: 如果一台web服务器宕机或者重启,那么这台机器上保 存的会话数据都会丢失,会造成用户暂时无法访问的问 题,或者用户之前的授权操作需要再执行一次
2. session replication:session 复制 每一台服务器上都保持一份相同的session (造成额外的存储开销和网络开销)
session复制,通过相关技术实现session复制,使得集群 中的各个服务器相互保存各自节点存储的 session 数据。 tomcat本身就可以实现session复制的功能,基于IP组播 放方式。
这种实现方式的问题:
1. 同步session数据会造成网络开销,随着集群规模越大, 同步session带来的带宽影响也越大
2. 每个节点需要保存集群中所有节点的 session 数据,就 需要比较大的内存来存储。
3. session 集中存储 :存储在db、 存储在缓存服务器 (redis)
使用 spring-session -data-redis
http://www.glmapper.com/
4. 基于cookie (主流)
a)
access_token(userid/token/timestamp(过期时间) 加密)
将access_token存储在客户端的cookie中 每次 客户端过来访问 服务器端拦截其中 获取cookie中的access_token 根据 userid和timestamp(过期时间) 判断是否有效
b)基于JWT的解决方案
json web Token 客户端和服务端信息安全传递,身份认证的一种解决方案。用在登陆上
jwt由三个组成:header,payload 载荷,signature
header{
typ:"jwt" //类型
alg:"HS256" //加密算法
zip: "gzip/deflate" //压缩算法 加密之后的字符串 比较长的时候 可以使用压缩算法
}
payload :jwt本身规范提供的格式 claims
{
iss:“签发者”
iat:“签发时间”
exp:“过期时间”
sub:
}
可以自己定一些claims,放入自定义的信息如 uid 等
signature: 将 header+ payload 组合成为一个字符串
Base64(header).Base64(payload) + head中定义的算法 +密钥 生成一个字符串 str.签名字符串 就是 JWT的token
com.auth0
java-jwt
3.3.0
joda-time
joda-time
2.9.9
/**
* @Auther: tengxiao
* @Date: 2018/9/13 16:47
* @Description:
*/
public class JWTTokenUtil {
private static final String JWT_KEY_USER_ID="JWT_KEY_USER_ID";
private static final int EXPIRED_TIME=6000;
private static final String SECRET_KEY="tengvincent_user";
public static String generatorToken(Long userId)throws Exception{
//header Map
Map headerMap=new HashMap<>();
headerMap.put("typ","JWT");
headerMap.put("alg","HS256");
String token=JWT.create()
.withHeader(headerMap)
.withClaim("iss","Service")//签发者
.withClaim("aud","APP")
.withClaim(JWT_KEY_USER_ID,userId)
.withIssuedAt(DateTime.now().toDate())//sign time
.withExpiresAt(DateTime.now().plusMinutes(EXPIRED_TIME).toDate())//expired time
.sign(Algorithm.HMAC256(SECRET_KEY));
return token;
}
public static Map varifyToken(String token){
DecodedJWT jwt=null;
try{
JWTVerifier verifer= JWT.require(Algorithm.HMAC256(SECRET_KEY)).build();
jwt=verifer.verify(token);
}catch (Exception e){
// e.printStackTrace();
// token 校验失败, 抛出Token验证非法异常
}
return jwt.getClaims();
}
public static Long getTokenInfo(String token){
Map claims = varifyToken(token);
Claim user_id_claim = claims.get("user_id");
if (null == user_id_claim || StringUtils.isEmpty(user_id_claim.asString())) {
// token 校验失败, 抛出Token验证非法异常
}
return Long.valueOf(user_id_claim.asString());
}
}
(1)简单的说,token只是一个标识,以token加redis为例,服务端将token保存在redis中,客服端访问时带上token,如果在redis中能够查到这个token,说明身份有效。
(2)jwt不需要查库,本身已经包含了用户的相关信息,可以直接通过服务端解析出相关的信息,与session,token的最大区别就是服务端不保存任何信息。
在jwt中保存过期时间,解析时进行判定,如果即将超时则重新设置过期时间返回一个新的jwt给客户端。
登出时将相关的信息比如用户名存储在redis中,并设置过期时间。当再次访问时,从jwt中解析出用户名去redis中查找,如果存在则表示此jwt已登出失效。这里需要注意的是,如果用此方法,则验证jwt是否登出应该放在第一位。思考一个场景,如果redis中存储的是用户名,那么当用户登出后,redis中已经有了相应的用户名,当用户再次登录时,解析jwt发现此用户已登出,则jwt失效,所以在登录时要清空相关的登出缓存。
参考文档
jwt demo 代码: https://github.com/tengxvincent/spring-boot-vincent/tree/master/vincent-jwt
https://www.cnblogs.com/mantoudev/p/8994341.html
https://bbs.huaweicloud.com/blogs/06607ea7b53211e7b8317ca23e93a891
https://blog.csdn.net/w57685321/article/details/79463837