分布式单点登录SSO
分布式单点登录SSO
在分布式系统架构中, 如果用户进行了登录操作, 如果让用户在多个业务子系统中都能免密登录呢?
当然想到读写速度快, 而且多个服务器能共同访问, 优先想到的应该是redis缓存
SSO介绍
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的
SSO实现流程
实现步骤:
- 当用户输入用户名和密码之后点击提交按钮.将数据传给JT-WEB服务器.
- JT-WEB利用RPC方式访问JT-SSO 校验数据是否有效.
- 如果校验的数据准备无误,之后将用户信息保存到Redis中. key 使用uuid, value使用userJSON. 并且设定7天超时.
- JT-SSO 将服务器数据返回给JT-WEB服务器.(如果用户名和密码错误的则返回为null即可).
- JT-WEB服务器将数据保存到Cookie中.(要求实现Cookie共享).
主要实现代码
jt-web为前端服务器, jt-sso为单点登录服务器, 使用dubbo进行通信
controller编写(jt-web)
/**
* 1.url地址:http://www.jt.com/user/doLogin?r=0.04360522021726099
* 2.参数: {username:_username,password:_password},
* 3.返回值结果: SysResult
*
* 需求1: 将cookie名称为 "JT_TICKET"数据输出到浏览器中,要求7天超时.
* 并且实现"jt.com"数据共享
*
* Cookie特点:
* 1.浏览器中只能查看当前网址下的Cookie信息
* 2.doMain 表示cookie共享的策略
* doMain:www.jd.com 当前的Cookie数据只能在当前域名中使用
* doMain:.jd.com 当前Cookie是共享的可以在域名为jd.com结尾的域名中共享.
*
**/
@RequestMapping("/doLogin")
@ResponseBody
public SysResult doLogin(User user, HttpServletResponse response){
//完成用户登录操作 之后获取ticket密钥信息
String ticket = userService.doLogin(user);
if(StringUtils.isEmpty(ticket)){
//如果为null,则说明用户名或密码有问题
return SysResult.fail();
}
//1.创建Cookie对象, 将uuid发送代前端cookie保存
Cookie cookie = new Cookie("JT_TICKET",ticket);
//2.设定cookie存活的时间 value=-1 当用户关闭会话时,cookie删除
//2.设定cookie存活的时间 value= 0 立即删除cookie
//2.设定cookie存活的时间 value= >0 设定cookie超时时间
cookie.setMaxAge(7*24*60*60);
//3.在jt.com的域名中实现数据共享.
cookie.setDomain("jt.com");
cookie.setPath("/"); //一般情况下都是/
//4.将数据保存到浏览器中
response.addCookie(cookie);
return SysResult.success();
}
service层编写(jt-sso)
注意: 下面用的mybatisplus查询的数据库
/**
* 目的: 校验用户信息是否有效并且实现单点登录操作.
* 步骤:
* 1.校验用户名和密码是否正确(密码明文转化为密文)
* 2.查询数据库检查是否有结果
* 3.如果有结果,则动态生成TICKET信息(uuid),将user对象转化为JSON
* 4.将数据保存到redis中,并且设定超时时间.
* 5.返回当前用户登录的密钥.
*
* @param user
* @return 返回uuid
*/
@Override
public String doLogin(User user) {
String passwork = user.getPassword();
String md5Pass = DigestUtils.md5DigestAsHex(passwork.getBytes()); // 将密码MD5加密
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", user.getUsername())
.eq("password", md5Pass);
//获取的是用户的全部记录 (包含涉密信息)
User userDB = userMapper.selectOne(queryWrapper); // 在数据库中查询用户
//校验数据是否有效
if( userDB == null){
//用户名和密码错误
return null;
}
//如果程序执行到这里,说明用户名和密码正确的. 开启单点登录流程
// uuid去掉横杠-
String ticket = UUID.randomUUID() .toString().replace("-", "");
//脱敏处理 余额/密码/手机号/家庭地址
userDB.setPassword("123456你信不???");
String userJSON = ObjectMapperUtil.toJSON(userDB);
//保存的redis, 时间为7天有效, 操作时保证原子性
jedisCluster.setex(ticket, 7*24*60*60, userJSON);
return ticket;
}