fix(tenant):修复用户名缺少验证
This commit is contained in:
1 parent
87744297d0
commit
5117173b4b
9 files changed
+116
-5
No files matched your search
@@ -170,6 +170,9 @@ const (
|
||||
CodeModelImportInvalidFile = 10087
|
||||
CodeModelImportBadVersion = 10088
|
||||
|
||||
// Username validation
|
||||
CodeInvalidUsername = 10089
|
||||
|
||||
// Demo mode
|
||||
CodeDemoModeRestricted = 10403
|
||||
|
||||
@@ -276,7 +279,7 @@ const (
|
||||
|
||||
// Setup errors
|
||||
MsgSetupCompleted = "系统已完成初始化"
|
||||
MsgSetupInvalidUsername = "用户名格式无效,仅支持字母、数字和下划线,长度3-20"
|
||||
MsgSetupInvalidUsername = "用户名格式无效,仅支持字母和数字,长度3-20"
|
||||
MsgSetupPasswordMismatch = "两次输入的密码不一致"
|
||||
MsgSetupNotInitialized = "系统未初始化,请先完成设置"
|
||||
|
||||
@@ -290,6 +293,9 @@ const (
|
||||
// Model import errors
|
||||
MsgModelImportInvalidFile = "导入文件格式无效"
|
||||
MsgModelImportBadVersion = "导入文件版本不兼容"
|
||||
|
||||
// Username validation
|
||||
MsgInvalidUsername = "用户名仅支持英文字母和数字,不能为纯数字"
|
||||
)
|
||||
|
||||
// Demo mode messages
|
||||
|
||||
@@ -72,6 +72,11 @@ func (s *sAdmin) ListUsers(ctx context.Context, req *v1.AdminUserListReq) (*v1.A
|
||||
func (s *sAdmin) CreateUser(ctx context.Context, req *v1.AdminUserCreateReq) (*v1.AdminUserCreateRes, error) {
|
||||
username := strings.TrimSpace(req.Username)
|
||||
|
||||
// Validate username format
|
||||
if err := common.ValidateUsername(username); err != nil {
|
||||
return nil, common.NewBusinessError(consts.CodeInvalidUsername, err.Error())
|
||||
}
|
||||
|
||||
// Check username uniqueness
|
||||
count, err := dao.SysAdminUsers.Ctx(ctx).
|
||||
Where("username", username).Count()
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/qianfree/team-api/internal/utility/crypto"
|
||||
)
|
||||
|
||||
var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9_]{3,20}$`)
|
||||
var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9]{3,20}$`)
|
||||
|
||||
// AdminExists checks whether any admin user exists in the database.
|
||||
func AdminExists(ctx context.Context) (bool, error) {
|
||||
@@ -31,6 +31,17 @@ func ValidateSetupUsername(username string) error {
|
||||
if !usernameRegex.MatchString(username) {
|
||||
return common.NewBusinessError(consts.CodeSetupInvalidUsername, consts.MsgSetupInvalidUsername)
|
||||
}
|
||||
// Disallow pure numeric usernames
|
||||
allDigit := true
|
||||
for _, c := range username {
|
||||
if c < '0' || c > '9' {
|
||||
allDigit = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allDigit {
|
||||
return common.NewBusinessError(consts.CodeSetupInvalidUsername, "用户名不能为纯数字")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,11 @@ func (s *sAdmin) CreateTenant(ctx context.Context, req *v1.TenantCreateReq) (*v1
|
||||
username := strings.TrimSpace(req.Username)
|
||||
email := strings.TrimSpace(strings.ToLower(req.Email))
|
||||
|
||||
// Validate username format
|
||||
if err := common.ValidateUsername(username); err != nil {
|
||||
return nil, common.NewBusinessError(consts.CodeInvalidUsername, err.Error())
|
||||
}
|
||||
|
||||
count, err := dao.TntTenants.Ctx(ctx).
|
||||
Where("code", tenantCode).Count()
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,27 @@ import (
|
||||
"github.com/qianfree/team-api/internal/consts"
|
||||
)
|
||||
|
||||
// ValidateUsername 校验用户名格式:仅允许英文字母和数字,不能为纯数字,长度3-50
|
||||
func ValidateUsername(username string) error {
|
||||
if len(username) < 3 || len(username) > 50 {
|
||||
return gerror.New("用户名长度为3-50位")
|
||||
}
|
||||
allDigit := true
|
||||
for _, c := range username {
|
||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
|
||||
allDigit = false
|
||||
} else if c >= '0' && c <= '9' {
|
||||
// digits are ok
|
||||
} else {
|
||||
return gerror.New("用户名仅支持英文字母和数字,不能包含特殊字符或中文")
|
||||
}
|
||||
}
|
||||
if allDigit {
|
||||
return gerror.New("用户名不能为纯数字")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePassword 校验密码强度:至少8位,包含大写、小写、数字
|
||||
func ValidatePassword(password string) error {
|
||||
if len(password) < 8 {
|
||||
|
||||
@@ -35,6 +35,11 @@ func (s *sTenant) Register(ctx context.Context, req *v1.TenantRegisterReq) (*v1.
|
||||
tenantCode := strings.TrimSpace(strings.ToLower(req.TenantCode))
|
||||
username := strings.TrimSpace(req.Username)
|
||||
|
||||
// Validate username format
|
||||
if err := common.ValidateUsername(username); err != nil {
|
||||
return nil, common.NewBusinessError(consts.CodeInvalidUsername, err.Error())
|
||||
}
|
||||
|
||||
// 根据配置选择验证方式
|
||||
emailVerificationEnabled := common.Config().GetBool(ctx, "register_email_verification")
|
||||
if emailVerificationEnabled {
|
||||
|
||||
@@ -229,6 +229,11 @@ func (s *sTenant) JoinByInvite(ctx context.Context, req *v1.TenantMemberJoinReq)
|
||||
username := strings.TrimSpace(req.Username)
|
||||
tenantID := invitation.TenantID
|
||||
|
||||
// Validate username format
|
||||
if err := common.ValidateUsername(username); err != nil {
|
||||
return nil, common.NewBusinessError(consts.CodeInvalidUsername, err.Error())
|
||||
}
|
||||
|
||||
var userID int64
|
||||
|
||||
err = dao.TntTenants.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
@@ -381,6 +386,12 @@ func (s *sTenant) CreateMember(ctx context.Context, req *v1.TenantMemberCreateRe
|
||||
|
||||
username := strings.TrimSpace(req.Username)
|
||||
email := strings.TrimSpace(strings.ToLower(req.Email))
|
||||
|
||||
// Validate username format
|
||||
if err := common.ValidateUsername(username); err != nil {
|
||||
return nil, common.NewBusinessError(consts.CodeInvalidUsername, err.Error())
|
||||
}
|
||||
|
||||
displayName := req.DisplayName
|
||||
if displayName == "" {
|
||||
displayName = username
|
||||
|
||||
@@ -41,10 +41,13 @@
|
||||
v-model="form.username"
|
||||
type="text"
|
||||
required
|
||||
placeholder="请输入用户名"
|
||||
placeholder="仅支持英文字母和数字,不能为纯数字"
|
||||
class="input pl-11"
|
||||
:class="{ 'input-error': usernameError }"
|
||||
@input="validateUsername"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="usernameError" class="input-error-text">{{ usernameError }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -193,6 +196,25 @@ const form = ref({
|
||||
password: '',
|
||||
})
|
||||
|
||||
const usernameError = ref('')
|
||||
|
||||
function validateUsername() {
|
||||
const val = form.value.username
|
||||
if (!val) {
|
||||
usernameError.value = ''
|
||||
return
|
||||
}
|
||||
if (/[^a-zA-Z0-9]/.test(val)) {
|
||||
usernameError.value = '用户名仅支持英文字母和数字'
|
||||
} else if (/^\d+$/.test(val)) {
|
||||
usernameError.value = '用户名不能为纯数字'
|
||||
} else if (val.length < 3) {
|
||||
usernameError.value = '用户名长度至少 3 位'
|
||||
} else {
|
||||
usernameError.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const code = route.query.code as string
|
||||
if (!code) {
|
||||
@@ -218,6 +240,9 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
async function handleJoin() {
|
||||
validateUsername()
|
||||
if (usernameError.value) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await fetch('/api/tenant/members/join', {
|
||||
|
||||
@@ -89,6 +89,10 @@ function validateUser(): boolean {
|
||||
|
||||
if (!userForm.username.trim()) {
|
||||
userErrors.username = '请输入用户名'
|
||||
} else if (/[^a-zA-Z0-9]/.test(userForm.username)) {
|
||||
userErrors.username = '用户名仅支持英文字母和数字'
|
||||
} else if (/^\d+$/.test(userForm.username)) {
|
||||
userErrors.username = '用户名不能为纯数字'
|
||||
} else if (userForm.username.length < 3) {
|
||||
userErrors.username = '用户名长度至少 3 位'
|
||||
}
|
||||
@@ -96,6 +100,23 @@ function validateUser(): boolean {
|
||||
return Object.keys(userErrors).length === 0
|
||||
}
|
||||
|
||||
function validateUsernameRealtime() {
|
||||
const val = userForm.username
|
||||
if (!val) {
|
||||
delete userErrors.username
|
||||
return
|
||||
}
|
||||
if (/[^a-zA-Z0-9]/.test(val)) {
|
||||
userErrors.username = '用户名仅支持英文字母和数字'
|
||||
} else if (/^\d+$/.test(val)) {
|
||||
userErrors.username = '用户名不能为纯数字'
|
||||
} else if (val.length < 3) {
|
||||
userErrors.username = '用户名长度至少 3 位'
|
||||
} else {
|
||||
delete userErrors.username
|
||||
}
|
||||
}
|
||||
|
||||
function goNext() {
|
||||
if (!validateOrg()) return
|
||||
step.value = 2
|
||||
@@ -292,10 +313,11 @@ async function handleRegister() {
|
||||
<input
|
||||
v-model="userForm.username"
|
||||
type="text"
|
||||
placeholder="请输入用户名"
|
||||
placeholder="仅支持英文字母和数字,不能为纯数字"
|
||||
class="input pl-11"
|
||||
:class="{ 'input-error': userErrors.username }"
|
||||
/>
|
||||
@input="validateUsernameRealtime"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="userErrors.username" class="input-error-text">{{ userErrors.username }}</p>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user