package com.gkhy.assess.framework.shiro.service; import com.gkhy.assess.common.constant.CacheConstant; import com.gkhy.assess.common.enums.ApproveStatusEnum; import com.gkhy.assess.common.enums.UserIdentityEnum; import com.gkhy.assess.common.enums.UserStatusEnum; import com.gkhy.assess.common.exception.ApiException; import com.gkhy.assess.common.utils.JwtTokenUtil; import com.gkhy.assess.common.utils.RedisUtils; import com.gkhy.assess.system.domain.SysUser; import com.gkhy.assess.system.service.SysUserService; import com.gkhy.assess.system.utils.ShiroUtils; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.authc.AuthenticationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component public class SysLoginService { @Autowired private SysUserService sysUserService; @Autowired private SysPasswordService passwordService; @Autowired private RedisUtils redisUtils; public SysUser login(String username, String password) { SysUser sysUser=sysUserService.getUserByUsernamePhone(username); validUser(sysUser); passwordService.validate(sysUser,password); recordLoginInfo(sysUser.getId()); return sysUser; } public void validUser(SysUser sysUser){ if(sysUser==null) { throw new AuthenticationException("用户不存在"); } if(UserStatusEnum.DELETED.getCode().equals(sysUser.getDelFlag())){ throw new AuthenticationException("用户已被删除"); } if(UserStatusEnum.DISABLE.getCode().equals(sysUser.getStatus())){ throw new AuthenticationException("用户已被停用"); } if(UserIdentityEnum.AGENCY.getCode().equals(sysUser.getIdentity())){ if(!ApproveStatusEnum.APPROVED.getCode().equals(sysUser.getState())){ throw new AuthenticationException("机构账户审批还未通过"); } } } public SysUser validJwtToken(String jwtToken){ String username= JwtTokenUtil.getUsername(jwtToken); if(StringUtils.isEmpty(username)){ throw new AuthenticationException("token非法无效!"); } SysUser sysUser=sysUserService.getUserByUsernamePhone(username); validUser(sysUser); if(!jwtTokenRefresh(jwtToken,username,sysUser.getPassword())){ throw new AuthenticationException("Token失效,请重新登录!"); } // setRolePermission(sysUser); return sysUser; } /** * JWTToken刷新生命周期 (实现: 用户在线操作不掉线功能) * 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样),缓存有效期设置为Jwt有效时间的2倍 * 2、当该用户再次请求时,通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证 * 3、当该用户这次请求jwt生成的token值已经超时,但该token对应cache中的k还是存在,则表示该用户一直在操作只是JWT的token失效了,程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值,该缓存生命周期重新计算 * 4、当该用户这次请求jwt在生成的token值已经超时,并在cache中不存在对应的k,则表示该用户账户空闲超时,返回用户信息已失效,请重新登录。 * 注意: 前端请求Header中设置Authorization保持不变,校验有效性以缓存中的token为准。 * 用户过期时间 = Jwt有效时间 * 2。 * * @param username * @param passWord * @return */ public boolean jwtTokenRefresh(String jwtToken,String username,String passWord){ String tokenKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+":"+JwtTokenUtil.md5Encode(jwtToken)); String userKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+":"+username); String cacheToken= (String) redisUtils.get(tokenKey); if(StringUtils.isNotEmpty(cacheToken)){ // 校验token有效性 if(!JwtTokenUtil.verify(cacheToken,username,passWord)){ String newToken=JwtTokenUtil.sign(username,passWord); // 设置超时时间 redisUtils.set(tokenKey,newToken); redisUtils.expire(tokenKey,JwtTokenUtil.EXPIRATION*2/1000); redisUtils.expire(userKey,(JwtTokenUtil.EXPIRATION*2/1000)+2); } return true; } return false; } public void recordLoginInfo(Long userId){ SysUser user=new SysUser(); user.setId(userId); user.setLoginIp(ShiroUtils.getIp()); user.setLoginDate(LocalDateTime.now()); sysUserService.updateById(user); } }