You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
4.7 KiB
206 lines
4.7 KiB
// Code scaffolded by goctl. Safe to edit.
|
|
// goctl 1.9.2
|
|
|
|
package svc
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/casbin/casbin/v2"
|
|
casbinmodel "github.com/casbin/casbin/v2/model"
|
|
gormadapter "github.com/casbin/gorm-adapter/v3"
|
|
|
|
"github.com/youruser/base/internal/config"
|
|
"github.com/youruser/base/internal/middleware"
|
|
"github.com/youruser/base/model"
|
|
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/zeromicro/go-zero/rest"
|
|
)
|
|
|
|
const casbinModelText = `
|
|
[request_definition]
|
|
r = sub, obj, act
|
|
|
|
[policy_definition]
|
|
p = sub, obj, act
|
|
|
|
[role_definition]
|
|
g = _, _
|
|
|
|
[policy_effect]
|
|
e = some(where (p.eft == allow))
|
|
|
|
[matchers]
|
|
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && r.act == p.act
|
|
`
|
|
|
|
type ServiceContext struct {
|
|
Config config.Config
|
|
Cors rest.Middleware
|
|
Log rest.Middleware
|
|
Auth rest.Middleware
|
|
Authz rest.Middleware
|
|
// 数据库连接
|
|
DB *gorm.DB
|
|
// Casbin enforcer
|
|
Enforcer *casbin.Enforcer
|
|
}
|
|
|
|
func NewServiceContext(c config.Config) *ServiceContext {
|
|
// 创建数据库连接
|
|
dsn := c.MySQL.DSN
|
|
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
|
if err != nil {
|
|
panic("Failed to connect database: " + err.Error())
|
|
}
|
|
|
|
// 自动迁移表
|
|
err = db.AutoMigrate(&model.User{}, &model.Profile{})
|
|
if err != nil {
|
|
panic("Failed to migrate database: " + err.Error())
|
|
}
|
|
|
|
// 初始化 Casbin
|
|
enforcer := initCasbin(db)
|
|
|
|
// 种子超级管理员
|
|
seedSuperAdmin(db)
|
|
|
|
// 种子 Casbin 策略
|
|
seedCasbinPolicies(enforcer)
|
|
|
|
return &ServiceContext{
|
|
Config: c,
|
|
Cors: middleware.NewCorsMiddleware().Handle,
|
|
Log: middleware.NewLogMiddleware().Handle,
|
|
Auth: middleware.NewAuthMiddleware().Handle,
|
|
Authz: middleware.NewAuthzMiddleware(enforcer).Handle,
|
|
DB: db,
|
|
Enforcer: enforcer,
|
|
}
|
|
}
|
|
|
|
// Close 关闭资源
|
|
func (s *ServiceContext) Close() error {
|
|
if s.DB != nil {
|
|
sqlDB, err := s.DB.DB()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return sqlDB.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// initCasbin 初始化 Casbin enforcer
|
|
func initCasbin(db *gorm.DB) *casbin.Enforcer {
|
|
// 使用 GORM adapter(自动创建 casbin_rule 表)
|
|
adapter, err := gormadapter.NewAdapterByDB(db)
|
|
if err != nil {
|
|
panic("Failed to create Casbin adapter: " + err.Error())
|
|
}
|
|
|
|
// 从字符串加载 model
|
|
m, err := casbinmodel.NewModelFromString(casbinModelText)
|
|
if err != nil {
|
|
panic("Failed to create Casbin model: " + err.Error())
|
|
}
|
|
|
|
enforcer, err := casbin.NewEnforcer(m, adapter)
|
|
if err != nil {
|
|
panic("Failed to create Casbin enforcer: " + err.Error())
|
|
}
|
|
|
|
// 加载策略
|
|
if err := enforcer.LoadPolicy(); err != nil {
|
|
panic("Failed to load Casbin policy: " + err.Error())
|
|
}
|
|
|
|
log.Println("[Casbin] Enforcer initialized successfully")
|
|
return enforcer
|
|
}
|
|
|
|
// seedSuperAdmin 首次启动创建超级管理员
|
|
func seedSuperAdmin(db *gorm.DB) {
|
|
ctx := context.Background()
|
|
|
|
existing, err := model.FindOneByUsername(ctx, db, "admin")
|
|
if err == nil {
|
|
if existing.Role != model.RoleSuperAdmin {
|
|
existing.Role = model.RoleSuperAdmin
|
|
existing.Source = model.SourceSystem
|
|
model.Update(ctx, db, existing)
|
|
log.Println("[Seed] Updated admin to super_admin role")
|
|
}
|
|
return
|
|
}
|
|
|
|
password := fmt.Sprintf("%x", md5.Sum([]byte("admin123")))
|
|
admin := &model.User{
|
|
Username: "admin",
|
|
Phone: "13800000000",
|
|
Email: "",
|
|
Password: password,
|
|
Role: model.RoleSuperAdmin,
|
|
Source: model.SourceSystem,
|
|
Remark: "系统自动创建的超级管理员",
|
|
Status: 1,
|
|
}
|
|
|
|
_, err = model.Insert(ctx, db, admin)
|
|
if err != nil {
|
|
log.Printf("[Seed] Failed to create super admin: %v", err)
|
|
return
|
|
}
|
|
log.Println("[Seed] Super admin created: admin / admin123")
|
|
}
|
|
|
|
// seedCasbinPolicies 种子 Casbin 策略(幂等)
|
|
func seedCasbinPolicies(enforcer *casbin.Enforcer) {
|
|
// 角色层级: super_admin > admin > user > guest
|
|
roleHierarchy := [][]string{
|
|
{"super_admin", "admin"},
|
|
{"admin", "user"},
|
|
{"user", "guest"},
|
|
}
|
|
for _, g := range roleHierarchy {
|
|
if has, _ := enforcer.HasGroupingPolicy(g[0], g[1]); !has {
|
|
enforcer.AddGroupingPolicy(g[0], g[1])
|
|
}
|
|
}
|
|
|
|
// 默认策略
|
|
policies := [][]string{
|
|
// guest: 仪表盘只读
|
|
{"guest", "/api/v1/dashboard/*", "GET"},
|
|
|
|
// user: 个人中心
|
|
{"user", "/api/v1/profile/*", "GET"},
|
|
{"user", "/api/v1/profile/*", "PUT"},
|
|
{"user", "/api/v1/profile/*", "POST"},
|
|
|
|
// admin: 用户管理(增查改)
|
|
{"admin", "/api/v1/users", "GET"},
|
|
{"admin", "/api/v1/user", "POST"},
|
|
{"admin", "/api/v1/user/:id", "GET"},
|
|
{"admin", "/api/v1/user/:id", "PUT"},
|
|
|
|
// super_admin: 用户删除
|
|
{"super_admin", "/api/v1/user/:id", "DELETE"},
|
|
}
|
|
|
|
for _, p := range policies {
|
|
if has, _ := enforcer.HasPolicy(p[0], p[1], p[2]); !has {
|
|
enforcer.AddPolicy(p[0], p[1], p[2])
|
|
}
|
|
}
|
|
|
|
enforcer.SavePolicy()
|
|
log.Println("[Casbin] Policies seeded successfully")
|
|
}
|
|
|