ludc
2025-01-16 5203081b68e3a8dc139d1807b2f8774e4a00a82a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
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<String, String> 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<SmFunctionVO> functionVOList = functionQueryService.listFunctionByUserOid(user.getOid(), null, ResourceControlTypeEnum.BS);
        if (!CollectionUtils.isEmpty(functionVOList)) {
            List<String> 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<String, Object> 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;
    }
}