fix(tenant):修复用户名缺少验证

This commit is contained in:
martin committed 2026-06-07 19:03:29 +08:00
1 parent 87744297d0
commit 5117173b4b
9 files changed
+116 -5

No files matched your search

+7 -1
View File
@@ -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
+5
View File
@@ -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()
+12 -1
View File
@@ -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
}
+5
View File
@@ -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 {
+21
View File
@@ -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 {
+5
View File
@@ -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 {
+11
View File
@@ -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
+26 -1
View File
@@ -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', {
+24 -2
View File
@@ -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>