昨天电话面试的时候和面试官聊项目的时候聊的比较多就是单点登录我是怎么实现的,这篇博客便对此做点单登录系统进行总结。
SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。
就比如在我这个系统中,由于各个系统拆分在不同的系统中,
比如选课系统,和登录系统,这俩不在同一个tomcat下,如果使用tomcat内置的session,这可取不到信息,需要配置tomcat集群。用tomcat相互之间session相互广播的方式,配置tomcatSession复制。
如图所示:
在服务节点较少时候这种方式,没什么问题,但是随着tomcat节点的增多session的广播就会形成session风暴,严重暂用带宽。
解决办法: 可以将session服务单独抽离出来,使用Session服务器,保存Session信息。
session本质上就是一组带有过期时间的键值对数据,这种数据有没有发现和Redis极为相似,所以本项目采用Redis作为Session服务器存储Session。
单点登录系统实现
注册
要登录先注册,和正常系统流程没啥区别。直接上代码
@Override
public EasResult register(TbUser user) {
//数据有效性校验
if (StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())
|| StringUtils.isBlank(user.getPhone())) {
return EasResult.build(400, "用户数据不完整,注册失败");
}
//1.用户名 2.手机号 3.邮箱
EasResult result = checkData(user.getUsername(), 1);
if (!(boolean) result.getData()) {
return EasResult.build(400, "此用户名已经被占用");
}//错误提示
result = checkData(user.getPhone(), 2);
if (!(boolean)result.getData()) {
return EasResult.build(400, "手机号已经被占用");
}//错误提示
//补全pojo的属性
user.setCreated(new Date());
user.setUpdated(new Date());
//对密码进行md5加密
String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
user.setPassword(md5Pass);
//把用户数据插入到数据库中
userMapper.insert(user);
//返回添加成功
return EasResult.ok();
}
登录…这个我们就得说说了。
画个流程图吧…
登录的处理流程:
1、登录页面提交用户名密码。
2、登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。
3、把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。
4、使用String类型保存Session信息。可以使用“前缀:token”为key
5、设置Key的过期时间。模拟Session的过期时间。
6、把token写入cookie中。
7、Cookie需要跨域。使用工具类。
8、Cookie的有效期。关闭浏览器失效。
9、登录成功。
代码实现
@Override
public EasResult userLogin(String username, String password) {
// 1、判断用户和密码是否正确
//根据用户名查询用户信息
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo(username);
List<TbUser> list = userMapper.selectByExample(example);
System.out.println(list);
if (list == null || list.size() == 0) {
//返回登录失败
return EasResult.build(400, "用户名或密码错误");
}
//取用户信息
TbUser user = list.get(0);
//判断密码是否正确
if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {
// 2、如果不正确,返回登录失败
return EasResult.build(400, "用户名或密码错误");
}
// 3、如果正确生成token。
String token = UUID.randomUUID().toString();
// 4、把用户信息写入redis,key:token value:用户信息
user.setPassword(null);
jedisClient.set("SESSION:" + token, JsonUtils.objectToJson(user));
// 5、设置Session的过期时间
jedisClient.expire("SESSION:" + token, SESSION_EXPIRE);
// 6、把token返回
return EasResult.ok(token);
}
token就是Key(也就是所谓的令牌验证),Value就是TbUser对象转换成json。
如果我们需要取用户信息呢。
代码实现
@Override
public EasResult getUserByToken(String token) {
//根据token到redis中取用户信息
String json = jedisClient.get("SESSION:" + token);
//取不到用户信息,登录已经过期,返回登录过期
if (StringUtils.isBlank(json)) {
return EasResult.build(201, "用户登录已经过期");
}
//取到用户信息更新token的过期时间
jedisClient.expire("SESSION:" + token, SESSION_EXPIRE);
//返回结果,EasResult其中包含TbUser对象
TbUser user = JsonUtils.jsonToPojo(json, TbUser.class);
return EasResult.ok(user);
}
缓存服务器,不建议和Session服务器公用一台Redis。andRedis存不建议保存用户密码信息,哪怕是加密过的。