mirror of https://gitee.com/topiam/eiam
⚡ 个人中心
parent
890cb5c3f8
commit
0b2840e1f2
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.controller.user;
|
||||
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import cn.topiam.employee.audit.annotation.Audit;
|
||||
import cn.topiam.employee.audit.event.type.EventType;
|
||||
import cn.topiam.employee.console.pojo.update.user.*;
|
||||
import cn.topiam.employee.console.service.user.UserProfileService;
|
||||
import cn.topiam.employee.support.result.ApiRestResult;
|
||||
import cn.topiam.employee.support.web.decrypt.DecryptRequestBody;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import static cn.topiam.employee.common.constant.UserConstants.*;
|
||||
import static cn.topiam.employee.support.constant.EiamConstants.V1_API_PATH;
|
||||
|
||||
/**
|
||||
* 账户管理
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/12 21:39
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = V1_API_PATH + "/user/profile")
|
||||
public class UserProfileController {
|
||||
|
||||
/**
|
||||
* 修改账户信息
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Audit(type = EventType.MODIFY_ACCOUNT_INFO_PORTAL)
|
||||
@Operation(summary = "修改账户信息")
|
||||
@PutMapping("/change_info")
|
||||
public ApiRestResult<Boolean> changeInfo(@DecryptRequestBody @RequestBody @Validated UpdateUserInfoRequest param) {
|
||||
Boolean result = userProfileService.changeInfo(param);
|
||||
return ApiRestResult.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备修改密码
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Audit(type = EventType.PREPARE_MODIFY_PASSWORD)
|
||||
@Operation(summary = "准备修改账户密码")
|
||||
@PostMapping("/prepare_change_password")
|
||||
public ApiRestResult<Boolean> prepareChangePassword(@DecryptRequestBody @RequestBody @Validated PrepareChangePasswordRequest param) {
|
||||
return ApiRestResult.ok(userProfileService.prepareChangePassword(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Audit(type = EventType.MODIFY_USER_PASSWORD_PORTAL)
|
||||
@Operation(summary = "修改账户密码")
|
||||
@PutMapping("/change_password")
|
||||
public ApiRestResult<Boolean> changePassword(@DecryptRequestBody @RequestBody @Validated ChangePasswordRequest param) {
|
||||
return ApiRestResult.ok(userProfileService.changePassword(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备修改手机
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Audit(type = EventType.PREPARE_MODIFY_PHONE)
|
||||
@Operation(summary = "准备修改手机")
|
||||
@PostMapping("/prepare_change_phone")
|
||||
public ApiRestResult<Boolean> prepareChangePhone(@DecryptRequestBody @RequestBody @Validated PrepareChangePhoneRequest param) {
|
||||
return ApiRestResult.ok(userProfileService.prepareChangePhone(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改手机
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Audit(type = EventType.MODIFY_USER_PHONE_PORTAL)
|
||||
@Operation(summary = "修改手机")
|
||||
@PutMapping("/change_phone")
|
||||
public ApiRestResult<Boolean> changePhone(@RequestBody @Validated ChangePhoneRequest param) {
|
||||
return ApiRestResult.ok(userProfileService.changePhone(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备修改邮箱
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Audit(type = EventType.PREPARE_MODIFY_EMAIL)
|
||||
@Operation(summary = "准备修改邮箱")
|
||||
@PostMapping("/prepare_change_email")
|
||||
public ApiRestResult<Boolean> prepareChangeEmail(@DecryptRequestBody @RequestBody @Validated PrepareChangeEmailRequest param) {
|
||||
return ApiRestResult.ok(userProfileService.prepareChangeEmail(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改邮箱
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Audit(type = EventType.MODIFY_USER_EMAIL_PORTAL)
|
||||
@Operation(summary = "修改邮箱")
|
||||
@PutMapping("/change_email")
|
||||
public ApiRestResult<Boolean> changeEmail(@RequestBody @Validated ChangeEmailRequest param) {
|
||||
return ApiRestResult.ok(userProfileService.changeEmail(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码发送验证码
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Operation(summary = "忘记密码发送验证码")
|
||||
@GetMapping(FORGET_PASSWORD_CODE)
|
||||
public ApiRestResult<Boolean> forgetPasswordCode(@Parameter(description = "验证码接收者(邮箱/手机号)") @RequestParam String recipient) {
|
||||
return ApiRestResult.ok(userProfileService.forgetPasswordCode(recipient));
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码预认证
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Operation(summary = "忘记密码预认证")
|
||||
@PostMapping(PREPARE_FORGET_PASSWORD)
|
||||
public ApiRestResult<Boolean> prepareForgetPassword(@DecryptRequestBody @RequestBody @Validated PrepareForgetPasswordRequest param) {
|
||||
return ApiRestResult
|
||||
.ok(userProfileService.prepareForgetPassword(param.getRecipient(), param.getCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记密码
|
||||
*
|
||||
* @return {@link ApiRestResult}
|
||||
*/
|
||||
@Operation(summary = "忘记密码")
|
||||
@PutMapping(FORGET_PASSWORD)
|
||||
public ApiRestResult<Boolean> forgetPassword(@DecryptRequestBody @RequestBody @Validated ForgetPasswordRequest forgetPasswordRequest) {
|
||||
return ApiRestResult.ok(userProfileService.forgetPassword(forgetPasswordRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户资料service
|
||||
*/
|
||||
private final UserProfileService userProfileService;
|
||||
|
||||
public UserProfileController(UserProfileService userProfileService) {
|
||||
this.userProfileService = userProfileService;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.converter.user;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserDetailEntity;
|
||||
import cn.topiam.employee.common.entity.account.UserEntity;
|
||||
import cn.topiam.employee.console.pojo.update.user.UpdateUserInfoRequest;
|
||||
|
||||
/**
|
||||
* AccountConverter
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/3/25 21:52
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface UserProfileConverter {
|
||||
|
||||
/**
|
||||
* 用户更新参数转换为用户实体类
|
||||
*
|
||||
* @param param {@link UpdateUserInfoRequest} 更新参数
|
||||
* @return {@link UserEntity} 用户实体
|
||||
*/
|
||||
@Mapping(target = "passwordPlainText", ignore = true)
|
||||
@Mapping(target = "deleted", ignore = true)
|
||||
@Mapping(target = "identitySourceId", ignore = true)
|
||||
@Mapping(target = "phoneVerified", ignore = true)
|
||||
@Mapping(target = "phoneAreaCode", ignore = true)
|
||||
@Mapping(target = "username", ignore = true)
|
||||
@Mapping(target = "updateTime", ignore = true)
|
||||
@Mapping(target = "updateBy", ignore = true)
|
||||
@Mapping(target = "status", ignore = true)
|
||||
@Mapping(target = "remark", ignore = true)
|
||||
@Mapping(target = "phone", ignore = true)
|
||||
@Mapping(target = "password", ignore = true)
|
||||
@Mapping(target = "lastUpdatePasswordTime", ignore = true)
|
||||
@Mapping(target = "lastAuthTime", ignore = true)
|
||||
@Mapping(target = "lastAuthIp", ignore = true)
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "externalId", ignore = true)
|
||||
@Mapping(target = "expireDate", ignore = true)
|
||||
@Mapping(target = "expand", ignore = true)
|
||||
@Mapping(target = "emailVerified", ignore = true)
|
||||
@Mapping(target = "email", ignore = true)
|
||||
@Mapping(target = "dataOrigin", ignore = true)
|
||||
@Mapping(target = "createTime", ignore = true)
|
||||
@Mapping(target = "createBy", ignore = true)
|
||||
@Mapping(target = "authTotal", ignore = true)
|
||||
UserEntity userUpdateParamConvertToUserEntity(UpdateUserInfoRequest param);
|
||||
|
||||
/**
|
||||
* 用户详情修改入参转换用户详情实体
|
||||
*
|
||||
* @param param {@link UpdateUserInfoRequest}
|
||||
* @return {@link UserDetailEntity}
|
||||
*/
|
||||
@Mapping(target = "deleted", ignore = true)
|
||||
@Mapping(target = "website", ignore = true)
|
||||
@Mapping(target = "idType", ignore = true)
|
||||
@Mapping(target = "userId", ignore = true)
|
||||
@Mapping(target = "remark", ignore = true)
|
||||
@Mapping(target = "idCard", ignore = true)
|
||||
@Mapping(target = "address", ignore = true)
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "updateTime", ignore = true)
|
||||
@Mapping(target = "updateBy", ignore = true)
|
||||
@Mapping(target = "createTime", ignore = true)
|
||||
@Mapping(target = "createBy", ignore = true)
|
||||
UserDetailEntity userUpdateParamConvertToUserDetailsEntity(UpdateUserInfoRequest param);
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.service.user;
|
||||
|
||||
import cn.topiam.employee.console.pojo.update.user.*;
|
||||
|
||||
/**
|
||||
* 账户服务
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/4/3 22:20
|
||||
*/
|
||||
public interface UserProfileService {
|
||||
|
||||
/**
|
||||
* 更新用户
|
||||
*
|
||||
* @param param {@link UpdateUserInfoRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean changeInfo(UpdateUserInfoRequest param);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*
|
||||
* @param param {@link ChangePasswordRequest}
|
||||
* @return Boolean
|
||||
*/
|
||||
Boolean changePassword(ChangePasswordRequest param);
|
||||
|
||||
/**
|
||||
* 修改手机
|
||||
*
|
||||
* @param param {@link PrepareChangePhoneRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean prepareChangePhone(PrepareChangePhoneRequest param);
|
||||
|
||||
/**
|
||||
* 修改手机
|
||||
*
|
||||
* @param param {@link ChangePhoneRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean changePhone(ChangePhoneRequest param);
|
||||
|
||||
/**
|
||||
* 准备修改邮箱
|
||||
*
|
||||
* @param param {@link PrepareChangeEmailRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean prepareChangeEmail(PrepareChangeEmailRequest param);
|
||||
|
||||
/**
|
||||
* 修改邮箱
|
||||
*
|
||||
* @param param {@link ChangeEmailRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean changeEmail(ChangeEmailRequest param);
|
||||
|
||||
/**
|
||||
* 修改密码预认证
|
||||
*
|
||||
* @param param {@link PrepareChangePasswordRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean prepareChangePassword(PrepareChangePasswordRequest param);
|
||||
|
||||
/**
|
||||
* 忘记密码发送验证码
|
||||
*
|
||||
* @param recipient {@link String} 验证码接收者(邮箱/手机号)
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean forgetPasswordCode(String recipient);
|
||||
|
||||
/**
|
||||
* 忘记密码预认证
|
||||
*
|
||||
* @param recipient {@link String} 验证码接收者(邮箱/手机号)
|
||||
* @param code {@link String} 验证码
|
||||
* @return {@link Boolean} 忘记密码 Token
|
||||
*/
|
||||
Boolean prepareForgetPassword(String recipient, String code);
|
||||
|
||||
/**
|
||||
* 忘记密码
|
||||
*
|
||||
* @param forgetPasswordRequest {@link ForgetPasswordRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean forgetPassword(ForgetPasswordRequest forgetPasswordRequest);
|
||||
|
||||
}
|
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.service.user.impl;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserDetailEntity;
|
||||
import cn.topiam.employee.common.entity.account.UserEntity;
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.enums.MessageNoticeChannel;
|
||||
import cn.topiam.employee.common.enums.SmsType;
|
||||
import cn.topiam.employee.common.exception.PasswordValidatedFailException;
|
||||
import cn.topiam.employee.common.exception.UserNotFoundException;
|
||||
import cn.topiam.employee.common.repository.account.UserDetailRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserIdpRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||
import cn.topiam.employee.common.repository.authentication.IdentityProviderRepository;
|
||||
import cn.topiam.employee.console.converter.user.UserProfileConverter;
|
||||
import cn.topiam.employee.console.pojo.update.user.*;
|
||||
import cn.topiam.employee.console.service.user.UserProfileService;
|
||||
import cn.topiam.employee.core.message.sms.SmsMsgEventPublish;
|
||||
import cn.topiam.employee.core.security.otp.OtpContextHelp;
|
||||
import cn.topiam.employee.support.context.ServletContextHelp;
|
||||
import cn.topiam.employee.support.exception.BadParamsException;
|
||||
import cn.topiam.employee.support.exception.InfoValidityFailException;
|
||||
import cn.topiam.employee.support.exception.TopIamException;
|
||||
import cn.topiam.employee.support.security.password.PasswordPolicyManager;
|
||||
import cn.topiam.employee.support.security.util.SecurityUtils;
|
||||
import cn.topiam.employee.support.util.BeanUtils;
|
||||
import cn.topiam.employee.support.util.PhoneNumberUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import static cn.topiam.employee.core.message.sms.SmsMsgEventPublish.USERNAME;
|
||||
import static cn.topiam.employee.support.constant.EiamConstants.FORGET_PASSWORD_TOKEN_ID;
|
||||
import static cn.topiam.employee.support.exception.enums.ExceptionStatus.EX000102;
|
||||
import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_BY;
|
||||
import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_TIME;
|
||||
import static cn.topiam.employee.support.util.EmailUtils.isEmailValidate;
|
||||
import static cn.topiam.employee.support.util.PhoneNumberUtils.isPhoneValidate;
|
||||
|
||||
/**
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/4/3 22:20
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UserProfileServiceImpl implements UserProfileService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(UserProfileServiceImpl.class);
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean changeInfo(UpdateUserInfoRequest param) {
|
||||
//用户信息
|
||||
UserEntity toUserEntity = userProfileConverter.userUpdateParamConvertToUserEntity(param);
|
||||
UserEntity user = userRepository
|
||||
.findById(Long.valueOf(SecurityUtils.getCurrentUser().getId())).orElseThrow();
|
||||
BeanUtils.merge(toUserEntity, user, LAST_MODIFIED_BY, LAST_MODIFIED_TIME);
|
||||
userRepository.save(user);
|
||||
//用户详情
|
||||
String currentUserId = SecurityUtils.getCurrentUserId();
|
||||
UserDetailEntity detail = userDetailsRepository.findByUserId(Long.valueOf(currentUserId))
|
||||
.orElse(new UserDetailEntity().setUserId(user.getId()));
|
||||
UserDetailEntity toUserDetailsEntity = userProfileConverter
|
||||
.userUpdateParamConvertToUserDetailsEntity(param);
|
||||
toUserDetailsEntity.setId(detail.getId());
|
||||
BeanUtils.merge(toUserDetailsEntity, detail, LAST_MODIFIED_BY, LAST_MODIFIED_TIME);
|
||||
userDetailsRepository.save(detail);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean changePassword(ChangePasswordRequest param) {
|
||||
//获取用户
|
||||
UserEntity user = getCurrentUser();
|
||||
Boolean checkOtp = otpContextHelp.checkOtp(
|
||||
MessageNoticeChannel.SMS == param.getChannel() ? SmsType.UPDATE_PASSWORD.getCode()
|
||||
: MailType.UPDATE_PASSWORD.getCode(),
|
||||
param.getChannel(),
|
||||
MessageNoticeChannel.SMS == param.getChannel() ? user.getPhone() : user.getEmail(),
|
||||
param.getVerifyCode());
|
||||
if (!checkOtp) {
|
||||
throw new InfoValidityFailException(EX000102.getMessage());
|
||||
}
|
||||
// 校验密码
|
||||
passwordPolicyManager.validate(user, param.getNewPassword());
|
||||
//修改密码
|
||||
userRepository.updateUserPassword(Long.valueOf(SecurityUtils.getCurrentUser().getId()),
|
||||
passwordEncoder.encode(param.getNewPassword()), LocalDateTime.now());
|
||||
logger.info("用户ID: [{}] 用户名: [{}] 修改密码成功", user.getId(), user.getUsername());
|
||||
//异步下线所有用户
|
||||
removeSession(SecurityUtils.getCurrentUserName());
|
||||
//@formatter:on
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean prepareChangePhone(PrepareChangePhoneRequest param) {
|
||||
UserEntity user = validatedPassword(param.getPassword());
|
||||
// 发送短信验证码
|
||||
if (StringUtils.isNotBlank(user.getPhone())) {
|
||||
otpContextHelp.sendOtp(param.getPhone(), SmsType.UPDATE_PHONE.getCode(),
|
||||
MessageNoticeChannel.SMS);
|
||||
} else {
|
||||
otpContextHelp.sendOtp(param.getPhone(), SmsType.BIND_PHONE.getCode(),
|
||||
MessageNoticeChannel.SMS);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改手机
|
||||
*
|
||||
* @param param {@link ChangePhoneRequest}
|
||||
* @return Boolean
|
||||
*/
|
||||
@Override
|
||||
public Boolean changePhone(ChangePhoneRequest param) {
|
||||
UserEntity user = getCurrentUser();
|
||||
Boolean checkOtp;
|
||||
if (StringUtils.isNotBlank(user.getPhone())) {
|
||||
checkOtp = otpContextHelp.checkOtp(SmsType.UPDATE_PHONE.getCode(),
|
||||
MessageNoticeChannel.SMS, param.getPhone(), param.getOtp());
|
||||
} else {
|
||||
checkOtp = otpContextHelp.checkOtp(SmsType.BIND_PHONE.getCode(),
|
||||
MessageNoticeChannel.SMS, param.getPhone(), param.getOtp());
|
||||
}
|
||||
if (!checkOtp) {
|
||||
throw new InfoValidityFailException(EX000102.getMessage());
|
||||
}
|
||||
// 校验是否已经存在
|
||||
UserEntity byEmail = userRepository.findByPhone(param.getPhone());
|
||||
if (Objects.nonNull(byEmail) && !user.getId().equals(byEmail.getId())) {
|
||||
throw new TopIamException("系统中已存在[" + param.getPhone() + "]手机号, 请先解绑");
|
||||
}
|
||||
Long id = Long.valueOf(SecurityUtils.getCurrentUser().getId());
|
||||
userRepository.updateUserPhone(id, param.getPhone());
|
||||
// 修改手机号成功发送短信
|
||||
LinkedHashMap<String, String> parameter = Maps.newLinkedHashMap();
|
||||
parameter.put(USERNAME, user.getUsername());
|
||||
smsMsgEventPublish.publish(SmsType.BIND_PHONE_SUCCESS, param.getPhone(), parameter);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备修改邮箱
|
||||
*
|
||||
* @param param {@link PrepareChangeEmailRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
@Override
|
||||
public Boolean prepareChangeEmail(PrepareChangeEmailRequest param) {
|
||||
UserEntity user = validatedPassword(param.getPassword());
|
||||
// 发送邮箱验证码
|
||||
if (StringUtils.isNotBlank(user.getEmail())) {
|
||||
otpContextHelp.sendOtp(param.getEmail(), MailType.UPDATE_BIND_MAIL.getCode(),
|
||||
MessageNoticeChannel.MAIL);
|
||||
} else {
|
||||
otpContextHelp.sendOtp(param.getEmail(), MailType.BIND_EMAIL.getCode(),
|
||||
MessageNoticeChannel.MAIL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改邮箱
|
||||
*
|
||||
* @param param {@link ChangeEmailRequest}
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean changeEmail(ChangeEmailRequest param) {
|
||||
UserEntity user = getCurrentUser();
|
||||
Boolean checkOtp;
|
||||
if (StringUtils.isNotBlank(user.getEmail())) {
|
||||
checkOtp = otpContextHelp.checkOtp(MailType.UPDATE_BIND_MAIL.getCode(),
|
||||
MessageNoticeChannel.MAIL, param.getEmail(), param.getOtp());
|
||||
} else {
|
||||
checkOtp = otpContextHelp.checkOtp(MailType.BIND_EMAIL.getCode(),
|
||||
MessageNoticeChannel.MAIL, param.getEmail(), param.getOtp());
|
||||
}
|
||||
if (!checkOtp) {
|
||||
throw new InfoValidityFailException(EX000102.getMessage());
|
||||
}
|
||||
// 校验是否已经存在
|
||||
UserEntity byEmail = userRepository.findByEmail(param.getEmail());
|
||||
if (Objects.nonNull(byEmail) && !user.getId().equals(byEmail.getId())) {
|
||||
throw new TopIamException("系统中已存在[" + param.getEmail() + "]邮箱, 请先解绑");
|
||||
}
|
||||
userRepository.updateUserEmail(Long.valueOf(SecurityUtils.getCurrentUser().getId()),
|
||||
param.getEmail());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean prepareChangePassword(PrepareChangePasswordRequest param) {
|
||||
UserEntity user = getCurrentUser();
|
||||
// 发送短信验证码
|
||||
if (MessageNoticeChannel.SMS == param.getChannel()) {
|
||||
otpContextHelp.sendOtp(user.getPhone(), SmsType.UPDATE_PASSWORD.getCode(),
|
||||
MessageNoticeChannel.SMS);
|
||||
} else {
|
||||
otpContextHelp.sendOtp(user.getEmail(), MailType.UPDATE_PASSWORD.getCode(),
|
||||
MessageNoticeChannel.MAIL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean forgetPasswordCode(String recipient) {
|
||||
if (isEmailValidate(recipient)) {
|
||||
// 验证在库中是否有邮箱
|
||||
Optional<UserEntity> byEmail = Optional
|
||||
.ofNullable(userRepository.findByEmail(recipient));
|
||||
if (byEmail.isPresent()) {
|
||||
otpContextHelp.sendOtp(recipient, MailType.FORGET_PASSWORD.getCode(),
|
||||
MessageNoticeChannel.MAIL);
|
||||
return true;
|
||||
}
|
||||
log.warn("忘记密码: : 邮箱: [{}] 不存在", recipient);
|
||||
} else if (isPhoneValidate(recipient)) {
|
||||
// 验证在库中是否有手机号
|
||||
Optional<UserEntity> byPhone = Optional
|
||||
.ofNullable(userRepository.findByPhone(PhoneNumberUtils.getPhoneNumber(recipient)));
|
||||
if (byPhone.isPresent()) {
|
||||
otpContextHelp.sendOtp(recipient, SmsType.FORGET_PASSWORD.getCode(),
|
||||
MessageNoticeChannel.SMS);
|
||||
return true;
|
||||
}
|
||||
log.warn("忘记密码: : 手机号: [{}] 不存在", recipient);
|
||||
}
|
||||
log.error("忘记密码: : 接受者: [{}] 格式错误", recipient);
|
||||
throw new BadParamsException("请输入正确的手机号或邮箱");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean prepareForgetPassword(String recipient, String code) {
|
||||
// 校验验证码
|
||||
Boolean checkOtp = false;
|
||||
Optional<UserEntity> user = Optional.empty();
|
||||
if (isEmailValidate(recipient)) {
|
||||
user = Optional.ofNullable(userRepository.findByEmail(recipient));
|
||||
if (user.isPresent()) {
|
||||
checkOtp = otpContextHelp.checkOtp(MailType.FORGET_PASSWORD.getCode(),
|
||||
MessageNoticeChannel.MAIL, recipient, code);
|
||||
}
|
||||
} else if (isPhoneValidate(recipient)) {
|
||||
user = Optional
|
||||
.ofNullable(userRepository.findByPhone(PhoneNumberUtils.getPhoneNumber(recipient)));
|
||||
if (user.isPresent()) {
|
||||
checkOtp = otpContextHelp.checkOtp(SmsType.FORGET_PASSWORD.getCode(),
|
||||
MessageNoticeChannel.SMS, recipient, code);
|
||||
}
|
||||
}
|
||||
if (!checkOtp) {
|
||||
throw new InfoValidityFailException(EX000102.getMessage());
|
||||
}
|
||||
|
||||
// 生成忘记密码TOKEN ID
|
||||
String tokenId = UUID.randomUUID().toString();
|
||||
HttpSession session = ServletContextHelp.getSession();
|
||||
// 保存用户ID到Redis, 有效期10分钟
|
||||
stringRedisTemplate.opsForValue().set(session.getId() + tokenId,
|
||||
String.valueOf(user.get().getId()), 10, TimeUnit.MINUTES);
|
||||
// 保存TOKEN ID到会话
|
||||
session.setAttribute(FORGET_PASSWORD_TOKEN_ID, tokenId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean forgetPassword(ForgetPasswordRequest forgetPasswordRequest) {
|
||||
// 验证TOKEN
|
||||
HttpSession session = ServletContextHelp.getSession();
|
||||
String redisTokenId = session.getId() + session.getAttribute(FORGET_PASSWORD_TOKEN_ID);
|
||||
String userId = stringRedisTemplate.opsForValue().get(redisTokenId);
|
||||
if (Objects.isNull(userId)) {
|
||||
// 清除tokenId
|
||||
session.removeAttribute(FORGET_PASSWORD_TOKEN_ID);
|
||||
return false;
|
||||
}
|
||||
//修改密码
|
||||
Optional<UserEntity> user = userRepository.findById(Long.valueOf(userId));
|
||||
if (user.isPresent()) {
|
||||
UserEntity userEntity = user.get();
|
||||
userRepository.updateUserPassword(userEntity.getId(),
|
||||
passwordEncoder.encode(forgetPasswordRequest.getNewPassword()),
|
||||
LocalDateTime.now());
|
||||
logger.info("忘记密码: 用户ID: [{}] 用户名: [{}] 修改密码成功", userEntity.getId(),
|
||||
userEntity.getUsername());
|
||||
removeSession(userEntity.getUsername());
|
||||
stringRedisTemplate.delete(redisTokenId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步下线所有用户
|
||||
*
|
||||
* @param username {@link String}
|
||||
*/
|
||||
private void removeSession(String username) {
|
||||
executor.execute(() -> {
|
||||
List<SessionInformation> sessions = sessionRegistry.getAllSessions(username, false);
|
||||
sessions.forEach(SessionInformation::expireNow);
|
||||
//@formatter:on
|
||||
});
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* 为保证安全,从数据库获取最新信息使用,并更新到当前上下文中
|
||||
*
|
||||
* @return {@link UserEntity}
|
||||
*/
|
||||
public UserEntity getCurrentUser() {
|
||||
String userId = SecurityUtils.getCurrentUserId();
|
||||
Optional<UserEntity> optional = userRepository.findById(Long.valueOf(userId));
|
||||
if (optional.isPresent()) {
|
||||
return optional.get();
|
||||
}
|
||||
SecurityContextHolder.clearContext();
|
||||
logger.error("根据用户ID: [{}] 未查询到用户信息", userId);
|
||||
throw new UserNotFoundException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
*
|
||||
* @param password {@link String}
|
||||
* @return {@link UserEntity}
|
||||
*/
|
||||
public UserEntity validatedPassword(String password) {
|
||||
UserEntity user = getCurrentUser();
|
||||
boolean matches = passwordEncoder.matches(password, user.getPassword());
|
||||
if (!matches) {
|
||||
logger.error("用户ID: [{}] 用户名: [{}] 密码匹配失败", user.getId(), user.getUsername());
|
||||
throw new PasswordValidatedFailException();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executor
|
||||
*/
|
||||
private final Executor executor;
|
||||
|
||||
/**
|
||||
* AccountConverter
|
||||
*/
|
||||
private final UserProfileConverter userProfileConverter;
|
||||
|
||||
/**
|
||||
* PasswordEncoder
|
||||
*/
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* UserRepository
|
||||
*/
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* 用户详情Repository
|
||||
*/
|
||||
private final UserDetailRepository userDetailsRepository;
|
||||
|
||||
/**
|
||||
* SessionRegistry
|
||||
*/
|
||||
private final SessionRegistry sessionRegistry;
|
||||
|
||||
/**
|
||||
* OtpContextHelp
|
||||
*/
|
||||
private final OtpContextHelp otpContextHelp;
|
||||
|
||||
/**
|
||||
* SmsMsgEventPublish
|
||||
*/
|
||||
private final SmsMsgEventPublish smsMsgEventPublish;
|
||||
|
||||
/**
|
||||
* StringRedisTemplate
|
||||
*/
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* PasswordPolicyManager
|
||||
*/
|
||||
private final PasswordPolicyManager<UserEntity> passwordPolicyManager;
|
||||
|
||||
/**
|
||||
* IdentityProviderRepository
|
||||
*/
|
||||
private final IdentityProviderRepository identityProviderRepository;
|
||||
|
||||
/**
|
||||
* UserAuthnBindRepository
|
||||
*/
|
||||
private final UserIdpRepository userIdpRepository;
|
||||
|
||||
public UserProfileServiceImpl(AsyncConfigurer asyncConfigurer,
|
||||
UserProfileConverter userProfileConverter,
|
||||
PasswordEncoder passwordEncoder, UserRepository userRepository,
|
||||
UserDetailRepository userDetailsRepository,
|
||||
SessionRegistry sessionRegistry, OtpContextHelp otpContextHelp,
|
||||
SmsMsgEventPublish smsMsgEventPublish,
|
||||
StringRedisTemplate stringRedisTemplate,
|
||||
PasswordPolicyManager<UserEntity> passwordPolicyManager,
|
||||
IdentityProviderRepository identityProviderRepository,
|
||||
UserIdpRepository userIdpRepository) {
|
||||
this.executor = asyncConfigurer.getAsyncExecutor();
|
||||
this.userProfileConverter = userProfileConverter;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userRepository = userRepository;
|
||||
this.userDetailsRepository = userDetailsRepository;
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
this.otpContextHelp = otpContextHelp;
|
||||
this.smsMsgEventPublish = smsMsgEventPublish;
|
||||
this.stringRedisTemplate = stringRedisTemplate;
|
||||
this.passwordPolicyManager = passwordPolicyManager;
|
||||
this.identityProviderRepository = identityProviderRepository;
|
||||
this.userIdpRepository = userIdpRepository;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue