|
@@ -0,0 +1,284 @@
|
|
|
+package com.citu.module.menduner.system.service.auth;
|
|
|
+
|
|
|
+import cn.hutool.core.lang.Assert;
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import com.citu.framework.common.enums.CommonStatusEnum;
|
|
|
+import com.citu.framework.common.enums.TerminalEnum;
|
|
|
+import com.citu.framework.common.enums.UserTypeEnum;
|
|
|
+import com.citu.framework.common.util.monitor.TracerUtils;
|
|
|
+import com.citu.framework.common.util.servlet.ServletUtils;
|
|
|
+import com.citu.module.menduner.system.controller.admin.auth.vo.*;
|
|
|
+import com.citu.module.menduner.system.convert.MdeAuthConvert;
|
|
|
+import com.citu.module.menduner.system.dal.dataobject.user.MdeUserDO;
|
|
|
+import com.citu.module.menduner.system.enums.sms.MdeSmsSceneEnum;
|
|
|
+import com.citu.module.menduner.system.service.user.MdeUserService;
|
|
|
+import com.citu.module.system.api.logger.LoginLogApi;
|
|
|
+import com.citu.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
|
|
+import com.citu.module.system.api.oauth2.OAuth2TokenApi;
|
|
|
+import com.citu.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
|
|
+import com.citu.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
|
|
+import com.citu.module.system.api.sms.SmsCodeApi;
|
|
|
+import com.citu.module.system.api.social.SocialClientApi;
|
|
|
+import com.citu.module.system.api.social.SocialUserApi;
|
|
|
+import com.citu.module.system.api.social.dto.SocialUserBindReqDTO;
|
|
|
+import com.citu.module.system.api.social.dto.SocialUserRespDTO;
|
|
|
+import com.citu.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
|
|
+import com.citu.module.system.enums.logger.LoginLogTypeEnum;
|
|
|
+import com.citu.module.system.enums.logger.LoginResultEnum;
|
|
|
+import com.citu.module.system.enums.oauth2.OAuth2ClientConstants;
|
|
|
+import com.citu.module.system.enums.sms.SmsSceneEnum;
|
|
|
+import com.citu.module.system.enums.social.SocialTypeEnum;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+import static com.citu.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
+import static com.citu.framework.common.util.servlet.ServletUtils.getClientIP;
|
|
|
+import static com.citu.framework.web.core.util.WebFrameworkUtils.getTerminal;
|
|
|
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.*;
|
|
|
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
|
|
|
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.AUTH_LOGIN_USER_DISABLED;
|
|
|
+import static com.citu.module.system.enums.ErrorCodeConstants.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * menduner的认证 Service 接口 实现
|
|
|
+ *
|
|
|
+ * @author rayson
|
|
|
+ **/
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class MdeAuthServiceImpl implements MdeAuthService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private MdeUserService userService;
|
|
|
+ @Resource
|
|
|
+ private SmsCodeApi smsCodeApi;
|
|
|
+ @Resource
|
|
|
+ private LoginLogApi loginLogApi;
|
|
|
+ @Resource
|
|
|
+ private SocialUserApi socialUserApi;
|
|
|
+ @Resource
|
|
|
+ private SocialClientApi socialClientApi;
|
|
|
+ @Resource
|
|
|
+ private OAuth2TokenApi oauth2TokenApi;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MdeAuthLoginRespVO login(MdeAuthLoginReqVO reqVO) {
|
|
|
+ // 使用手机 + 密码,进行登录。
|
|
|
+ MdeUserDO user = check(reqVO.getMobile(), reqVO.getPassword());
|
|
|
+
|
|
|
+ // 如果 socialType 非空,说明需要绑定社交用户
|
|
|
+ String openid = null;
|
|
|
+ if (reqVO.getSocialType() != null) {
|
|
|
+ openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
|
|
+ reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())).getCheckedData();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 Token 令牌,记录登录日志
|
|
|
+ return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, openid);
|
|
|
+ }
|
|
|
+
|
|
|
+ private MdeUserDO check(String mobile, String password) {
|
|
|
+ final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE;
|
|
|
+ // 校验账号是否存在
|
|
|
+ MdeUserDO user = userService.getUserByPhone(mobile);
|
|
|
+ if (user == null) {
|
|
|
+ createLoginLog(null, mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
|
|
|
+ throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
|
|
+ }
|
|
|
+ if (!userService.isPasswordMatch(password, user.getPassword())) {
|
|
|
+ createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
|
|
|
+ throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
|
|
+ }
|
|
|
+ // 校验是否禁用
|
|
|
+ if (ObjectUtil.notEqual(user.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
|
|
+ createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED);
|
|
|
+ throw exception(AUTH_LOGIN_USER_DISABLED);
|
|
|
+ }
|
|
|
+ return user;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void logout(String token) {
|
|
|
+ // 删除访问令牌
|
|
|
+ OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token).getCheckedData();
|
|
|
+ if (accessTokenRespDTO == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 删除成功,则记录登出日志
|
|
|
+ createLogoutLog(accessTokenRespDTO.getUserId());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void createLogoutLog(Long userId) {
|
|
|
+ LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
|
|
+ reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType());
|
|
|
+ reqDTO.setTraceId(TracerUtils.getTraceId());
|
|
|
+ reqDTO.setUserId(userId);
|
|
|
+ reqDTO.setUserType(getUserType().getValue());
|
|
|
+ reqDTO.setUsername(getPhone(userId));
|
|
|
+ reqDTO.setUserAgent(ServletUtils.getUserAgent());
|
|
|
+ reqDTO.setUserIp(getClientIP());
|
|
|
+ reqDTO.setResult(LoginResultEnum.SUCCESS.getResult());
|
|
|
+ loginLogApi.createLoginLog(reqDTO);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public MdeAuthLoginRespVO smsLogin(MdeAuthSmsLoginReqVO reqVO) {
|
|
|
+ // 校验验证码
|
|
|
+ String userIp = getClientIP();
|
|
|
+ smsCodeApi.useSmsCode(MdeAuthConvert.INSTANCE.convert(reqVO, MdeSmsSceneEnum.MENDUNER_LOGIN.getScene(), userIp)).getCheckedData();
|
|
|
+
|
|
|
+ // 获得获得注册用户
|
|
|
+ MdeUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, getTerminal());
|
|
|
+ Assert.notNull(user, "获取用户失败,结果为空");
|
|
|
+
|
|
|
+ // 如果 socialType 非空,说明需要绑定社交用户
|
|
|
+ String openid = null;
|
|
|
+ if (reqVO.getSocialType() != null) {
|
|
|
+ openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
|
|
+ reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())).getCheckedData();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 Token 令牌,记录登录日志
|
|
|
+ return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS, openid);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public MdeAuthLoginRespVO socialLogin(MdeAuthSocialLoginReqVO reqVO) {
|
|
|
+ // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
|
|
+ SocialUserRespDTO socialUser = socialUserApi.getSocialUserByCode(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
|
|
|
+ reqVO.getCode(), reqVO.getState()).getCheckedData();
|
|
|
+ if (socialUser == null) {
|
|
|
+ throw exception(AUTH_SOCIAL_USER_NOT_FOUND);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 情况一:已绑定,直接读取用户信息
|
|
|
+ MdeUserDO user;
|
|
|
+ if (socialUser.getUserId() != null) {
|
|
|
+ user = userService.getMdeUser(socialUser.getUserId());
|
|
|
+ // 情况二:未绑定,注册用户 + 绑定用户
|
|
|
+ } else {
|
|
|
+ user = userService.createUser(socialUser.getAvatar(), getClientIP(), getTerminal());
|
|
|
+ socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
|
|
+ reqVO.getType(), reqVO.getCode(), reqVO.getState()));
|
|
|
+ }
|
|
|
+ if (user == null) {
|
|
|
+ throw exception(USER_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 Token 令牌,记录登录日志
|
|
|
+ return createTokenAfterLoginSuccess(user, user.getPhone(), LoginLogTypeEnum.LOGIN_SOCIAL, socialUser.getOpenid());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public MdeAuthLoginRespVO weixinMiniAppLogin(MdeAuthWeixinMiniAppLoginReqVO reqVO) {
|
|
|
+ // 获得对应的手机号信息
|
|
|
+ SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
|
|
|
+ UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode()).getCheckedData();
|
|
|
+ Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
|
|
|
+
|
|
|
+ // 获得获得注册用户
|
|
|
+ MdeUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(),
|
|
|
+ getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal());
|
|
|
+ Assert.notNull(user, "获取用户失败,结果为空");
|
|
|
+
|
|
|
+ // 绑定社交用户
|
|
|
+ String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
|
|
+ SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), reqVO.getState())).getCheckedData();
|
|
|
+
|
|
|
+ // 创建 Token 令牌,记录登录日志
|
|
|
+ return createTokenAfterLoginSuccess(user, user.getPhone(), LoginLogTypeEnum.LOGIN_SOCIAL, openid);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getSocialAuthorizeUrl(Integer type, String redirectUri) {
|
|
|
+ return socialClientApi.getAuthorizeUrl(type, UserTypeEnum.MEMBER.getValue(), redirectUri).getCheckedData();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void sendSmsCode(Long userId, MdeAuthSmsSendReqVO reqVO) {
|
|
|
+ // 情况 1:如果是修改手机场景,需要校验新手机号是否已经注册,说明不能使用该手机了
|
|
|
+ if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene())) {
|
|
|
+ MdeUserDO user = userService.getUserByPhone(reqVO.getMobile());
|
|
|
+ if (user != null && !Objects.equals(user.getId(), userId)) {
|
|
|
+ throw exception(AUTH_MOBILE_USED);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 情况 2:如果是重置密码场景,需要校验手机号是存在的
|
|
|
+ if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_RESET_PASSWORD.getScene())) {
|
|
|
+ MdeUserDO user = userService.getUserByPhone(reqVO.getMobile());
|
|
|
+ if (user == null) {
|
|
|
+ throw exception(MDE_USER_MOBILE_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 情况 3:如果是修改密码场景,需要查询手机号,无需前端传递
|
|
|
+ if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene())) {
|
|
|
+ MdeUserDO user = userService.getMdeUser(userId);
|
|
|
+ // TODO 芋艿:后续 member user 手机非强绑定,这块需要做下调整;
|
|
|
+ reqVO.setMobile(user.getPhone());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行发送
|
|
|
+ smsCodeApi.sendSmsCode(MdeAuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void validateSmsCode(Long userId, MdeAuthSmsValidateReqVO reqVO) {
|
|
|
+ smsCodeApi.validateSmsCode(MdeAuthConvert.INSTANCE.convert(reqVO));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MdeAuthLoginRespVO refreshToken(String refreshToken) {
|
|
|
+ OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken,
|
|
|
+ OAuth2ClientConstants.CLIENT_ID_DEFAULT).getCheckedData();
|
|
|
+ return MdeAuthConvert.INSTANCE.convert(accessTokenDO, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getPhone(Long userId) {
|
|
|
+ if (userId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ MdeUserDO user = userService.getMdeUser(userId);
|
|
|
+ return user != null ? user.getPhone() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) {
|
|
|
+ // 插入登录日志
|
|
|
+ LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
|
|
+ reqDTO.setLogType(logType.getType());
|
|
|
+ reqDTO.setTraceId(TracerUtils.getTraceId());
|
|
|
+ reqDTO.setUserId(userId);
|
|
|
+ reqDTO.setUserType(getUserType().getValue());
|
|
|
+ reqDTO.setUsername(mobile);
|
|
|
+ reqDTO.setUserAgent(ServletUtils.getUserAgent());
|
|
|
+ reqDTO.setUserIp(getClientIP());
|
|
|
+ reqDTO.setResult(loginResult.getResult());
|
|
|
+ loginLogApi.createLoginLog(reqDTO);
|
|
|
+ // 更新最后登录时间
|
|
|
+ if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) {
|
|
|
+ userService.updateUserLogin(userId, getClientIP());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private UserTypeEnum getUserType() {
|
|
|
+ return UserTypeEnum.MEMBER;
|
|
|
+ }
|
|
|
+
|
|
|
+ private MdeAuthLoginRespVO createTokenAfterLoginSuccess(MdeUserDO user, String phone,
|
|
|
+ LoginLogTypeEnum logType, String openid) {
|
|
|
+ // 插入登陆日志
|
|
|
+ createLoginLog(user.getId(), phone, logType, LoginResultEnum.SUCCESS);
|
|
|
+ // 创建 Token 令牌
|
|
|
+ OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
|
|
|
+ .setUserId(user.getId()).setUserType(getUserType().getValue())
|
|
|
+ .setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT)).getCheckedData();
|
|
|
+ // 构建返回结果
|
|
|
+ return MdeAuthConvert.INSTANCE.convert(accessTokenRespDTO, openid);
|
|
|
+ }
|
|
|
+}
|