上篇文章简单的描述了下单点登录的场景和解决单点登录问题的几套方案。
本文将从具体实现层面剖析如何实现SSO和session共享。
假设我们有三个子系统:Project1;Project2,Project 3,此时,访问每个子系统都会有自己的会话。只要他们之间互认了sessionID,我们就能任意在个子系统之间穿梭访问。
这是SSO的一张流程图。比较详细的上描述了用户从登陆页面到访问用户中心、订单系统的过程,采用了redis保存会话的共享机制,也是当前比较常用的一套实现方案。
该方案具有明显的好处:redis存取速度快,不会网出现多个节点session复制的问题,且效率高。
查询数据校验:
@Service
public class UserRegisterServiceImpl implements UserRegisterService {
@Autowired
private TbUserMapper userMapper;
@Override
public SystemResult checkInfo(String value, String type) throws Exception {
boolean result = false;
//type为类型,可选参数1、2、3分别代表username、phone、email
if ("1".equals(type)) {
result = checkUserName(value);
} else if ("2".equals(type)) {
result = checkPhone(value);
} else if ("3".equals(type)) {
result = checkEmail(value);
}
//返回结果
if (result) {
return TaotaoResult.ok(result);
}
throw new DataException("此数值已经存在");
}
private boolean checkUserName(String userName) throws Exception {
//创建查询条件
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo(userName);
List<TbUser> list = userMapper.selectByExample(example);
//判断结果中是否存在
if (list == null || list.isEmpty()) {
return true;
}
return false;
}
private boolean checkPhone(String phone) throws Exception {
//创建查询条件
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andPhoneEqualTo(phone);
List<TbUser> list = userMapper.selectByExample(example);
//判断结果中是否存在
if (list == null || list.isEmpty()) {
return true;
}
return false;
}
private boolean checkEmail(String email) throws Exception {
//创建查询条件
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andEmailEqualTo(email);
List<TbUser> list = userMapper.selectByExample(example);
//判断结果中是否存在
if (list == null || list.isEmpty()) {
return true;
}
return false;
}
}
用户注册实现:
@Override
public SystemResult register(TbUser user) throws Exception {
//有效性验证
if (StringUtils.isBlank(user.getUsername())) {
throw new DataException("用户名不能为空");
}
if (StringUtils.isBlank(user.getPassword())) {
throw new DataException("密码不能为空");
}
if (StringUtils.isBlank(user.getPhone())) {
throw new DataException("手机不能为空");
}
//转换md5
user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));
//完善user信息
user.setCreated(new Date());
user.setUpdated(new Date());
//添加到数据库
userMapper.insert(user);
return SystemResult.ok();
}
@Service
public class UserLoginServiceImpl implements UserLoginService {
@Autowired
private TbUserMapper userMapper;
@Autowired
private JedisCluster jedisCluster;
@Value("${USER_TOKEN_KEY}")
private String USER_TOKEN_KEY;
@Value("${SESSION_EXPIRE_TIME}")
private Integer SESSION_EXPIRE_TIME;
@Override
public SystemResult login(String username, String password) throws Exception {
//根据用户名查询用户信息
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo(username);
List<TbUser> list = userMapper.selectByExample(example);
if (null == list || list.isEmpty()) {
throw new DataException("用户不存在");
}
//核对密码
TbUser user = list.get(0);
if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {
throw new DataException("密码错误");
}
//登录成功,把用户信息写入redis
//生成一个用户token
String token = UUID.randomUUID().toString();
jedisCluster.set(USER_TOKEN_KEY + ":" + token, JsonUtils.objectToJson(user));
//设置session过期时间
jedisCluster.expire(USER_TOKEN_KEY + ":" + token, SESSION_EXPIRE_TIME);
return SystemResult.ok(token);
}
}
用户查询:
@Service
public class UserTokenServiceImpl implements UserTokenService {
@Autowired
private JedisCluster jedisCluster;
@Value("${USER_TOKEN_KEY}")
private String USER_TOKEN_KEY;
@Value("${SESSION_EXPIRE_TIME}")
private Integer SESSION_EXPIRE_TIME;
/**
* 根据token取用户信息
*/
@Override
public SystemResult getUserByToken(String token) throws Exception {
//从redis中取用户信息
String userJson = jedisCluster.get(USER_TOKEN_KEY + ":" + token);
if (StringUtils.isBlank(userJson)) {
throw new DataException("该用户已过期");
}
//把json转换成user对象
TbUser user = JsonUtils.jsonToPojo(userJson, TbUser.class);
//更新用户有效期
jedisCluster.expire(USER_TOKEN_KEY + ":" + token, SESSION_EXPIRE_TIME);
return SystemResult.ok(user);
}
}
后续登录拦截器的配置,就不在此多说了。