feat: govern knowledge assets and source citations
This commit is contained in:
@@ -285,7 +285,6 @@ public class AuthController {
|
||||
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||
@Pattern(regexp = RegexConstants.MOBILE, message = "{user.mobile.phone.number.not.valid}")
|
||||
String phonenumber,
|
||||
@NotBlank(message = "{sms.code.not.blank}")
|
||||
String smsCode
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ public class CaptchaController {
|
||||
@Value("${aihr.sms.prod-fixed-code-enabled:false}")
|
||||
private boolean smsProdFixedCodeEnabled;
|
||||
|
||||
@Value("${aihr.sms.verification-enabled:true}")
|
||||
private boolean smsVerificationEnabled = true;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
@@ -90,6 +93,9 @@ public class CaptchaController {
|
||||
public R<Void> smsCode(
|
||||
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||
@Pattern(regexp = RegexConstants.MOBILE, message = "{user.mobile.phone.number.not.valid}") String phonenumber) {
|
||||
if (!smsVerificationEnabled) {
|
||||
return R.ok();
|
||||
}
|
||||
boolean prodProfile = environment.acceptsProfiles(Profiles.of("prod"));
|
||||
boolean fixedCodeEnabled = shouldUseFixedSmsCode(
|
||||
smsDevFixedCode,
|
||||
|
||||
+12
-1
@@ -32,6 +32,7 @@ import org.dromara.web.service.SysLoginService;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -49,6 +50,9 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
||||
private final SysLoginService loginService;
|
||||
private final SysUserMapper userMapper;
|
||||
|
||||
@Value("${aihr.sms.verification-enabled:true}")
|
||||
private boolean smsVerificationEnabled = true;
|
||||
|
||||
@Override
|
||||
public LoginVo login(String body, SysClientVo client) {
|
||||
SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
|
||||
@@ -58,7 +62,10 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
||||
String smsCode = loginBody.getSmsCode();
|
||||
boolean appClient = isAppClient(client);
|
||||
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||
loginService.checkLogin(LoginType.SMS, tenantId, phonenumber, () -> !validateSmsCode(tenantId, phonenumber, smsCode));
|
||||
if (shouldVerifySmsCode(appClient, smsVerificationEnabled)) {
|
||||
loginService.checkLogin(LoginType.SMS, tenantId, phonenumber,
|
||||
() -> !validateSmsCode(tenantId, phonenumber, smsCode));
|
||||
}
|
||||
SysUserVo user = loadOrRegisterUserByPhonenumber(tenantId, phonenumber, appClient);
|
||||
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
|
||||
return loginService.buildLoginUser(user);
|
||||
@@ -123,6 +130,10 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
||||
return client != null && SmsCodeUtils.MOBILE_CLIENT_ID.equals(client.getClientId());
|
||||
}
|
||||
|
||||
static boolean shouldVerifySmsCode(boolean appClient, boolean verificationEnabled) {
|
||||
return !appClient || verificationEnabled;
|
||||
}
|
||||
|
||||
SysUserVo loadOrRegisterUserByPhonenumber(String tenantId, String phonenumber, boolean appClient) {
|
||||
if (appClient && userMapper.exists(new LambdaQueryWrapper<SysUser>()
|
||||
.eq(SysUser::getTenantId, tenantId)
|
||||
|
||||
@@ -155,6 +155,10 @@ mail:
|
||||
|
||||
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
|
||||
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
|
||||
captcha:
|
||||
# 本地自动化默认免图形验证码;仅显式设置后开启
|
||||
enable: ${AIHR_DEV_CAPTCHA_ENABLED:false}
|
||||
|
||||
aihr:
|
||||
practice:
|
||||
# 仅本地演示允许用训练次数代替 hire_date 推断新员工,生产默认关闭
|
||||
@@ -164,6 +168,8 @@ aihr:
|
||||
store-display-fields: true
|
||||
sms:
|
||||
login-template-id: ${AIHR_SMS_LOGIN_TEMPLATE_ID:}
|
||||
# 本地自动化默认直接按手机号登录;显式开启后才校验短信验证码
|
||||
verification-enabled: ${AIHR_SMS_VERIFICATION_ENABLED:false}
|
||||
# 演示兜底:非空则不真发短信,验证码固定为该值(仅 dev,prod 配置不含此项)
|
||||
dev-fixed-code: ${AIHR_SMS_DEV_FIXED_CODE:123456}
|
||||
sms:
|
||||
|
||||
@@ -167,6 +167,8 @@ aihr:
|
||||
store-display-fields: false
|
||||
sms:
|
||||
login-template-id: ${AIHR_SMS_LOGIN_TEMPLATE_ID:}
|
||||
# 短信是移动端唯一认证因子,生产默认保持校验
|
||||
verification-enabled: ${AIHR_SMS_VERIFICATION_ENABLED:true}
|
||||
# 试点期固定验证码:生产默认关闭,需同时显式配置固定码与开关
|
||||
dev-fixed-code: ${AIHR_SMS_DEV_FIXED_CODE:}
|
||||
prod-fixed-code-enabled: ${AIHR_SMS_PROD_FIXED_CODE_ENABLED:false}
|
||||
|
||||
+11
@@ -17,6 +17,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -37,6 +38,16 @@ class MobileSmsLoginTenantBoundaryTest {
|
||||
assertEquals("000000", AuthController.mobileTenantId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void mobileLoginAllowsAnOmittedCodeWhenDevelopmentVerificationIsDisabled() throws Exception {
|
||||
AuthController.MobileSmsLoginBody body = new ObjectMapper().readValue("""
|
||||
{"phonenumber":"13900000000"}
|
||||
""", AuthController.MobileSmsLoginBody.class);
|
||||
|
||||
assertEquals("13900000000", body.phonenumber());
|
||||
assertNull(body.smsCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void resendingSmsUsesTheSameLockAsVerification() throws Exception {
|
||||
|
||||
+7
@@ -52,6 +52,13 @@ class SmsAuthStrategyTenantTest {
|
||||
assertTrue(SmsAuthStrategy.isAppClient(appClient));
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlyTheMobileAppClientMaySkipSmsVerification() {
|
||||
assertFalse(SmsAuthStrategy.shouldVerifySmsCode(true, false));
|
||||
assertTrue(SmsAuthStrategy.shouldVerifySmsCode(true, true));
|
||||
assertTrue(SmsAuthStrategy.shouldVerifySmsCode(false, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void existingAppUserIsRetainedAndWrongOtpDoesNotConsumeTheCode() throws Exception {
|
||||
SysLoginService loginService = mock(SysLoginService.class);
|
||||
|
||||
Reference in New Issue
Block a user