package com.vci.web.service.impl; import com.vci.bo.LoginResultBO; import com.vci.constant.CacheNameConstant; import com.vci.corba.common.PLException; import com.vci.corba.framework.data.DeptInfo; import com.vci.corba.framework.data.LoginResult; import com.vci.corba.framework.data.LoginState; import com.vci.corba.framework.data.MachineInfo; import com.vci.dto.LoginUserDTO; import com.vci.lcstatuspck.FrameworkDataLCStatus; import com.vci.pagemodel.SmFunctionVO; import com.vci.pagemodel.SmPasswordStrategyVO; import com.vci.pagemodel.SmUserVO; import com.vci.starter.web.annotation.bus.VciLoginAfter; import com.vci.starter.web.annotation.bus.VciLogoutBefore; import com.vci.starter.web.annotation.bus.VciLogoutPlugin; import com.vci.starter.web.constant.VConstant; import com.vci.starter.web.exception.VciBaseException; import com.vci.starter.web.interceptor.VciSessionForLoginI; import com.vci.starter.web.pagemodel.RequestClientInfo; import com.vci.starter.web.pagemodel.SessionInfo; import com.vci.starter.web.pagemodel.TokenVO; import com.vci.starter.web.redis.RedisService; import com.vci.starter.web.util.ApplicationContextProvider; import com.vci.starter.web.util.Lcm.Func; import com.vci.starter.web.util.VciBaseUtil; import com.vci.starter.web.util.VciDateUtil; import com.vci.starter.web.util.WebThreadLocalUtil; import com.vci.web.ajaxTask.SmUserUnLockTask; import com.vci.web.ajaxTask.SmUserUnLockTaskManager; import com.vci.web.enumpck.ResourceControlTypeEnum; import com.vci.web.properties.VciSecurityManageProperties; import com.vci.web.properties.WebProperties; import com.vci.web.service.ISmFunctionQueryService; import com.vci.web.service.OsLoginServiceI; import com.vci.web.service.SmPwdStrategyQueryServiceI; import com.vci.web.service.SmUserQueryServiceI; import com.vci.web.util.PlatformClientUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.*; import java.util.stream.Collectors; import static com.vci.constant.FrameWorkBusLangCodeConstant.*; /** * 登录的服务 * @author weidy * @date 2020/1/29 */ @Service public class OsLoginServiceImpl implements OsLoginServiceI { /** * 日志 */ private Logger logger = LoggerFactory.getLogger(getClass()); /** * 会话处理 */ @Resource private VciSessionForLoginI sessionForLogin; /** * 解锁任务 */ @Resource private SmUserUnLockTaskManager unLockTaskManager; /** * 权限管理的相关配置 */ @Resource private VciSecurityManageProperties securityManageProperties; /** * 用户查询服务,可以兼容老平台 */ @Resource private SmUserQueryServiceI userQueryService; /** * 密码策略查询服务 */ @Resource private SmPwdStrategyQueryServiceI pwdStrategyQueryService; /** * 功能菜单的查询服务 */ @Resource private ISmFunctionQueryService functionQueryService; /** * 角色的查询服务,可以兼容老平台 */ //@Resource //private SmRoleQueryServiceI roleQueryService; /** * 权限的查询服务,可以兼容老平台 */ //@Resource //private ISmFunctionQueryService functionQueryService; /** * redis服务 */ @Resource private RedisService redisService; /** * 客户端配置文件 */ @Resource private WebProperties webProperties; /** * 平台调用客户端 */ @Resource private PlatformClientUtil platformClientUtil; /** * 执行登录 * @param userDTO 登录信息 * @param clientInfo 请求的客户端的信息 * @throws VciBaseException 登录发生异常的时候出现了错误 */ @Override public LoginResultBO login(LoginUserDTO userDTO, RequestClientInfo clientInfo) throws Exception { return login(userDTO,clientInfo,true); } /** * 登录 * @param userDTO 用户的数据传输对象 * @param clientInfo 客户端的信息 * @param checkPassword 是否校验密码 * @return 执行结果 * @throws VciBaseException 参数错误,用户不能登录等会抛出异常 */ private LoginResultBO login(LoginUserDTO userDTO, RequestClientInfo clientInfo, boolean checkPassword/*单点登录不需要校验密码*/) throws Exception { LoginResultBO loginResult = new LoginResultBO(); loginResult.setSuccess(false); //1.判断用户的基本信息 VciBaseUtil.alertNotNull(userDTO, "登录信息", userDTO.getUserId(), "用户账号"); if (checkPassword) { VciBaseUtil.alertNotNull(userDTO.getPassword(), "登录密码"); } //2、判断单设备登录,是否已经登录了 String userIdTokenKey = CacheNameConstant.cacheKey(CacheNameConstant.USERID_TOKEN_KEY, userDTO.getUserId().trim()); if(redisService.hasKey(userIdTokenKey) && !userDTO.isForceLogin() && securityManageProperties.isUserOneLogin()){ loginResult.setFailCode(USER_IS_LOGINED); loginResult.setFailMsg("当前用户已经在其他地方登录!"); return loginResult; } //说明已经登录了,那应该取消原来的登录 if (redisService.hasKey(userIdTokenKey) && userDTO.isForceLogin() && securityManageProperties.isUserOneLogin()) { String tokenKey = redisService.getCacheObject(userIdTokenKey); redisService.deleteObject(tokenKey); redisService.deleteObject(userIdTokenKey); redisService.decreOnlineUser(VConstant.CURRENT_LOGGED_USERS_KEY); } //3.获取用户的对象(对象中包含角色部门还有密码策略信息(当前用户没设置密码策略就是取的默认密码策略)) SmUserVO user = getUserByUserId(userDTO.getUserId().trim()); if (user == null || StringUtils.isBlank(user.getOid())) { loginResult.setFailCode(USER_NOT_FOUND); loginResult.setFailMsgArray(new String[]{userDTO.getUserId()}); return loginResult; } //5、调用平台登录接口,进行登录 MachineInfo machine = getMachieInfo(clientInfo); machine.country = clientInfo.getCountry(); machine.language = clientInfo.getLanguage(); machine.osUser = clientInfo.getOsUser(); machine.machine = clientInfo.getMachine(); String token = null; try { //前端传过来的密码是经过加密传输的,所以需要按照约定解密,再传给平台进行比对 userDTO.setPassword(Func.decryptAes(userDTO.getPassword(),"daliantan0v0vcip")); LoginResult chkRes = platformClientUtil.getFrameworkService().checkLogin(userDTO.getUserId(),userDTO.getPassword(), machine); loginResult.setFailCode(getErrorCode(chkRes)); //loginResult.setFailMsgArray(new String[]{userDTO.getUserId(), String.valueOf(chkRes.auxInfo)}); loginResult.setFailMsgArray(new String[]{String.valueOf(chkRes.auxInfo)}); //根据不同状态处理平台返回的信息 if(chkRes.state.equals(LoginState.Error) || chkRes.state.equals(LoginState.Locked) || chkRes.state.equals(LoginState.Freeze)){ return loginResult; } //关于密码策略相关的返回信息处理 if(chkRes.state.equals(LoginState.InitialPW)){ loginResult.setMustChangePassword(true); loginResult.setPasswordInfo("您的密码是管理员初始的密码,需要修改密码才能进行其它操作!"); return loginResult; }else if(chkRes.state.equals(LoginState.PWExpired)){ //需要立即修改密码 loginResult.setMustChangePassword(true); loginResult.setPasswordInfo("您的密码已经过期,请进行修改!"); return loginResult; }else if(chkRes.state.equals(LoginState.PWPolicyUpdated)){ //策略修改,直接返回限制用户后续操作 loginResult.setMustChangePassword(true); loginResult.setPasswordInfo("您的密码策略已经修改,需要修改密码才能进行其它操作!"); return loginResult; }else if(chkRes.state.equals(LoginState.PWWillExpire)){ loginResult.setPasswordInfo(String.format("您的密码有效期还有%s天,请注意修改!",chkRes.auxInfo)); } token = chkRes.token; } catch (Exception e) { loginResult.setFailCode(SYSTEM_ERROR); loginResult.setFailMsgArray(new String[]{userDTO.getUserId()}); return loginResult; } if(StringUtils.isBlank(token)){ loginResult.setFailMsg(TOKEN_EMPTY); loginResult.setFailMsgArray(new String[]{userDTO.getUserId()}); return loginResult; } //6、登录成功之后需要处理的逻辑 user.setLastLoginTime(new Date());//最后登录时间 //user.setPwdWrongCount(0);//登录成功密码错误次数清0 platformClientUtil.getFrameworkService().updateLogonInfo(user.getOid(),true); //处理用户登录成功的session SessionInfo sessionInfo = new SessionInfo(); sessionInfo.setToken(token); //初始化平台的token sessionForLogin.initInvocationInfo(sessionInfo); //记录当前登录人数的总数 redisService.increOnlineUser(VConstant.CURRENT_LOGGED_USERS_KEY); //拷贝用户到新的session会话中 copyUser2SessionInfo(user, sessionInfo, userDTO.getLangCode()); //拷贝请求信息到session会话中 copyRequest2SessionInfo(clientInfo, sessionInfo); //查看了平台的登录方法其实是有处理部门角色等相关信息的但是不知道为什么无法获取到 //部门信息处理 sessionInfo.setDeptOid(user.getPkDepartment()); sessionInfo.setDeptName(user.getPkDepartmentName()); sessionInfo.setDeptNum(user.getPkDepartmentNum()); //角色信息处理 String roleOids = user.getPkPerson(); String roleNames = user.getPkPersonName(); HashMap roleOidNameMap = new HashMap<>(); if(Func.isNotBlank(roleOids) && Func.isNotBlank(roleOids)){ String[] oids = roleOids.split(","); String[] names = roleNames.split(","); for (int i = 0; i < oids.length; i++) { roleOidNameMap.put(oids[i],names[i]); } } sessionInfo.setRolesName(roleOidNameMap); //查询所有的权限 List functionVOList = functionQueryService.listFunctionByUserOid(user.getOid(), null, ResourceControlTypeEnum.BS); if (!CollectionUtils.isEmpty(functionVOList)) { List functionOidList = functionVOList.stream().map(s -> s.getOid()).collect(Collectors.toList()); sessionInfo.setFunctionOids(functionOidList); } else { sessionInfo.setFunctionOids(new ArrayList()); } //添加到会话信息 TokenVO tokenVO = saveSessionInfo(sessionInfo); loginResult.setTokenVO(tokenVO); loginResult.setSuccess(true); return loginResult; } private MachineInfo getMachieInfo(RequestClientInfo clientInfo) { MachineInfo machine = new MachineInfo(); machine.country = clientInfo.getCountry(); machine.language = clientInfo.getLanguage(); machine.osUser = clientInfo.getOsUser(); machine.machine = clientInfo.getMachine(); return machine; } /** * 获取用户信息 * @param userId userId * @Return com.vci.frameworkcore.pagemodel.SmUserVO */ private SmUserVO getUserByUserId(String userId) { return userQueryService.getUserByUserId(userId); } /** * 退出系统 * * @param userToken 用户的许可码 * @throws VciBaseException 删除会话信息出错的时候会抛出异常 */ @Override public void logout(String userToken) throws Exception { VciBaseUtil.alertNotNull(userToken,"用户的会话许可"); Map logoutpluginBeanMap =ApplicationContextProvider.getApplicationContext().getBeansWithAnnotation(VciLogoutPlugin.class); if(!CollectionUtils.isEmpty(logoutpluginBeanMap)){ logoutpluginBeanMap.forEach((k,v) -> { Method[] methods = v.getClass().getDeclaredMethods(); if(methods!=null&& methods.length>0){ for(Method method:methods){ if(method.isAnnotationPresent(VciLogoutBefore.class)){ try { method.invoke(v,userToken); } catch(Throwable e){ if(logger.isErrorEnabled()){ logger.error("调用退出之前的插件出错",e); } throw new VciBaseException("调用退出之前的插件出错,{0},{1}",new String[]{v.getClass().getName(),method.getName()},e); } } } } }); } //清除存当前登录的用户(总数-1) redisService.decreOnlineUser(VConstant.CURRENT_LOGGED_USERS_KEY); sessionForLogin.logout( WebThreadLocalUtil.getCurrentUserSessionInfoInThread().getToken() ,WebThreadLocalUtil.getCurrentUserSessionInfoInThread().getUserId() ); if(!CollectionUtils.isEmpty(logoutpluginBeanMap)){ logoutpluginBeanMap.forEach((k,v) -> { Method[] methods = v.getClass().getDeclaredMethods(); if(methods!=null&& methods.length>0){ for(Method method:methods){ if(method.isAnnotationPresent(VciLoginAfter.class)){ try { method.invoke(v,userToken); } catch(Throwable e){ if(logger.isErrorEnabled()){ logger.error("调用退出登录之后的插件出错",e); } throw new VciBaseException("调用退出登录之后插件出错,{0},{1}",new String[]{v.getClass().getName(),method.getName()},e); } } } } }); } } /** * 保存会话信息 * @param sessionInfo 会话信息 */ private TokenVO saveSessionInfo(SessionInfo sessionInfo){ if(sessionForLogin == null){ throw new VciBaseException("没有配置会话存储的服务"); } WebThreadLocalUtil.setCurrentUserSessionInfoInThread(sessionInfo); return sessionForLogin.createToken(sessionInfo); } /** * 拷贝请求的信息到会话信息中 * @param clientInfo 请求信息 * @param sessionInfo 会话信息 */ private void copyRequest2SessionInfo(RequestClientInfo clientInfo, SessionInfo sessionInfo) { sessionInfo.setIp(clientInfo.getIpaddress()); //ip的地址在controller里设置 sessionInfo.setOs(clientInfo.getOsversion()); sessionInfo.setBrowser(clientInfo.getBrowserversion()); sessionInfo.setSso(clientInfo.isSso()); sessionInfo.setSsoServiceName(clientInfo.getSsoSystemName()); } /** * 拷贝用户的信息到 会话信息 * @param user 用户对象 * @param sessionInfo 会话对象 * @param langCode 语言编码 */ private void copyUser2SessionInfo(SmUserVO user, SessionInfo sessionInfo, String langCode){ sessionInfo.setUserOid(user.getOid()); sessionInfo.setUserId(user.getId()); sessionInfo.setUserName(user.getName()); sessionInfo.setUsertype(user.getUserType()); sessionInfo.setUsertypeText(user.getUserTypeText()); sessionInfo.setUserSecret(user.getSecretGrade()==null?"":(user.getSecretGrade()+ "")); sessionInfo.setUserSecretText(user.getSecretGradeText()); sessionInfo.setSex(user.getSex()); sessionInfo.setSexText(user.getSexText()); //sessionInfo.setPhotoUrl(user.getPhoto()); sessionInfo.setLanguage(user.getLangCode()); if(StringUtils.isNotBlank(langCode)){ //传递了要显示的语言 sessionInfo.setLanguage(langCode); } sessionInfo.setPersonOid(user.getPkPerson()); sessionInfo.setPersonName(user.getPkPersonName()); sessionInfo.setDeptOid(user.getPkDepartment()); sessionInfo.setDeptName(user.getPkDepartmentName()); sessionInfo.setDutyOid(user.getPkDuty()); sessionInfo.setDutyName(user.getPkDutyName()); sessionInfo.setEmail(user.getEmail()); sessionInfo.setPhoneNo(user.getTel()); sessionInfo.setRtxNo(user.getRtxNo()); sessionInfo.setIMId(user.getIMNo()); sessionInfo.setPortalId(user.getId()); sessionInfo.setLastLoginTime(user.getLastLoginTime()!=null?user.getLastLoginTime().getTime(): VciDateUtil.getNowTime()); } /** * 更新用户的错误次数 * @param userOid 用户主键 * @param wrongCount 错误次数 */ private void updateUserPwdWrongCount(String userOid,Integer wrongCount){ userQueryService.updateUserPwdWrongCount(userOid,wrongCount); } /** * 添加用户到解锁队列中 * @param userId 用户的账号 * @param lockTime 锁定时间,单位是分 */ public void addUserToUnLock(String userId,Integer lockTime){ //是因为被锁定,需要添加自动解锁 if(lockTime == null){ //防止数据出错,没有lockTime lockTime = 30; } userQueryService.lockUser(userId); unLockTaskManager.put(new SmUserUnLockTask(userQueryService,userId,(long)lockTime*60*1000)); } /** * 登录成功后更新用户信息 * @param userOid 用户主键 */ private void updateUserForLoginSuccess(String userOid){ userQueryService.updateUserLoginTime(userOid); } /** * 获取登录错误码 * @param chkRes 平台登录校验结果 * @Return java.lang.String */ public String getErrorCode(LoginResult chkRes){ String message = ""; switch(chkRes.state.value()) { case 0: return UNKNOWN; case 1: return LOGIN_SUCCESS; case 10: return USER_NOT_FOUND; case 11: return USER_PWD_NOT_EQUAL; case 12: return USER_IS_DISABLED; case 13: return USER_IS_LOCK; case 14: return PASSWORD_INIT; case 15: return PASSWORD_REMAIN_DAY; case 16: return PASSWORD_EXPIRE; case 17: return PASSWORD_POLICY_UPDATED; case 20: return SYSTEM_ERROR; default: message = SYSTEM_ERROR; break; } return message; } }