Files
v-hono/hono/auth.v
T

404 lines
10 KiB
V
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
module hono
import crypto.sha256
import crypto.rand
import time
import json
// 用户角色枚举
pub enum UserRole {
admin
manager
user
guest
}
// 用户结构
pub struct User {
pub:
id int
username string
email string
password_hash string
role UserRole
status bool // true: active, false: inactive
created_at int
updated_at int
}
// 菜单项结构
pub struct MenuItem {
pub:
id int
name string
path string
icon string
parent_id int
sort_order int
permissions []string
status bool
created_at int
updated_at int
mut:
children []MenuItem
}
// 用户会话结构
pub struct UserSession {
pub:
user_id int
token string
expires_at int
created_at int
}
// 认证管理器
pub struct AuthManager {
mut:
db DatabaseManager
}
// 创建认证管理器
pub fn new_auth_manager(db DatabaseManager) AuthManager {
return AuthManager{
db: db
}
}
// 初始化认证相关表
pub fn (mut auth AuthManager) init_tables() ! {
// 创建用户表
auth.db.db.exec('CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT NOT NULL,
status BOOLEAN DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);') or {
return error('Failed to create users table: $err')
}
// 创建菜单表
auth.db.db.exec('CREATE TABLE IF NOT EXISTS menu_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
path TEXT NOT NULL,
icon TEXT,
parent_id INTEGER DEFAULT 0,
sort_order INTEGER DEFAULT 0,
permissions TEXT,
status BOOLEAN DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);') or {
return error('Failed to create menu_items table: $err')
}
// 创建用户会话表
auth.db.db.exec('CREATE TABLE IF NOT EXISTS user_sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
token TEXT UNIQUE NOT NULL,
expires_at INTEGER NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
);') or {
return error('Failed to create user_sessions table: $err')
}
// 创建索引
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);') or {
return error('Failed to create index: $err')
}
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);') or {
return error('Failed to create index: $err')
}
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_menu_parent ON menu_items(parent_id);') or {
return error('Failed to create index: $err')
}
auth.db.db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_token ON user_sessions(token);') or {
return error('Failed to create index: $err')
}
}
// 密码哈希
fn hash_password(password string) string {
return sha256.sum(password.bytes()).hex()
}
// 生成随机令牌
fn generate_token() string {
random_bytes := rand.bytes(32) or { return '' }
mut token := ''
for byte in random_bytes {
token += '${byte:02x}'
}
return token
}
// 创建用户
pub fn (mut auth AuthManager) create_user(username string, email string, password string, role UserRole) !User {
now := int(time.now().unix())
password_hash := hash_password(password)
role_str := role.str()
auth.db.db.exec('INSERT INTO users (username, email, password_hash, role, status, created_at, updated_at) VALUES ("$username", "$email", "$password_hash", "$role_str", 1, $now, $now)') or {
return error('Failed to create user: $err')
}
// 获取插入的用户ID
rows := auth.db.db.exec('SELECT last_insert_rowid()') or {
return error('Failed to get user ID: $err')
}
user_id := rows[0].vals[0].int()
return User{
id: user_id
username: username
email: email
password_hash: password_hash
role: role
status: true
created_at: now
updated_at: now
}
}
// 用户登录
pub fn (mut auth AuthManager) login(username string, password string) !UserSession {
password_hash := hash_password(password)
rows := auth.db.db.exec('SELECT id, username, email, password_hash, role, status FROM users WHERE username = "$username" AND password_hash = "$password_hash" AND status = 1') or {
return error('Failed to query user: $err')
}
if rows.len == 0 {
return error('Invalid username or password')
}
user := rows[0]
user_id := user.vals[0].int()
// 生成会话令牌
token := generate_token()
now := int(time.now().unix())
expires_at := now + (24 * 60 * 60) // 24小时后过期
// 保存会话
auth.db.db.exec('INSERT INTO user_sessions (user_id, token, expires_at, created_at) VALUES ($user_id, "$token", $expires_at, $now)') or {
return error('Failed to create session: $err')
}
return UserSession{
user_id: user_id
token: token
expires_at: expires_at
created_at: now
}
}
// 验证令牌
pub fn (auth AuthManager) verify_token(token string) !User {
rows := auth.db.db.exec('SELECT u.id, u.username, u.email, u.password_hash, u.role, u.status FROM users u JOIN user_sessions s ON u.id = s.user_id WHERE s.token = "$token" AND s.expires_at > ${int(time.now().unix())} AND u.status = 1') or {
return error('Failed to verify token: $err')
}
if rows.len == 0 {
return error('Invalid or expired token')
}
user := rows[0]
role_str := user.vals[4]
role := match role_str {
'admin' { UserRole.admin }
'manager' { UserRole.manager }
'user' { UserRole.user }
'guest' { UserRole.guest }
else { UserRole.guest }
}
return User{
id: user.vals[0].int()
username: user.vals[1]
email: user.vals[2]
password_hash: user.vals[3]
role: role
status: user.vals[5].int() == 1
created_at: 0
updated_at: 0
}
}
// 注销会话
pub fn (mut auth AuthManager) logout(token string) ! {
auth.db.db.exec('DELETE FROM user_sessions WHERE token = "$token"') or {
return error('Failed to logout: $err')
}
}
// 创建菜单项
pub fn (mut auth AuthManager) create_menu_item(name string, path string, icon string, parent_id int, sort_order int, permissions []string) !MenuItem {
now := int(time.now().unix())
permissions_json := json.encode(permissions)
permissions_sql := permissions_json.replace('"', "''")
auth.db.db.exec('INSERT INTO menu_items (name, path, icon, parent_id, sort_order, permissions, status, created_at, updated_at) VALUES ("$name", "$path", "$icon", $parent_id, $sort_order, "$permissions_sql", 1, $now, $now)')
or {
eprintln('菜单插入SQL错误: $err')
return error('Failed to create menu item: $err')
}
// 获取插入的菜单ID
rows := auth.db.db.exec('SELECT last_insert_rowid()') or {
return error('Failed to get menu ID: $err')
}
menu_id := rows[0].vals[0].int()
return MenuItem{
id: menu_id
name: name
path: path
icon: icon
parent_id: parent_id
sort_order: sort_order
permissions: permissions
status: true
created_at: now
updated_at: now
}
}
// 获取所有菜单项
pub fn (auth AuthManager) get_all_menu_items() ![]MenuItem {
rows := auth.db.db.exec('SELECT id, name, path, icon, parent_id, sort_order, permissions, status FROM menu_items ORDER BY parent_id, sort_order') or {
return error('Failed to query all menu items: $err')
}
mut menus := []MenuItem{}
for row in rows {
permissions_str := row.vals[6]
permissions := json.decode([]string, permissions_str) or { []string{} }
menus << MenuItem{
id: row.vals[0].int()
name: row.vals[1]
path: row.vals[2]
icon: row.vals[3]
parent_id: row.vals[4].int()
sort_order: row.vals[5].int()
permissions: permissions
status: row.vals[7].int() == 1
created_at: 0
updated_at: 0
}
}
return menus
}
// 获取用户菜单
pub fn (auth AuthManager) get_user_menus(user User) ![]MenuItem {
// 根据用户角色获取菜单
role_permissions := match user.role {
.admin { ['*'] } // 管理员拥有所有权限
.manager { ['read', 'write', 'manage'] }
.user { ['read', 'write'] }
.guest { ['read'] }
}
// 构建权限查询条件
mut permission_conditions := []string{}
for permission in role_permissions {
if permission == '*' {
permission_conditions << 'permissions LIKE "%*%"'
} else {
permission_conditions << 'permissions LIKE "%$permission%"'
}
}
permission_where := permission_conditions.join(' OR ')
rows := auth.db.db.exec('SELECT id, name, path, icon, parent_id, sort_order, permissions, status FROM menu_items WHERE status = 1 AND ($permission_where) ORDER BY parent_id, sort_order') or {
return error('Failed to query menu items: $err')
}
mut menus := []MenuItem{}
for row in rows {
permissions_str := row.vals[6]
permissions := json.decode([]string, permissions_str) or { []string{} }
menus << MenuItem{
id: row.vals[0].int()
name: row.vals[1]
path: row.vals[2]
icon: row.vals[3]
parent_id: row.vals[4].int()
sort_order: row.vals[5].int()
permissions: permissions
status: row.vals[7].int() == 1
created_at: 0
updated_at: 0
}
}
return menus
}
// 构建菜单树
pub fn (auth AuthManager) build_menu_tree(menus []MenuItem) []MenuItem {
mut menu_map := map[int]MenuItem{}
mut root_menus := []MenuItem{}
// 创建菜单映射
for menu in menus {
menu_map[menu.id] = menu
}
// 构建树结构
for menu in menus {
if menu.parent_id == 0 {
// 根菜单
mut root_menu := menu
root_menu.children = auth.get_children(menu.id, menu_map)
root_menus << root_menu
}
}
return root_menus
}
// 获取子菜单
fn (auth AuthManager) get_children(parent_id int, menu_map map[int]MenuItem) []MenuItem {
mut children := []MenuItem{}
for _, menu in menu_map {
if menu.parent_id == parent_id {
mut child := menu
child.children = auth.get_children(menu.id, menu_map)
children << child
}
}
return children
}
// 检查用户权限
pub fn (auth AuthManager) check_permission(user User, required_permission string) bool {
return match user.role {
.admin { true } // 管理员拥有所有权限
.manager { required_permission in ['read', 'write', 'manage'] }
.user { required_permission in ['read', 'write'] }
.guest { required_permission == 'read' }
}
}
// 获取菜单id(通过path)
pub fn (auth AuthManager) get_menu_id_by_path(path string) int {
rows := auth.db.db.exec('SELECT id FROM menu_items WHERE path = "$path"') or { return 0 }
if rows.len > 0 {
return rows[0].vals[0].int()
}
return 0
}