【7.6.0】【sys】【auth】整理登录逻辑

pull/57/head
fengshuonan 2023-06-17 23:50:19 +08:00
parent 7a4393b2e3
commit 7e13c6ef03
2 changed files with 310 additions and 263 deletions

View File

@ -25,36 +25,23 @@
package cn.stylefeng.roses.kernel.auth.auth; package cn.stylefeng.roses.kernel.auth.auth;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES; import cn.hutool.crypto.symmetric.AES;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi; import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi;
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi; import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
import cn.stylefeng.roses.kernel.auth.api.SsoServerApi;
import cn.stylefeng.roses.kernel.auth.api.TempSecretApi;
import cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants;
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
import cn.stylefeng.roses.kernel.auth.api.context.AuthJwtContext; import cn.stylefeng.roses.kernel.auth.api.context.AuthJwtContext;
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext; import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
import cn.stylefeng.roses.kernel.auth.api.enums.SsoClientTypeEnum;
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException; import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
import cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum; import cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum;
import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander; import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander;
import cn.stylefeng.roses.kernel.auth.api.password.PasswordStoredEncryptApi;
import cn.stylefeng.roses.kernel.auth.api.password.PasswordTransferEncryptApi;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest; import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse; import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginWithTokenRequest; import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginWithTokenRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser; import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
import cn.stylefeng.roses.kernel.auth.api.pojo.payload.DefaultJwtPayload; import cn.stylefeng.roses.kernel.auth.api.pojo.payload.DefaultJwtPayload;
import cn.stylefeng.roses.kernel.auth.api.pojo.sso.SsoLoginCodeRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.sso.SsoProperties;
import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi; import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi;
import cn.stylefeng.roses.kernel.demo.expander.DemoConfigExpander; import cn.stylefeng.roses.kernel.demo.expander.DemoConfigExpander;
import cn.stylefeng.roses.kernel.dsctn.api.constants.DatasourceContainerConstants; import cn.stylefeng.roses.kernel.dsctn.api.constants.DatasourceContainerConstants;
@ -66,18 +53,8 @@ import cn.stylefeng.roses.kernel.jwt.api.exception.enums.JwtExceptionEnum;
import cn.stylefeng.roses.kernel.jwt.api.pojo.config.JwtConfig; import cn.stylefeng.roses.kernel.jwt.api.pojo.config.JwtConfig;
import cn.stylefeng.roses.kernel.log.api.LoginLogServiceApi; import cn.stylefeng.roses.kernel.log.api.LoginLogServiceApi;
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants; import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
import cn.stylefeng.roses.kernel.rule.tenant.RequestTenantCodeHolder;
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
import cn.stylefeng.roses.kernel.scanner.api.exception.ScannerException;
import cn.stylefeng.roses.kernel.scanner.api.exception.enums.ScannerExceptionEnum;
import cn.stylefeng.roses.kernel.scanner.api.holder.InitScanFlagHolder;
import cn.stylefeng.roses.kernel.security.api.DragCaptchaApi;
import cn.stylefeng.roses.kernel.security.api.ImageCaptchaApi;
import cn.stylefeng.roses.kernel.security.api.expander.SecurityConfigExpander;
import cn.stylefeng.roses.kernel.sys.api.SysUserServiceApi; import cn.stylefeng.roses.kernel.sys.api.SysUserServiceApi;
import cn.stylefeng.roses.kernel.sys.api.enums.user.UserStatusEnum;
import cn.stylefeng.roses.kernel.sys.api.pojo.user.UserValidateDTO; import cn.stylefeng.roses.kernel.sys.api.pojo.user.UserValidateDTO;
import cn.stylefeng.roses.kernel.validator.api.exception.enums.ValidatorExceptionEnum;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
@ -103,24 +80,9 @@ public class AuthServiceImpl implements AuthServiceApi {
@Resource @Resource
private SessionManagerApi sessionManagerApi; private SessionManagerApi sessionManagerApi;
@Resource
private PasswordStoredEncryptApi passwordStoredEncryptApi;
@Resource
private PasswordTransferEncryptApi passwordTransferEncryptApi;
@Resource @Resource
private LoginLogServiceApi loginLogServiceApi; private LoginLogServiceApi loginLogServiceApi;
@Resource
private ImageCaptchaApi captchaApi;
@Resource
private DragCaptchaApi dragCaptchaApi;
@Resource
private SsoProperties ssoProperties;
@Resource(name = "loginErrorCountCacheApi") @Resource(name = "loginErrorCountCacheApi")
private CacheOperatorApi<Integer> loginErrorCountCacheApi; private CacheOperatorApi<Integer> loginErrorCountCacheApi;
@ -130,23 +92,26 @@ public class AuthServiceImpl implements AuthServiceApi {
@Resource @Resource
private JwtApi jwtApi; private JwtApi jwtApi;
@Resource
private LoginService loginService;
@Override @Override
public LoginResponse login(LoginRequest loginRequest) { public LoginResponse login(LoginRequest loginRequest) {
return loginAction(loginRequest, true, null); return loginService.loginAction(loginRequest, true, null);
} }
@Override @Override
public LoginResponse loginWithUserName(String username) { public LoginResponse loginWithUserName(String username) {
LoginRequest loginRequest = new LoginRequest(); LoginRequest loginRequest = new LoginRequest();
loginRequest.setAccount(username); loginRequest.setAccount(username);
return loginAction(loginRequest, false, null); return loginService.loginAction(loginRequest, false, null);
} }
@Override @Override
public LoginResponse loginWithUserNameAndCaToken(String username, String caToken) { public LoginResponse loginWithUserNameAndCaToken(String username, String caToken) {
LoginRequest loginRequest = new LoginRequest(); LoginRequest loginRequest = new LoginRequest();
loginRequest.setAccount(username); loginRequest.setAccount(username);
return loginAction(loginRequest, false, caToken); return loginService.loginAction(loginRequest, false, caToken);
} }
@Override @Override
@ -265,7 +230,6 @@ public class AuthServiceImpl implements AuthServiceApi {
// 2. 校验用户token是否正确校验失败会抛出异常 // 2. 校验用户token是否正确校验失败会抛出异常
this.validateToken(token); this.validateToken(token);
} }
@Override @Override
@ -305,225 +269,4 @@ public class AuthServiceImpl implements AuthServiceApi {
return loginUser; return loginUser;
} }
/**
*
*
* @param loginRequest
* @param validatePassword true-false-
* @param caToken token32uuid
* @author fengshuonan
* @since 2020/10/21 16:59
*/
private LoginResponse loginAction(LoginRequest loginRequest, Boolean validatePassword, String caToken) {
// 1.参数为空校验
if (validatePassword) {
if (loginRequest == null || StrUtil.hasBlank(loginRequest.getAccount(), loginRequest.getPassword())) {
throw new AuthException(AuthExceptionEnum.PARAM_EMPTY);
}
} else {
if (loginRequest == null || StrUtil.hasBlank(loginRequest.getAccount())) {
throw new AuthException(AuthExceptionEnum.ACCOUNT_IS_BLANK);
}
}
// 1.2 判断账号是否密码重试次数过多被冻结
Integer loginErrorCount = loginErrorCountCacheApi.get(loginRequest.getAccount());
if (loginErrorCount != null && loginErrorCount >= LoginCacheConstants.MAX_ERROR_LOGIN_COUNT) {
throw new AuthException(AuthExceptionEnum.LOGIN_LOCKED);
}
// 1.3 暂存多租户编码v7.3.2增加,方便缓存调用过程中获取多租户的前缀)
RequestTenantCodeHolder.setTenantCode(loginRequest.getTenantCode());
// 2. 如果开启了验证码校验,则验证当前请求的验证码是否正确
if (SecurityConfigExpander.getCaptchaOpen()) {
String verKey = loginRequest.getVerKey();
String verCode = loginRequest.getVerCode();
if (StrUtil.isEmpty(verKey) || StrUtil.isEmpty(verCode)) {
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_EMPTY);
}
if (!captchaApi.validateCaptcha(verKey, verCode)) {
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_ERROR);
}
}
// 2.1 验证拖拽验证码
if (SecurityConfigExpander.getDragCaptchaOpen()) {
String verKey = loginRequest.getVerKey();
String verXLocationValue = loginRequest.getVerCode();
if (StrUtil.isEmpty(verKey) || StrUtil.isEmpty(verXLocationValue)) {
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_EMPTY);
}
if (!dragCaptchaApi.validateCaptcha(verKey, Convert.toInt(verXLocationValue))) {
throw new AuthException(ValidatorExceptionEnum.DRAG_CAPTCHA_ERROR);
}
}
// 2.2 校验当前系统是否初始化资源完成,如果资源还没有初始化,提示用户请等一下再登录
if (!InitScanFlagHolder.getFlag()) {
throw new ScannerException(ScannerExceptionEnum.SYSTEM_RESOURCE_URL_NOT_INIT);
}
// 3. 解密密码的密文需要sys_config相关配置打开
if (loginRequest.getPassword() != null && AuthConfigExpander.getPasswordRsaValidateFlag()) {
String decryptPassword = passwordTransferEncryptApi.decrypt(loginRequest.getPassword());
loginRequest.setPassword(decryptPassword);
}
// 4. 如果开启了单点登录并且CaToken没有值走单点登录获取loginCode
if (ssoProperties.getOpenFlag() && StrUtil.isEmpty(caToken)) {
if (SsoClientTypeEnum.client.name().equals(ssoProperties.getSsoClientType())) {
// 调用单点的接口获取loginCode远程接口校验用户级密码正确性。
String remoteLoginCode = getRemoteLoginCode(loginRequest);
return new LoginResponse(remoteLoginCode);
} else {
// 如果当前系统是单点服务端
SsoServerApi ssoServerApi = SpringUtil.getBean(SsoServerApi.class);
SsoLoginCodeRequest ssoLoginCodeRequest = new SsoLoginCodeRequest();
ssoLoginCodeRequest.setAccount(loginRequest.getAccount());
ssoLoginCodeRequest.setPassword(loginRequest.getPassword());
String remoteLoginCode = ssoServerApi.createSsoLoginCode(ssoLoginCodeRequest);
return new LoginResponse(remoteLoginCode);
}
}
// 5. 获取用户密码的加密值和用户的状态
UserValidateDTO userValidateInfo = sysUserServiceApi.getUserLoginValidateDTO(loginRequest.getAccount());
// 6. 校验用户密码是否正确
validateUserPassword(validatePassword, loginErrorCount, loginRequest, userValidateInfo);
// 7. 校验用户是否异常(不是正常状态)
if (!UserStatusEnum.ENABLE.getCode().equals(userValidateInfo.getUserStatus())) {
throw new AuthException(AuthExceptionEnum.USER_STATUS_ERROR, UserStatusEnum.getCodeMessage(userValidateInfo.getUserStatus()));
}
// 8. 生成用户的token
DefaultJwtPayload defaultJwtPayload = new DefaultJwtPayload(userValidateInfo.getUserId(), loginRequest.getAccount(),
loginRequest.getRememberMe(), caToken, loginRequest.getTenantCode());
String jwtToken = AuthJwtContext.me().generateTokenDefaultPayload(defaultJwtPayload);
// 9. 创建loginUser对象
LoginUser loginUser = new LoginUser(userValidateInfo.getUserId(), jwtToken);
synchronized (loginRequest.getAccount().intern()) {
// 10. 缓存用户信息,创建会话
sessionManagerApi.createSession(jwtToken, loginUser);
// 11. 如果开启了单账号单端在线,则踢掉已经上线的该用户
if (AuthConfigExpander.getSingleAccountLoginFlag()) {
sessionManagerApi.removeSessionExcludeToken(jwtToken);
}
}
// 演示环境,跳过记录日志
if (!DemoConfigExpander.getDemoEnvFlag()) {
// 12. 更新用户登录时间和ip
String ip = HttpServletUtil.getRequestClientIp(HttpServletUtil.getRequest());
sysUserServiceApi.updateUserLoginInfo(loginUser.getUserId(), ip);
// 13.登录成功日志
loginLogServiceApi.loginSuccess(loginUser.getUserId());
}
// 13.1 登录成功,清空用户的错误登录次数
this.cancelFreeze(loginRequest);
// 14. 组装返回结果
return new LoginResponse(loginUser.getUserId(), jwtToken);
}
/**
* loginCode
*
* @author fengshuonan
* @since 2021/2/26 15:15
*/
private String getRemoteLoginCode(LoginRequest loginRequest) {
// 获取sso的地址
String ssoUrl = AuthConfigExpander.getSsoUrl();
// 请求sso服务获取loginCode
HttpRequest httpRequest = HttpRequest.post(ssoUrl + AuthConstants.SYS_AUTH_SSO_GET_LOGIN_CODE);
httpRequest.body(JSON.toJSONString(loginRequest));
HttpResponse httpResponse = httpRequest.execute();
// 获取返回结果的message
String body = httpResponse.body();
JSONObject jsonObject = new JSONObject();
if (StrUtil.isNotBlank(body)) {
jsonObject = JSON.parseObject(body);
}
// 如果返回结果是失败的
if (httpResponse.getStatus() != 200) {
String message = jsonObject.getString("message");
throw new AuthException(AuthExceptionEnum.SSO_LOGIN_CODE_GET_ERROR, message);
}
// 从body中获取loginCode
String loginCode = jsonObject.getString("data");
// loginCode为空
if (loginCode == null) {
throw new AuthException(AuthExceptionEnum.SSO_LOGIN_CODE_GET_ERROR, "loginCode为空");
}
return loginCode;
}
/**
*
*
* @author fengshuonan
* @since 2022/3/26 14:16
*/
private void validateUserPassword(Boolean validatePassword, Integer loginErrorCount, LoginRequest loginRequest,
UserValidateDTO userValidateInfo) {
// 如果本次登录需要校验密码
if (validatePassword) {
Boolean checkResult = passwordStoredEncryptApi.checkPassword(loginRequest.getPassword(),
userValidateInfo.getUserPasswordHexed());
// 校验用户表密码是否正确,如果正确则直接返回
if (checkResult) {
return;
}
// 如果密码不正确则校验用户是否有临时秘钥
TempSecretApi tempSecretApi = null;
try {
tempSecretApi = SpringUtil.getBean(TempSecretApi.class);
if (tempSecretApi != null) {
String userTempSecretKey = tempSecretApi.getUserTempSecretKey(userValidateInfo.getUserId());
// 如果用户有临时秘钥,则校验秘钥是否正确
if (StrUtil.isNotBlank(userTempSecretKey)) {
Boolean checkTempKeyResult = passwordStoredEncryptApi.checkPassword(loginRequest.getPassword(), userTempSecretKey);
if (checkTempKeyResult) {
return;
}
}
}
} catch (Exception ignored) {
}
// 临时秘钥校验完成,返回前端用户密码错误
if (loginErrorCount == null) {
loginErrorCount = 0;
}
// 演示环境不记录error次数
if (!DemoConfigExpander.getDemoEnvFlag()) {
loginErrorCountCacheApi.put(loginRequest.getAccount(), loginErrorCount + 1);
}
throw new AuthException(AuthExceptionEnum.USERNAME_PASSWORD_ERROR);
}
}
} }

View File

@ -0,0 +1,304 @@
package cn.stylefeng.roses.kernel.auth.auth;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
import cn.stylefeng.roses.kernel.auth.api.SsoServerApi;
import cn.stylefeng.roses.kernel.auth.api.TempSecretApi;
import cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants;
import cn.stylefeng.roses.kernel.auth.api.constants.LoginCacheConstants;
import cn.stylefeng.roses.kernel.auth.api.context.AuthJwtContext;
import cn.stylefeng.roses.kernel.auth.api.enums.SsoClientTypeEnum;
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
import cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum;
import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander;
import cn.stylefeng.roses.kernel.auth.api.password.PasswordStoredEncryptApi;
import cn.stylefeng.roses.kernel.auth.api.password.PasswordTransferEncryptApi;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
import cn.stylefeng.roses.kernel.auth.api.pojo.payload.DefaultJwtPayload;
import cn.stylefeng.roses.kernel.auth.api.pojo.sso.SsoLoginCodeRequest;
import cn.stylefeng.roses.kernel.auth.api.pojo.sso.SsoProperties;
import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi;
import cn.stylefeng.roses.kernel.demo.expander.DemoConfigExpander;
import cn.stylefeng.roses.kernel.log.api.LoginLogServiceApi;
import cn.stylefeng.roses.kernel.rule.tenant.RequestTenantCodeHolder;
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
import cn.stylefeng.roses.kernel.scanner.api.exception.ScannerException;
import cn.stylefeng.roses.kernel.scanner.api.exception.enums.ScannerExceptionEnum;
import cn.stylefeng.roses.kernel.scanner.api.holder.InitScanFlagHolder;
import cn.stylefeng.roses.kernel.security.api.DragCaptchaApi;
import cn.stylefeng.roses.kernel.security.api.ImageCaptchaApi;
import cn.stylefeng.roses.kernel.security.api.expander.SecurityConfigExpander;
import cn.stylefeng.roses.kernel.sys.api.SysUserServiceApi;
import cn.stylefeng.roses.kernel.sys.api.enums.user.UserStatusEnum;
import cn.stylefeng.roses.kernel.sys.api.pojo.user.UserValidateDTO;
import cn.stylefeng.roses.kernel.validator.api.exception.enums.ValidatorExceptionEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
*
*
* @author fengshuonan
* @since 2023/6/17 23:45
*/
@Service
public class LoginService {
@Resource
private SysUserServiceApi sysUserServiceApi;
@Resource
private SessionManagerApi sessionManagerApi;
@Resource
private PasswordTransferEncryptApi passwordTransferEncryptApi;
@Resource
private ImageCaptchaApi captchaApi;
@Resource
private DragCaptchaApi dragCaptchaApi;
@Resource
private SsoProperties ssoProperties;
@Resource
private LoginLogServiceApi loginLogServiceApi;
@Resource(name = "loginErrorCountCacheApi")
private CacheOperatorApi<Integer> loginErrorCountCacheApi;
@Resource
private PasswordStoredEncryptApi passwordStoredEncryptApi;
/**
*
*
* @param loginRequest
* @param validatePassword true-false-
* @param caToken token32uuid
* @author fengshuonan
* @since 2020/10/21 16:59
*/
public LoginResponse loginAction(LoginRequest loginRequest, Boolean validatePassword, String caToken) {
// 1.参数为空校验
if (validatePassword) {
if (loginRequest == null || StrUtil.hasBlank(loginRequest.getAccount(), loginRequest.getPassword())) {
throw new AuthException(AuthExceptionEnum.PARAM_EMPTY);
}
} else {
if (loginRequest == null || StrUtil.hasBlank(loginRequest.getAccount())) {
throw new AuthException(AuthExceptionEnum.ACCOUNT_IS_BLANK);
}
}
// 1.2 判断账号是否密码重试次数过多被冻结
Integer loginErrorCount = loginErrorCountCacheApi.get(loginRequest.getAccount());
if (loginErrorCount != null && loginErrorCount >= LoginCacheConstants.MAX_ERROR_LOGIN_COUNT) {
throw new AuthException(AuthExceptionEnum.LOGIN_LOCKED);
}
// 1.3 暂存多租户编码v7.3.2增加,方便缓存调用过程中获取多租户的前缀)
RequestTenantCodeHolder.setTenantCode(loginRequest.getTenantCode());
// 2. 如果开启了验证码校验,则验证当前请求的验证码是否正确
if (SecurityConfigExpander.getCaptchaOpen()) {
String verKey = loginRequest.getVerKey();
String verCode = loginRequest.getVerCode();
if (StrUtil.isEmpty(verKey) || StrUtil.isEmpty(verCode)) {
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_EMPTY);
}
if (!captchaApi.validateCaptcha(verKey, verCode)) {
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_ERROR);
}
}
// 2.1 验证拖拽验证码
if (SecurityConfigExpander.getDragCaptchaOpen()) {
String verKey = loginRequest.getVerKey();
String verXLocationValue = loginRequest.getVerCode();
if (StrUtil.isEmpty(verKey) || StrUtil.isEmpty(verXLocationValue)) {
throw new AuthException(ValidatorExceptionEnum.CAPTCHA_EMPTY);
}
if (!dragCaptchaApi.validateCaptcha(verKey, Convert.toInt(verXLocationValue))) {
throw new AuthException(ValidatorExceptionEnum.DRAG_CAPTCHA_ERROR);
}
}
// 2.2 校验当前系统是否初始化资源完成,如果资源还没有初始化,提示用户请等一下再登录
if (!InitScanFlagHolder.getFlag()) {
throw new ScannerException(ScannerExceptionEnum.SYSTEM_RESOURCE_URL_NOT_INIT);
}
// 3. 解密密码的密文需要sys_config相关配置打开
if (loginRequest.getPassword() != null && AuthConfigExpander.getPasswordRsaValidateFlag()) {
String decryptPassword = passwordTransferEncryptApi.decrypt(loginRequest.getPassword());
loginRequest.setPassword(decryptPassword);
}
// 4. 如果开启了单点登录并且CaToken没有值走单点登录获取loginCode
if (ssoProperties.getOpenFlag() && StrUtil.isEmpty(caToken)) {
if (SsoClientTypeEnum.client.name().equals(ssoProperties.getSsoClientType())) {
// 调用单点的接口获取loginCode远程接口校验用户级密码正确性。
String remoteLoginCode = getRemoteLoginCode(loginRequest);
return new LoginResponse(remoteLoginCode);
} else {
// 如果当前系统是单点服务端
SsoServerApi ssoServerApi = SpringUtil.getBean(SsoServerApi.class);
SsoLoginCodeRequest ssoLoginCodeRequest = new SsoLoginCodeRequest();
ssoLoginCodeRequest.setAccount(loginRequest.getAccount());
ssoLoginCodeRequest.setPassword(loginRequest.getPassword());
String remoteLoginCode = ssoServerApi.createSsoLoginCode(ssoLoginCodeRequest);
return new LoginResponse(remoteLoginCode);
}
}
// 5. 获取用户密码的加密值和用户的状态
UserValidateDTO userValidateInfo = sysUserServiceApi.getUserLoginValidateDTO(loginRequest.getAccount());
// 6. 校验用户密码是否正确
validateUserPassword(validatePassword, loginErrorCount, loginRequest, userValidateInfo);
// 7. 校验用户是否异常(不是正常状态)
if (!UserStatusEnum.ENABLE.getCode().equals(userValidateInfo.getUserStatus())) {
throw new AuthException(AuthExceptionEnum.USER_STATUS_ERROR, UserStatusEnum.getCodeMessage(userValidateInfo.getUserStatus()));
}
// 8. 生成用户的token
DefaultJwtPayload defaultJwtPayload = new DefaultJwtPayload(userValidateInfo.getUserId(), loginRequest.getAccount(),
loginRequest.getRememberMe(), caToken, loginRequest.getTenantCode());
String jwtToken = AuthJwtContext.me().generateTokenDefaultPayload(defaultJwtPayload);
// 9. 创建loginUser对象
LoginUser loginUser = new LoginUser(userValidateInfo.getUserId(), jwtToken);
synchronized (loginRequest.getAccount().intern()) {
// 10. 缓存用户信息,创建会话
sessionManagerApi.createSession(jwtToken, loginUser);
// 11. 如果开启了单账号单端在线,则踢掉已经上线的该用户
if (AuthConfigExpander.getSingleAccountLoginFlag()) {
sessionManagerApi.removeSessionExcludeToken(jwtToken);
}
}
// 演示环境,跳过记录日志
if (!DemoConfigExpander.getDemoEnvFlag()) {
// 12. 更新用户登录时间和ip
String ip = HttpServletUtil.getRequestClientIp(HttpServletUtil.getRequest());
sysUserServiceApi.updateUserLoginInfo(loginUser.getUserId(), ip);
// 13.登录成功日志
loginLogServiceApi.loginSuccess(loginUser.getUserId());
}
// 13.1 登录成功,清空用户的错误登录次数
loginErrorCountCacheApi.remove(loginRequest.getAccount());
// 14. 组装返回结果
return new LoginResponse(loginUser.getUserId(), jwtToken);
}
/**
* loginCode
*
* @author fengshuonan
* @since 2021/2/26 15:15
*/
private String getRemoteLoginCode(LoginRequest loginRequest) {
// 获取sso的地址
String ssoUrl = AuthConfigExpander.getSsoUrl();
// 请求sso服务获取loginCode
HttpRequest httpRequest = HttpRequest.post(ssoUrl + AuthConstants.SYS_AUTH_SSO_GET_LOGIN_CODE);
httpRequest.body(JSON.toJSONString(loginRequest));
HttpResponse httpResponse = httpRequest.execute();
// 获取返回结果的message
String body = httpResponse.body();
JSONObject jsonObject = new JSONObject();
if (StrUtil.isNotBlank(body)) {
jsonObject = JSON.parseObject(body);
}
// 如果返回结果是失败的
if (httpResponse.getStatus() != 200) {
String message = jsonObject.getString("message");
throw new AuthException(AuthExceptionEnum.SSO_LOGIN_CODE_GET_ERROR, message);
}
// 从body中获取loginCode
String loginCode = jsonObject.getString("data");
// loginCode为空
if (loginCode == null) {
throw new AuthException(AuthExceptionEnum.SSO_LOGIN_CODE_GET_ERROR, "loginCode为空");
}
return loginCode;
}
/**
*
*
* @author fengshuonan
* @since 2022/3/26 14:16
*/
private void validateUserPassword(Boolean validatePassword, Integer loginErrorCount, LoginRequest loginRequest,
UserValidateDTO userValidateInfo) {
// 如果本次登录需要校验密码
if (validatePassword) {
Boolean checkResult = passwordStoredEncryptApi.checkPassword(loginRequest.getPassword(),
userValidateInfo.getUserPasswordHexed());
// 校验用户表密码是否正确,如果正确则直接返回
if (checkResult) {
return;
}
// 如果密码不正确则校验用户是否有临时秘钥
TempSecretApi tempSecretApi = null;
try {
tempSecretApi = SpringUtil.getBean(TempSecretApi.class);
if (tempSecretApi != null) {
String userTempSecretKey = tempSecretApi.getUserTempSecretKey(userValidateInfo.getUserId());
// 如果用户有临时秘钥,则校验秘钥是否正确
if (StrUtil.isNotBlank(userTempSecretKey)) {
Boolean checkTempKeyResult = passwordStoredEncryptApi.checkPassword(loginRequest.getPassword(), userTempSecretKey);
if (checkTempKeyResult) {
return;
}
}
}
} catch (Exception ignored) {
}
// 临时秘钥校验完成,返回前端用户密码错误
if (loginErrorCount == null) {
loginErrorCount = 0;
}
// 演示环境不记录error次数
if (!DemoConfigExpander.getDemoEnvFlag()) {
loginErrorCountCacheApi.put(loginRequest.getAccount(), loginErrorCount + 1);
}
throw new AuthException(AuthExceptionEnum.USERNAME_PASSWORD_ERROR);
}
}
}