mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-09 09:06:36 +08:00
fix(user): restore plaintext default password checking and warning mechanism
This commit is contained in:
@@ -30,7 +30,7 @@ ON CONFLICT (username) DO NOTHING;
|
|||||||
|
|
||||||
-- Seed default administrator user (username: admin, password: 12345678)
|
-- Seed default administrator user (username: admin, password: 12345678)
|
||||||
INSERT INTO w_users (id, username, password, nickname, email, is_active, is_admin, last_login_at, created_at, updated_at)
|
INSERT INTO w_users (id, username, password, nickname, email, is_active, is_admin, last_login_at, created_at, updated_at)
|
||||||
VALUES (1, 'admin', '$2a$12$oJ3wdlwSnBxjJHUdxDlWAOF/9JyWa1Ordk8XRcfcghmLI6v4VMA12', '管理员', 'admin@wavelet.local', TRUE, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
VALUES (1, 'admin', '12345678', '管理员', 'admin@wavelet.local', TRUE, TRUE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
ON CONFLICT (username) DO NOTHING;
|
ON CONFLICT (username) DO NOTHING;
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ ON CONFLICT (username) DO NOTHING;
|
|||||||
|
|
||||||
-- Seed default administrator user (username: admin, password: 12345678)
|
-- Seed default administrator user (username: admin, password: 12345678)
|
||||||
INSERT INTO w_users (id, username, password, nickname, email, is_active, is_admin, last_login_at, created_at, updated_at)
|
INSERT INTO w_users (id, username, password, nickname, email, is_active, is_admin, last_login_at, created_at, updated_at)
|
||||||
VALUES (1, 'admin', '$2a$12$oJ3wdlwSnBxjJHUdxDlWAOF/9JyWa1Ordk8XRcfcghmLI6v4VMA12', '管理员', 'admin@wavelet.local', 1, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
VALUES (1, 'admin', '12345678', '管理员', 'admin@wavelet.local', 1, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
ON CONFLICT (username) DO NOTHING;
|
ON CONFLICT (username) DO NOTHING;
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
|||||||
@@ -29,22 +29,23 @@ func (AccessToken) TableName() string {
|
|||||||
|
|
||||||
// User 用户表实体
|
// User 用户表实体
|
||||||
type User struct {
|
type User struct {
|
||||||
ID uint64 `json:"id,string" gorm:"primaryKey;not null"`
|
ID uint64 `json:"id,string" gorm:"primaryKey;not null"`
|
||||||
Username string `json:"username" gorm:"size:64;uniqueIndex"`
|
Username string `json:"username" gorm:"size:64;uniqueIndex"`
|
||||||
Password string `json:"password,omitempty" gorm:"size:255"`
|
Password string `json:"password,omitempty" gorm:"size:255"`
|
||||||
Nickname string `json:"nickname" gorm:"size:255"`
|
Nickname string `json:"nickname" gorm:"size:255"`
|
||||||
Email string `json:"email" gorm:"size:255;index"`
|
Email string `json:"email" gorm:"size:255;index"`
|
||||||
AvatarURL string `json:"avatar_url" gorm:"size:255"`
|
AvatarURL string `json:"avatar_url" gorm:"size:255"`
|
||||||
IsActive bool `json:"is_active" gorm:"default:true;index"`
|
IsActive bool `json:"is_active" gorm:"default:true;index"`
|
||||||
IsAdmin bool `json:"is_admin" gorm:"default:false"`
|
IsAdmin bool `json:"is_admin" gorm:"default:false"`
|
||||||
Bio string `json:"bio" gorm:"size:500"`
|
NeedChangePassword bool `json:"need_change_password,omitempty" gorm:"-"`
|
||||||
Phone string `json:"phone" gorm:"size:32"`
|
Bio string `json:"bio" gorm:"size:500"`
|
||||||
Gender string `json:"gender" gorm:"size:16"`
|
Phone string `json:"phone" gorm:"size:32"`
|
||||||
Website string `json:"website" gorm:"size:255"`
|
Gender string `json:"gender" gorm:"size:16"`
|
||||||
Location string `json:"location" gorm:"size:255"`
|
Website string `json:"website" gorm:"size:255"`
|
||||||
LastLoginAt time.Time `json:"last_login_at" gorm:"index"`
|
Location string `json:"location" gorm:"size:255"`
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;index"`
|
LastLoginAt time.Time `json:"last_login_at" gorm:"index"`
|
||||||
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;index"`
|
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;index"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 表名
|
// TableName 表名
|
||||||
@@ -52,6 +53,17 @@ func (User) TableName() string {
|
|||||||
return "w_users"
|
return "w_users"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPlaintextPassword 检查当前密码是否为未加密的明文密码(如初始默认密码)
|
||||||
|
func (u *User) IsPlaintextPassword() bool {
|
||||||
|
if u.Password == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !strings.HasPrefix(u.Password, "$2a$") &&
|
||||||
|
!strings.HasPrefix(u.Password, "$2b$") &&
|
||||||
|
!strings.HasPrefix(u.Password, "$2y$") &&
|
||||||
|
!strings.HasPrefix(u.Password, "$2x$")
|
||||||
|
}
|
||||||
|
|
||||||
// SetEncryptedPassword 设置加密密码
|
// SetEncryptedPassword 设置加密密码
|
||||||
func (u *User) SetEncryptedPassword(password string) error {
|
func (u *User) SetEncryptedPassword(password string) error {
|
||||||
trimmed := strings.TrimSpace(password)
|
trimmed := strings.TrimSpace(password)
|
||||||
@@ -66,13 +78,22 @@ func (u *User) SetEncryptedPassword(password string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckPassword 校验密码
|
// CheckPassword 校验密码(支持 bcrypt 哈希校验与初始明文密码校验)
|
||||||
func (u *User) CheckPassword(password string) bool {
|
func (u *User) CheckPassword(password string) bool {
|
||||||
if u.Password == "" {
|
if u.Password == "" {
|
||||||
util.DummyCheckPassword(password)
|
util.DummyCheckPassword(password)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return util.CheckPasswordHash(u.Password, password)
|
if !u.IsPlaintextPassword() {
|
||||||
|
return util.CheckPasswordHash(u.Password, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 明文密码兼容比对(识别初始默认密码向用户警告修改密码)
|
||||||
|
if u.Password == password {
|
||||||
|
u.NeedChangePassword = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// loginRequest 登录请求参数
|
// loginRequest 登录请求参数
|
||||||
|
|||||||
@@ -157,4 +157,24 @@ func TestUserLoginHTTPHandler(t *testing.T) {
|
|||||||
setCookie := w.Header().Get("Set-Cookie")
|
setCookie := w.Header().Get("Set-Cookie")
|
||||||
assert.NotEmpty(t, setCookie)
|
assert.NotEmpty(t, setCookie)
|
||||||
assert.Contains(t, setCookie, "wavelet_session=")
|
assert.Contains(t, setCookie, "wavelet_session=")
|
||||||
|
|
||||||
|
// Plaintext default password seeded user
|
||||||
|
plainUser := &user.User{
|
||||||
|
Username: "plain_admin",
|
||||||
|
Password: "12345678", // Plaintext seed
|
||||||
|
Email: "plain@example.com",
|
||||||
|
IsActive: true,
|
||||||
|
}
|
||||||
|
require.NoError(t, user.CreateUser(context.Background(), plainUser))
|
||||||
|
|
||||||
|
reqBodyPlain := `{"username":"plain_admin","password":"12345678"}`
|
||||||
|
reqPlain, _ := http.NewRequest(http.MethodPost, "/api/v1/user/login", bytes.NewBufferString(reqBodyPlain))
|
||||||
|
reqPlain.Header.Set("Content-Type", "application/json")
|
||||||
|
wPlain := httptest.NewRecorder()
|
||||||
|
|
||||||
|
r.ServeHTTP(wPlain, reqPlain)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, wPlain.Code)
|
||||||
|
assert.Contains(t, wPlain.Body.String(), `"username":"plain_admin"`)
|
||||||
|
assert.Contains(t, wPlain.Body.String(), `"need_change_password":true`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,21 +24,22 @@ func toUserDTO(u *User) *contracts.UserDTO {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &contracts.UserDTO{
|
return &contracts.UserDTO{
|
||||||
ID: u.ID,
|
ID: u.ID,
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
Nickname: u.Nickname,
|
Nickname: u.Nickname,
|
||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
AvatarURL: u.AvatarURL,
|
AvatarURL: u.AvatarURL,
|
||||||
IsActive: u.IsActive,
|
IsActive: u.IsActive,
|
||||||
IsAdmin: u.IsAdmin,
|
IsAdmin: u.IsAdmin,
|
||||||
Bio: u.Bio,
|
NeedChangePassword: u.NeedChangePassword || u.IsPlaintextPassword(),
|
||||||
Phone: u.Phone,
|
Bio: u.Bio,
|
||||||
Gender: u.Gender,
|
Phone: u.Phone,
|
||||||
Website: u.Website,
|
Gender: u.Gender,
|
||||||
Location: u.Location,
|
Website: u.Website,
|
||||||
LastLoginAt: u.LastLoginAt,
|
Location: u.Location,
|
||||||
CreatedAt: u.CreatedAt,
|
LastLoginAt: u.LastLoginAt,
|
||||||
UpdatedAt: u.UpdatedAt,
|
CreatedAt: u.CreatedAt,
|
||||||
|
UpdatedAt: u.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user