houzhongjian
2024-09-14 818a0170d8f2950d52cc7300a302356bbc523236
提交 | 用户 | 时间
e7c126 1 package com.iailab.module.system.service.auth;
H 2
3 import cn.hutool.core.util.ObjectUtil;
4 import com.iailab.framework.common.enums.CommonStatusEnum;
5 import com.iailab.framework.common.enums.UserTypeEnum;
6 import com.iailab.framework.common.util.monitor.TracerUtils;
7 import com.iailab.framework.common.util.servlet.ServletUtils;
8 import com.iailab.framework.common.util.validation.ValidationUtils;
9 import com.iailab.module.system.api.logger.dto.LoginLogCreateReqDTO;
10 import com.iailab.module.system.api.sms.SmsCodeApi;
11 import com.iailab.module.system.api.social.dto.SocialUserBindReqDTO;
12 import com.iailab.module.system.api.social.dto.SocialUserRespDTO;
13 import com.iailab.module.system.controller.admin.auth.vo.*;
14 import com.iailab.module.system.convert.auth.AuthConvert;
15 import com.iailab.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
16 import com.iailab.module.system.dal.dataobject.user.AdminUserDO;
17 import com.iailab.module.system.enums.logger.LoginLogTypeEnum;
18 import com.iailab.module.system.enums.logger.LoginResultEnum;
19 import com.iailab.module.system.enums.oauth2.OAuth2ClientConstants;
20 import com.iailab.module.system.enums.sms.SmsSceneEnum;
21 import com.iailab.module.system.service.logger.LoginLogService;
22 import com.iailab.module.system.service.member.MemberService;
23 import com.iailab.module.system.service.oauth2.OAuth2TokenService;
24 import com.iailab.module.system.service.social.SocialUserService;
25 import com.iailab.module.system.service.user.AdminUserService;
26 import com.google.common.annotations.VisibleForTesting;
27 import com.xingyuv.captcha.model.common.ResponseModel;
28 import com.xingyuv.captcha.model.vo.CaptchaVO;
29 import com.xingyuv.captcha.service.CaptchaService;
30 import lombok.extern.slf4j.Slf4j;
31 import org.springframework.beans.factory.annotation.Value;
32 import org.springframework.stereotype.Service;
33
34 import javax.annotation.Resource;
35 import javax.validation.Validator;
36 import java.util.Objects;
37
38 import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
39 import static com.iailab.framework.common.util.servlet.ServletUtils.getClientIP;
40 import static com.iailab.module.system.enums.ErrorCodeConstants.*;
41
42 /**
43  * Auth Service 实现类
44  *
45  * @author iailab
46  */
47 @Service
48 @Slf4j
49 public class AdminAuthServiceImpl implements AdminAuthService {
50
51     @Resource
52     private AdminUserService userService;
53     @Resource
54     private LoginLogService loginLogService;
55     @Resource
56     private OAuth2TokenService oauth2TokenService;
57     @Resource
58     private SocialUserService socialUserService;
59     @Resource
60     private MemberService memberService;
61     @Resource
62     private Validator validator;
63     @Resource
64     private CaptchaService captchaService;
65     @Resource
66     private SmsCodeApi smsCodeApi;
67
68     /**
69      * 验证码的开关,默认为 true
70      */
71     @Value("${iailab.captcha.enable:true}")
72     private Boolean captchaEnable;
73
74     @Override
75     public AdminUserDO authenticate(String username, String password) {
76         final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
77         // 校验账号是否存在
78         AdminUserDO user = userService.getUserByUsername(username);
79         if (user == null) {
80             createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
81             throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
82         }
83         if (!userService.isPasswordMatch(password, user.getPassword())) {
84             createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
85             throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
86         }
87         // 校验是否禁用
88         if (CommonStatusEnum.isDisable(user.getStatus())) {
89             createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
90             throw exception(AUTH_LOGIN_USER_DISABLED);
91         }
92         return user;
93     }
94
95     @Override
96     public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
97         // 校验验证码
98         validateCaptcha(reqVO);
99
100         // 使用账号密码,进行登录
101         AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
102
103         // 如果 socialType 非空,说明需要绑定社交用户
104         if (reqVO.getSocialType() != null) {
105             socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
106                     reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
107         }
108         // 创建 Token 令牌,记录登录日志
109         return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
110     }
111
112     @Override
113     public void sendSmsCode(AuthSmsSendReqVO reqVO) {
114         // 登录场景,验证是否存在
115         if (userService.getUserByMobile(reqVO.getMobile()) == null) {
116             throw exception(AUTH_MOBILE_NOT_EXISTS);
117         }
118         // 发送验证码
119         smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
120     }
121
122     @Override
123     public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) {
124         // 校验验证码
125         smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())).getCheckedData();
126
127         // 获得用户信息
128         AdminUserDO user = userService.getUserByMobile(reqVO.getMobile());
129         if (user == null) {
130             throw exception(USER_NOT_EXISTS);
131         }
132
133         // 创建 Token 令牌,记录登录日志
134         return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
135     }
136
137     private void createLoginLog(Long userId, String username,
138                                 LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) {
139         // 插入登录日志
140         LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
141         reqDTO.setLogType(logTypeEnum.getType());
142         reqDTO.setTraceId(TracerUtils.getTraceId());
143         reqDTO.setUserId(userId);
144         reqDTO.setUserType(getUserType().getValue());
145         reqDTO.setUsername(username);
146         reqDTO.setUserAgent(ServletUtils.getUserAgent());
147         reqDTO.setUserIp(ServletUtils.getClientIP());
148         reqDTO.setResult(loginResult.getResult());
149         loginLogService.createLoginLog(reqDTO);
150         // 更新最后登录时间
151         if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) {
152             userService.updateUserLogin(userId, ServletUtils.getClientIP());
153         }
154     }
155
156     @Override
157     public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) {
158         // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
159         SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
160                 reqVO.getCode(), reqVO.getState());
161         if (socialUser == null || socialUser.getUserId() == null) {
162             throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
163         }
164
165         // 获得用户
166         AdminUserDO user = userService.getUser(socialUser.getUserId());
167         if (user == null) {
168             throw exception(USER_NOT_EXISTS);
169         }
170
171         // 创建 Token 令牌,记录登录日志
172         return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
173     }
174
175     @VisibleForTesting
176     void validateCaptcha(AuthLoginReqVO reqVO) {
177         // 如果验证码关闭,则不进行校验
178         if (!captchaEnable) {
179             return;
180         }
181         // 校验验证码
182         ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
183         CaptchaVO captchaVO = new CaptchaVO();
184         captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
185         ResponseModel response = captchaService.verification(captchaVO);
186         // 验证不通过
187         if (!response.isSuccess()) {
188             // 创建登录失败日志(验证码不正确)
189             createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR);
190             throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg());
191         }
192     }
193
194     private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
195         // 插入登陆日志
196         createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
197         // 创建访问令牌
198         OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
199                 OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
200         // 构建返回结果
201         return AuthConvert.INSTANCE.convert(accessTokenDO);
202     }
203
204     @Override
205     public AuthLoginRespVO refreshToken(String refreshToken) {
206         OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
207         return AuthConvert.INSTANCE.convert(accessTokenDO);
208     }
209
210     @Override
211     public void logout(String token, Integer logType) {
212         // 删除访问令牌
213         OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
214         if (accessTokenDO == null) {
215             return;
216         }
217         // 删除成功,则记录登出日志
218         createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType);
219     }
220
221     private void createLogoutLog(Long userId, Integer userType, Integer logType) {
222         LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
223         reqDTO.setLogType(logType);
224         reqDTO.setTraceId(TracerUtils.getTraceId());
225         reqDTO.setUserId(userId);
226         reqDTO.setUserType(userType);
227         if (ObjectUtil.equal(getUserType().getValue(), userType)) {
228             reqDTO.setUsername(getUsername(userId));
229         } else {
230             reqDTO.setUsername(memberService.getMemberUserMobile(userId));
231         }
232         reqDTO.setUserAgent(ServletUtils.getUserAgent());
233         reqDTO.setUserIp(ServletUtils.getClientIP());
234         reqDTO.setResult(LoginResultEnum.SUCCESS.getResult());
235         loginLogService.createLoginLog(reqDTO);
236     }
237
238     private String getUsername(Long userId) {
239         if (userId == null) {
240             return null;
241         }
242         AdminUserDO user = userService.getUser(userId);
243         return user != null ? user.getUsername() : null;
244     }
245
246     private UserTypeEnum getUserType() {
247         return UserTypeEnum.ADMIN;
248     }
249
250 }