Easymall项目分布式拆分整合(八)
目录
一.User用户系统的2个延伸问题
1.登录顶替
- 每次登录时,利用唯一值判断当前用户是否曾经登录过; user_id相关,value就是登录时创建的ticket
- 发现没有key,从来没有登陆过,直接执行登录逻辑
- 发现有值,有人曾经登录过,获取ticket,del删除,执行后续登录逻辑
1.UserController类
//验证登录的用户名密码是否正确
@RequestMapping("login")
public String doLogin(User user){
String ticket=userService.doLogin(user);
//成功登录返回redis的key,失败返回""
return ticket;
}
2.UserService类
@Autowired
private RedisCumUtils redis;
public String doLogin(User user) {
//查询一下数据库数据,是否存在userExist
//处理用户密码加密
user.setUserPassword(MD5Util.md5(user.getUserPassword()));
User exist=userMapper.selectExist(user);
try{
if(exist==null){//登录失败
return "";
}else{
//通过唯一值确定是否已经存在
String alterKey="alterKey_"+exist.getUserId();
if(redis.isExist(alterKey)){
redis.delete(redis.query(alterKey));
}
//表示成功,存储在redis返回key值
String ticket=MD5Util.md5("EM_TICKET"+System.currentTimeMillis()
+exist.getUserId());
//准备value值,mapper转化user为json字符串
String userJson=MapperUtils.MP.writeValueAsString(exist);
//set数据到redis供后续逻辑使用
redis.addOrUpdateExpire(ticket, userJson, 60*30);
//生产一个
redis.addOrUpdate(alterKey, ticket);
//验证最多一个用户登录,顶替登录逻辑
//TODO
return ticket;
}
}catch(Exception e){
e.printStackTrace();
return "";
}
}
在登录方法里先做判断,获取当前值,判断是否已经存在,如果存在,把之前的值删除,保存新的数据
2.30分钟超时突然断开
用户在访问系统过程中,总是通过redis的ticket来判断登录用户的状态,获取用户的信息,从而进行各种操作(购物车,订单提交)
一旦数据超时30分钟,就会突然在用户浏览器无法访问使用用户登录状态;
1.解决办法:实现续租逻辑
每次校验登录状态,判断剩余时间ttl,如果发现登录状态的ticket在操作时,判断剩余时间小于10分钟,续租5分钟
工具类封装方法
1.判断剩余时间
//拿到剩余时间
public Long queryTimeLeft(String key){
ShardedJedis jedis = pool.getResource();
try{
return jedis.ttl(key);
}catch(Exception e){
e.printStackTrace();
return 0l;
}finally{
pool.returnBrokenResource(jedis);
}
}
2.延长时间
//延迟时间
public void expandTime(String key,Integer seconds){
ShardedJedis jedis = pool.getResource();
try{
jedis.expire(key, seconds);
}catch(Exception e){
e.printStackTrace();
}finally{
pool.returnBrokenResource(jedis);
}
}
2.UserController类
//校验登录状态,查询redis数据
@RequestMapping("query/{ticket}")
public String checkTicket(@PathVariable String ticket
,String callback){
try{
//走到redis校验数据
String userJson=userService.queryTicket(ticket);
//封装返回的json数据,SysResult
SysResult result=null;
if(StringUtils.isNotEmpty(userJson)){//登录状态正常
result=SysResult.build(200, "", userJson);
}else{
result=SysResult.build(201, "", null);
}
//将result解析成json等待返回使用
String resultJson=MapperUtils.MP.writeValueAsString(result);
//判断请求需要的数据格式,callback
if(callback==null){
return resultJson;//作为json字符串返回
}else{
return callback+"("+resultJson+")";//jsonp格式返回
}
}catch(Exception e){
return "";
}
}
3.userService类
public String queryTicket(String ticket) {
//TODO 超时时间延长--续租
//每次访问到这个方法,都是由于用户访问了head.jsp的js代码
//判断剩余时间
Long time=redis.queryTimeLeft(ticket);
if(time<60*10){//将时间添加5分钟做续租
Integer extTime=(int) (time+60*5);
redis.expandTime(ticket, extTime);
}
return redis.query(ticket);
}