session为空_分布式系列 单点登录和session共享实现

单点登录,就是通过一个用户认证系统,打通所有关联子系统的认证,从而达到安全高效访问子系统的目的。而session共享是实现的单点登录的一种方式。

一、真伪单点登录

何为真单点,何为伪单点呢?其实理解起来也很简单。

伪单点,举个例子,比如一般中小公司里的内部系统,这类系统一般只有一套用户认证和权限系统,各子系统之间也都是相互信任的,对用户的验证方式都是统一的,这类就是伪单点;

像两个比较大型的系统之间的整合,比如A公司的a系统要整合到B公司的b系统中,两个系统之前肯定不是统一的验证方式,信任什么的更不用提了,这类单点就稍微复杂一些,也就是真单点。

二、如何解决单点问题

关于解决单点问题,其实有比较成熟的策略了,适合各种简单或复杂的业务场景。

1、简单些的,比如,使用shiro进行会话管理;

2、服务器间的session复制;

3、shiro+cas实现;

4、使用专业的SSO产品。

一般来说,shiro方式实现就可以了,大型企业或需要实现真单点的,一般可能会使用到专业的SSO产品。

三、实现原理

fc855e751eef4021ddf306489f050edc.png

从上图可以看出,客户端在访问系统时:

1、要先通过认证服务器,认证服务器在用户登陆后,会颁发一个ticket;

2、客户端拿到ticket后,再去访问子系统;

3、若子系统无法识别,或检测到非法,会再次转到认证服务器,让客户端再次登录。

客户端get到ticket后,在到达每个子系统之前,都会被检测一下ticket,也就是检查用户的登录信息。

四、实现案例

假设我们有三个子系统:Project1;Project2,Project 3,此时,访问每个子系统都会有自己的会话。只要他们之间互认了sessionID,我们就能任意在个子系统之间穿梭访问。

9d2cb93668ed3d29a69770103d883bde.png

这是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);
    }
}

后续登录拦截器的配置,省略

————————————————

版权声明:本文为CSDN博主「追着蜗牛打」的原创文章, 非本人所有。

原文链接:https://blog.csdn.net/u010191243/article/details/74015463

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值