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

 

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.3.0</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.9.9</version>
        </dependency>

/**
 * @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<String,Object> 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<String,Claim> 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<String, Claim> 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+redis与jwt的区别

    (1)简单的说,token只是一个标识,以token加redis为例,服务端将token保存在redis中,客服端访问时带上token,如果在redis中能够查到这个token,说明身份有效。

    (2)jwt不需要查库,本身已经包含了用户的相关信息,可以直接通过服务端解析出相关的信息,与session,token的最大区别就是服务端不保存任何信息(服务端只需要保存密钥key)。

2.如何实现jwt续期

在jwt中保存过期时间,解析时进行判定,如果即将超时则重新设置过期时间返回一个新的jwt给客户端。

3.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

 

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐