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.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 javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; @Component public class SysLoginService { @Autowired private SysUserService sysUserService; @Autowired private SysPasswordService passwordService; @Autowired private RedisUtils redisUtils; @Autowired private HttpServletRequest request; public SysUser login(String username, String password,Integer identity) { SysUser sysUser=sysUserService.getUserByUsernamePhone(username,identity); validUser(sysUser); passwordService.validate(sysUser,password); recordLoginInfo(sysUser.getId()); return sysUser; } public void validUser(SysUser sysUser){ if(sysUser==null) { throw new AuthenticationException("用户不存在"); } String uri=request.getRequestURI(); if(uri.startsWith("/api/system")){ if(UserIdentityEnum.EXPERT.getCode().equals(sysUser.getIdentity())){ throw new AuthenticationException("专家用户无权登录后台"); } }else if(uri.startsWith("/api/app/")){ if(!UserIdentityEnum.EXPERT.getCode().equals(sysUser.getIdentity())){ throw new AuthenticationException("只有专家用户才能登录APP"); } } 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); Integer identity=JwtTokenUtil.getIdentity(jwtToken); if(StringUtils.isEmpty(username)||identity==null){ throw new AuthenticationException("token非法无效!"); } SysUser sysUser=sysUserService.getUserByUsernamePhone(username,identity); validUser(sysUser); if(!JwtTokenUtil.verify(jwtToken,username,sysUser.getPassword(),identity)){ throw new AuthenticationException("token非法无效!"); } if(!jwtTokenRefresh(jwtToken,username,sysUser.getPassword(),identity)){ throw new AuthenticationException("您的账号登录过期,请重新登录!"); } // 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, Integer identity){ String tokenKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+":"+JwtTokenUtil.md5Encode(jwtToken)); // String userKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+":"+username+"_"+identity); String cacheToken= (String) redisUtils.get(tokenKey); if(StringUtils.isNotEmpty(cacheToken)){ // 校验token有效性 if(!JwtTokenUtil.isNeedUpdate(cacheToken,username,passWord,identity)){ String newToken=JwtTokenUtil.sign(username,passWord,identity); // 设置超时时间 redisUtils.set(tokenKey,newToken,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); } }