1、什么是SSO
SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。
传统登录中的问题:在集群环境中。需要把同一套代码部署到多台服务器上。每个工程都有自己独立的session。在集群环境中每个工程都有自己的session,如果把用户信息写入session而不共享的话,会出现用户反复登录的情况。
提供以下解决方案:
第一种方案:可以配置tomcat的session共享。配置tomcat集群。Tomcat配置好集群后,会不停的向集群中其他的tomcat广播自己的session信息。其他的tomcat做session同步。可以保证所有的tomcatsession中的内容都是一致的。
优点:不用修改代码就可以实现session共享。
缺点:tomcat 是全局session复制,集群内每个tomcat的session完全同步(也就是任何时候都完全一样的) 在大规模应用的时候,用户过多,集群内tomcat数量过多,session的全局复制会导致集群性能下降, 因此,tomcat的数量不能太多,5个以下为好。
缺陷是不能解决分布式工程的session共享问题。例如支付宝和淘宝单点登录的问题。
第二种方案:
实现单点登录系统,提供服务接口。把session数据存放在redis。
Redis可以设置key的生存时间、访问速度快,效率高。
优点:redis存取速度快,不会出现多个节点session复制的问题。效率高。
缺点:需要程序员开发。
2、用户登录:
登录成功,需要生成一个token可以使用UUID。需要把用户信息写入redis,key就是token,value就是用户信息。返回token数据字符串,把token写入cookie,返回登录成功。Service层代码如下:
public Result userLogin(String username, String password) {
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo(username);
List<TbUser> list = userMapper.selectByExample(example);
//如果没有此用户名
if (null == list || list.size() == 0) {
return Result.build(400, "用户名或密码错误");
}
TbUser user = list.get(0);
//比对密码
if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {
return Result.build(400, "用户名或密码错误");
}
//生成token
String token = UUID.randomUUID().toString();
//保存用户之前,把用户对象中的密码清空。
user.setPassword(null);
//把用户信息写入redis
jedisClient.set(REDIS_USER_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user));
//设置session的过期时间
jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);
//返回token
return Result.ok(token);
}
3、根据token查询用户信息:
拦截器实现流程:
根据token判断用户是否登录或者session是否过期。接收token,根据token到redis中取用户信息。判断token字符串是否对应用户信息,如果不对应说明token非法或者session已过期。取到了说明用户就是正常的登录状态。返回用户信息,同时重置用户的过期时间。接收token,调用dao,到redis中查询token对应的用户信息。返回用户信息并更新过期时间。代码如下:
public Result getUserByToken(String token) {
//根据token从redis中查询用户信息
String json = jedisClient.get(REDIS_USER_SESSION_KEY + ":" + token);
//判断是否为空
if (StringUtils.isBlank(json)) {
return Result.build(400, "此session已经过期,请重新登录");
}
//更新过期时间
jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);
//返回用户信息
return Result.ok(JsonUtils.jsonToPojo(json, TbUser.class));
}
一个简单的单点登录功能就实现了。