package com.vci.web.service.impl; import com.vci.frameworkcore.ajaxTask.SmUserUnLockTask; import com.vci.frameworkcore.ajaxTask.SmUserUnLockTaskManager; import com.vci.frameworkcore.compatibility.ISmFunctionQueryService; import com.vci.frameworkcore.compatibility.SmRoleQueryServiceI; import com.vci.frameworkcore.compatibility.SmUserQueryServiceI; import com.vci.frameworkcore.pagemodel.SmUserVO; import com.vci.frameworkcore.properties.VciSecurityManageProperties; import com.vci.starter.web.annotation.bus.*; 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.util.ApplicationContextProvider; import com.vci.starter.web.util.Md5; import com.vci.starter.web.util.VciBaseUtil; import com.vci.web.bo.LoginResultBO; import com.vci.web.dto.LoginUserDTO; import com.vci.web.service.LoginServiceI; import com.vci.web.util.BusAnnotationUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.lang.reflect.Method; import java.util.Map; /** * 登录的服务 * @author weidy * @date 2020/1/29 */ @Service public class LoginServiceImpl implements LoginServiceI { /** * 日志 */ private Logger logger = LoggerFactory.getLogger(getClass()); /** * 会话处理 */ @Autowired private VciSessionForLoginI sessionForLogin; /** * 解锁任务 */ @Autowired private SmUserUnLockTaskManager unLockTaskManager; /** * 权限管理的相关配置 */ @Autowired private VciSecurityManageProperties securityManageProperties; /** * 用户查询服务,可以兼容老平台 */ @Autowired() private SmUserQueryServiceI userQueryService; /** * 角色的查询服务,可以兼容老平台 */ @Autowired private SmRoleQueryServiceI roleQueryService; /** * 权限的查询服务,可以兼容老平台 */ @Autowired private ISmFunctionQueryService functionQueryService; /** * 执行登录 * @param userDTO 登录信息 * @param clientInfo 请求的客户端的信息 * @throws VciBaseException 登录发生异常的时候出现了错误 */ @Override public LoginResultBO login(LoginUserDTO userDTO, RequestClientInfo clientInfo) throws VciBaseException { return login(userDTO,clientInfo,true); } /** * 登录 * @param userDTO 用户的数据传输对象 * @param clientInfo 客户端的信息 * @param checkPassword 是否校验密码 * @return 执行结果 * @throws VciBaseException 参数错误,用户不能登录等会抛出异常 */ private LoginResultBO login(LoginUserDTO userDTO, RequestClientInfo clientInfo, boolean checkPassword) throws VciBaseException { LoginResultBO loginResult = new LoginResultBO(); loginResult.setSuccess(false); //1.判断用户的基本信息 // VciBaseUtil.alertNotNull(userDTO, "登录信息", userDTO.getUserId(), "用户账号"); // if (checkPassword) { // VciBaseUtil.alertNotNull(userDTO.getPassword(), "登录密码"); // } //登录之前调用插件 BusAnnotationUtil.callForAnnotation(VciLoginPlugin.class,VciLoginBefore.class,userDTO,clientInfo); // String loginInfo = checkIsLogined(userDTO.getUserId()); // if (!userDTO.isForceLogin() && securityManageProperties.isUserOneLogin()) { // //需要看看是否已经登录了 // if (StringUtils.isNotBlank(loginInfo)) { // loginResult.setFailCode(USER_IS_LOGINED); // loginResult.setFailMsg(loginInfo); // return loginResult; // } // } //2.获取用户的对象.这里需要兼容老平台和老pdm,老编码和老平台是一个表 // SmUserVO user = userQueryService.getUserByUserId(userDTO.getUserId().trim()); // if (user == null || StringUtils.isEmpty(user.getOid())) { // loginResult.setFailCode(USER_NOT_FOUND); // loginResult.setFailMsgArray(new String[]{userDTO.getUserId()}); // return loginResult; // } //如果用户已经被停用和锁定,不能登录 //如果用户的失效日期已经超过了当前时间,不能登录 //只有新平台的用户才判断失效 // if (FrameworkDataLCStatus.DISABLED.getValue().equals(user.getLcStatus())) { // loginResult.setFailCode(USER_IS_DISABLED); // loginResult.setFailMsgArray(new String[]{userDTO.getUserId()}); // return loginResult; // } // if (userQueryService.checkUserLock(user, user.getPwdWrongCount())) { // loginResult.setFailCode(USER_IS_LOCK); // loginResult.setFailMsgArray(new String[]{userDTO.getUserId()}); // return loginResult; // } // if (user.getDisabledate() != null) { // //2021版本才有这个属性的值,当前这个没有这个值 // Date disableDate = VciDateUtil.addOrSubDate(user.getDisabledate(), Calendar.DATE, 1); // if (disableDate != null && disableDate.getTime() < System.currentTimeMillis()) { // loginResult.setFailCode(USER_MORE_THAN_DISABLE_DATE); // loginResult.setFailMsgArray(new String[]{userDTO.getUserId()}); // return loginResult; // } // } // if (user.getPwdWrongCount() == null) { // user.setPwdWrongCount(0); // } // SmPasswordStrategyVO passwordStrategyVO = userQueryService.getPasswordStrategyVOByUserOid(user.getOid()); // if (checkPassword) { // boolean passwordIsEqual = userQueryService.checkPasswordEqual(userDTO.getPassword(), user.getOid()); // //3.判断用户的密码是否正确 // if (!passwordIsEqual) { // //前端需要先md5一次,然后后台再MD5一次, // if (logger.isDebugEnabled()) { // logger.debug("{}密码不正确", user.getId()); // } // if (passwordStrategyVO == null) { // //可能数据问题没有设置密码策略 // passwordStrategyVO = new SmPasswordStrategyVO(); // passwordStrategyVO.setRetryTime(6); // passwordStrategyVO.setLockTime(30); // } // if (passwordStrategyVO.getRetryTime() <= (user.getPwdWrongCount() + 1)) { // user.setLockFlag(true); // updateUserPwdWrongCount(user.getOid(), user.getPwdWrongCount() + 1); // addUserToUnLock(userDTO.getUserId(), passwordStrategyVO.getLockTime()); // updateUserPwdWrongCount(user.getOid(), user.getPwdWrongCount() + 1); // loginResult.setFailCode(USER_PWD_LOCK); // loginResult.setFailMsgArray(new String[]{userDTO.getUserId(), passwordStrategyVO.getLockTime() + ""}); // return loginResult; // } else { // //还没有到锁定的次数 // updateUserPwdWrongCount(user.getOid(), user.getPwdWrongCount() + 1); // //5, 这是第一次错误,剩下的是 5- (0+1) // loginResult.setFailCode(USER_PWD_NOT_EQUAL); // loginResult.setFailMsgArray(new String[]{userDTO.getUserId(), (passwordStrategyVO.getRetryTime() - (user.getPwdWrongCount() + 1)) + ""}); // return loginResult; // } // } // } //说明密码正确的 // if (logger.isDebugEnabled()) { // logger.debug("{}的密码正确", user.getId()); // } // user.setLastLoginTime(new Date()); // user.setPwdWrongCount(0); SmUserVO user = new SmUserVO(); user.setId("1"); user.setName("1"); user.setOid("1"); user.setUserType("1"); user.setUserTypeText("1"); user.setSecretGrade(1); user.setSecretGradeText("1"); user.setSex("1"); user.setSexText("1"); user.setPkPerson("1"); user.setPkPersonName("1"); user.setPkDepartment("1"); user.setPkDepartmentName("1"); user.setPkDuty("1"); user.setPkDutyName("1"); user.setEmail("1"); user.setTel("1"); user.setRtxNo("1"); user.setIMNo("1"); SessionInfo sessionInfo = new SessionInfo(); //拷贝用户的新到session会话中 copyUser2SessionInfo(user, sessionInfo, userDTO.getLangCode()); //拷贝请求信息到session会话中 copyRequest2SessionInfo(clientInfo, sessionInfo); //查询所有的角色 // List roleVOList = roleQueryService.listRoleByUserOid(user.getOid(), null); // if (!CollectionUtils.isEmpty(roleVOList)) { // Map roleOidNameMap = roleVOList.stream().collect(Collectors.toMap(s -> s.getOid(), t -> t.getName())); // sessionInfo.setRolesName(roleOidNameMap); // } else { // sessionInfo.setRolesName(new HashMap()); // } //查询所有的权限 // 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()); // } loginResult.setSuccess(true); //检查是否该修改密码 // if (!clientInfo.isSso() && checkPassword) { // //最后修改时间+ 失效时间,大于等于当前日期,则需要马上修改密码 // Date currentDay = null; // try { // currentDay = VciDateUtil.getNow(VciDateUtil.DateFormat); // } catch (Throwable e) { // if (logger.isErrorEnabled()) { // logger.error("获取当前日期", e); // } // } // if (currentDay != null && passwordStrategyVO != null && passwordStrategyVO.getValidDay() != null) { // Date inValidDay = null; // if (user.getLastModifyPasswordTime() == null) { // //重来没有登录过 // loginResult.setMustChangePassword(true); // } else { // inValidDay = VciDateUtil.addOrSubDate(user.getLastModifyPasswordTime(), Calendar.DATE, passwordStrategyVO.getValidDay()); // if (inValidDay.getTime() <= (currentDay).getTime()) { // loginResult.setMustChangePassword(true); // } // } // if (!loginResult.isMustChangePassword()) { // if (VciDateUtil.addOrSubDate(inValidDay, Calendar.DATE, -(passwordStrategyVO.getRemindDay())).getTime() // <= (currentDay).getTime()) { // //您的密码还有{0}天过期,请及时修改密码 // long remainDay = VciDateUtil.getDaySub(inValidDay, currentDay); // loginResult.setPasswordInfo(MessageFormat.format(PASSWORD_REMAIN_DAY, new String[]{String.valueOf(remainDay)})); // } // } // } // } //原本想使用jwt来生成token,但是有以下问题 //1.jwt不能处理注销的问题 //2.jwt生成的token太长了 //3.因为本平台不是互联网系统,只需要解决分布式用户信息的获取和权限的校验即可。 //4.平台引用了redis和数据库来存储会话的信息,只需要保证根据token能获取到会话信息即可 //5.在服务启动的时候,将会话信息清除,在注销的时候将会话信息清除 //uuid在高并发的情况下会重复,但是传统软件并发很小,所以出现的重复的概率很小 sessionInfo.setToken(Md5.md5(VciBaseUtil.getPk() + "_" + user.getId())); loginResult.setSessionInfo(sessionInfo); // updateUserForLoginSuccess(user.getOid()); // if (StringUtils.isNotBlank(loginInfo) && userDTO.isForceLogin() && securityManageProperties.isUserOneLogin()) { // //说明已经登录了,那应该取消原来的登录 // popUser(userDTO.getUserId()); // } //添加到会话信息 // saveSessionInfo(sessionInfo); //登录后执行 // BusAnnotationUtil.callForAnnotation(VciLoginPlugin.class,VciLoginAfter.class,userDTO,clientInfo,loginResult); return loginResult; } /** * 单点登录 * * @param userDTO 登录信息 * @param clientInfo 请求的客户端的信息 * @return 登录的结果对象 * @throws VciBaseException 登录失败的时候抛出异常 */ @Override public LoginResultBO singleLogin(LoginUserDTO userDTO, RequestClientInfo clientInfo) throws VciBaseException { return login(userDTO,clientInfo,false); } /** * 退出系统 * * @param userToken 用户的许可码 * @throws VciBaseException 删除会话信息出错的时候会抛出异常 */ @Override public void logout(String userToken) throws VciBaseException { 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); } } } } }); } sessionForLogin.logout(userToken); 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 userId 用户名 * @return 已经登录时, */ private String checkIsLogined(String userId) { if(sessionForLogin == null){ throw new VciBaseException("没有配置会话存储的服务"); } return sessionForLogin.checkIsLogined(userId); } /** * 把以前的登录信息移除 * @param userId 用户名 */ private void popUser(String userId){ if(sessionForLogin == null){ throw new VciBaseException("没有配置会话存储的服务"); } sessionForLogin.popUser(userId); } /** * 保存会话信息 * @param sessionInfo 会话信息 */ private void saveSessionInfo(SessionInfo sessionInfo){ if(sessionForLogin == null){ throw new VciBaseException("没有配置会话存储的服务"); } sessionForLogin.saveSessionInfo(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.setWorkNo(user.getWorkNo()); // sessionInfo.setWorkTypeOid(user.getPkWorkType()); // sessionInfo.setWorkTypeName(user.getPkWorkTypeText()); } /** * 更新用户的错误次数 * @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); } }