redis实现SSO单点登录,集群,分布式锁

释放双眼,带上耳机,听听看~!

    使用redis实现单点登录,一般情况下都是配合cookie的.将生成的唯一的token信息存储在cookie中,当发生多服务调用时,都会在cookie中读取到该值,再去redis中查询是否有该用户信息存在.如此实现单点登陆

    一般在微服务架构中,SSO单点登陆会抽离出来作为一个单独的服务,来实现服务器集群下的 session共享问题.

   
为什么要使用redis 来解决session 共享问题呢?

    1. redis 是一个纯键值类型的NSQL数据库,所有的操作都是具有原子性的.

    2. redis 可以设置key的生存时间,访问速度快速效率高.

    缺点就是: 会对代码有一定的侵入性.需要自行编码实现!!!

    下面对原有的登陆与单点登录做一下简单的比较:

    简单的单服务的登录逻辑:

    

众所周知,session属于会话,如果在将来业务需要进行壮大需要改造成多服务,那么上去解决方案完全行不通!

所以才会引进SSO单点登录的逻辑,它完美的解决了在多服务下的session共享问题,

    :但是又会带来哪些问题呢?

    1.在高并发下,很难保证生成的token的唯一性,并且有可能存在cookie存储的token信息,在redis 中查询不到,

  于是在这种情况下就需要使用分布式锁!分布式锁有很多实现方式,这里说到的则是利用redis的原子性来实现分布式锁.

    利用redis 中的setnx 和getset命令来实现redis 分布式锁!

setnx:

    

getset:

    

    在redis中null 标记为nil;

那么何为分布式锁呢?

    字面意思:就是在分布式系统中的锁!

    那么这把在分布式中的锁时怎么锁,来确保token的一致性的呢?

首先定义一个锁的名字(根据登录用户)作为key("比喻说:XXX_redis_lock"),那么value采取的时当前时间+超时时间作为value


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1    /**
2     * 加锁
3     *
4     * @param key
5     * @param time 当前时间 + 超时时间
6     * @return
7     */
8    public boolean lock(String key, Long time) {
9        Boolean flag = false;
10        // 将值存储到redis中
11        if (RedisPoolUtil.setnx(key, time)) {
12            flag = true;
13        }
14        // 如果该值存在,判断设置的时间是否超时,如果超时,也将释放锁
15        Long redisTime = Long.valueOf(RedisPoolUtil.get(key));
16        if (!StringUtils.isEmpty(String.valueOf(redisTime)) && redisTime < System.currentTimeMillis()) {
17            // shi用redis 的getset api 将新的值存储到对应的key中 并且返回旧的值
18            String oldValue = RedisPoolUtil.getset(key, String.valueOf(time));
19            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(String.valueOf(redisTime))) {
20                // 说明该锁已经超时,需要给当前线程开启锁
21                flag = true;
22            }
23        }
24        return flag;
25    }
26

    当键值不存在时,第一次访问(这里的RedisPoolUtil 是我自己封装的一个基于JedisPool 的工具类),setnx会设置成功,此时返回的数据为1;再将定义的信号量修改为true;

    不管成功与否,都需要进行下一步的判断,防止返回的结果一直为false(有可能某个服务在获取锁的过程中出现了bug,但是此时并没有释放锁,如果不做如下的判断,那么该判断将会一直保持false状态,其他的线程将不会得到该锁对象)

    下面的判断为,判断该锁是否已经超时,如果超时,将直接释放掉锁!

    在操作完一些的操作后释放掉锁!(将token保存在cookie,将用户信息保存到redis….)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1  /**
2     * 释放锁
3     *
4     * @param key
5     * @param time
6     */
7    public void unlock(String key, Long time) {
8        // 判断获取到的锁 是否与该锁相同
9        try {
10            String currentValue = RedisPoolUtil.get(key);
11            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(String.valueOf(time))) {
12                RedisPoolUtil.del(key);
13            }
14        } catch (Exception e) {
15            log.error("[释放锁] 释放锁失败", e);
16        }
17    }
18

至此:redis分布式锁基本完成!

    加油!!!

给TA打赏
共{{data.count}}人
人已打赏
安全网络

CDN安全市场到2022年价值76.3亿美元

2018-2-1 18:02:50

安全运维

A*算法

2021-12-12 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索