gin 示例

最后更新于:2022-04-02 03:07:28

[TOC] ## 概述
rbac_model.conf ``` [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) && r.obj == p.obj && r.act == p.act ```

main.go ``` package main import ( "fmt" "github.com/allegro/bigcache" "github.com/casbin/casbin/v2" gormadapter "github.com/casbin/gorm-adapter/v3" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/gofrs/uuid" "gorm.io/driver/sqlite" "gorm.io/gorm" "log" "time" ) var ( DB *gorm.DB GlobalCache *bigcache.BigCache router *gin.Engine adapter *gormadapter.Adapter // 自动生成数据 tables []gormadapter.CasbinRule ) func init() { // Connect to DB var err error open := sqlite.Open("gorm.db") DB, err = gorm.Open(open, &gorm.Config{}) if err != nil { panic(fmt.Sprintf("failed to connect to DB: %v", err)) } // Initialize cache GlobalCache, err = bigcache.NewBigCache(bigcache.DefaultConfig(30 * time.Minute)) // Set expire time to 30 mins if err != nil { panic(fmt.Sprintf("failed to initialize cahce: %v", err)) } DB.Exec("drop table casbin_rule") // Initialize casbin adapter adapter, err = gormadapter.NewAdapterByDB(DB) if err != nil { panic(fmt.Sprintf("failed to initialize casbin adapter: %v", err)) } // 自动生成数据 tables = append(tables, gormadapter.CasbinRule{Ptype: "p", V0: "user", V1: "data", V2: "read"}) tables = append(tables, gormadapter.CasbinRule{Ptype: "p", V0: "admin", V1: "data", V2: "read"}) tables = append(tables, gormadapter.CasbinRule{Ptype: "p", V0: "admin", V1: "data", V2: "write"}) tables = append(tables, gormadapter.CasbinRule{Ptype: "g", V0: "Bob", V1: "user"}) tables = append(tables, gormadapter.CasbinRule{Ptype: "g", V0: "Alice", V1: "admin"}) DB.Create(tables) // Initialize gin router router = gin.Default() corsConfig := cors.DefaultConfig() corsConfig.AllowAllOrigins = true corsConfig.AllowCredentials = true router.Use(cors.New(corsConfig)) // CORS configuraion router.POST("/user/login", Login) // Secure our API resource := router.Group("/api") { resource.GET("/data", Authorize("data", "read", adapter), ReadResource) resource.POST("/data", Authorize("data", "write", adapter), WriteResource) } } func main() { // Start our application err := router.Run(":8888") if err != nil { panic(fmt.Sprintf("failed to start gin engin: %v", err)) } log.Println("application is now running...") } func Login(c *gin.Context) { username, password := c.PostForm("username"), c.PostForm("password") // Authentication // blahblah... fmt.Printf("%+v\n", password) // Generate random session id u, err := uuid.NewV4() if err != nil { log.Fatal(err) } sessionId := fmt.Sprintf("%s-%s", u.String(), username) // Store current subject in cache GlobalCache.Set(sessionId, []byte(username)) // Send cache key back to client in cookie c.SetCookie("current_subject", sessionId, 30*60, "/data", "", false, true) c.Set("current_subject",sessionId) c.JSON(200, RestResponse{Code: 1, Message: username + " logged in successfully"}) } type RestResponse struct { Code int Message string Data interface{} } func ReadResource(c *gin.Context) { // some stuff // blahblah... c.JSON(200, RestResponse{Code: 1, Message: "read resource successfully", Data: "resource"}) } func WriteResource(c *gin.Context) { // some stuff // blahblah... c.JSON(200, RestResponse{Code: 1, Message: "write resource successfully", Data: "resource"}) } func Authorize(obj string, act string, adapter *gormadapter.Adapter) gin.HandlerFunc { return func(c *gin.Context) { // Get current user/subject session, existed := c.Cookie("current_subject") if existed!=nil { c.AbortWithStatusJSON(401, RestResponse{Message: "user hasn't logged in yet"}) return } fmt.Printf("======adapter%+v\n", session) sub,err:=GlobalCache.Get(session) if err != nil { c.AbortWithStatusJSON(401, RestResponse{Message: "not found sessionid"}) return } // 进行权限判断 ok, err := enforce(string(sub), obj, act, adapter) if err != nil { log.Println(err) c.AbortWithStatusJSON(500, RestResponse{Message: "error occurred when authorizing user"}) return } if !ok { c.AbortWithStatusJSON(403, RestResponse{Message: "forbidden"}) return } c.Next() } } func enforce(sub string, obj string, act string, adapter *gormadapter.Adapter) (bool, error) { // Load model configuration file and policy store adapter enforcer, err := casbin.NewEnforcer("rbac_model.conf", adapter) if err != nil { return false, fmt.Errorf("failed to create casbin enforcer: %w", err) } // Load policies from DB dynamically err = enforcer.LoadPolicy() if err != nil { return false, fmt.Errorf("failed to load policy from DB: %w", err) } // Verify ok, err := enforcer.Enforce(sub, obj, act) return ok, err } ```

使用IDEA的http 测试功能测试 测试 Bob ``` # 测试 Bol 角色为 user POST http://localhost:8888/user/login Content-Type: application/x-www-form-urlencoded username=Bol&password=123456 ### 断言有可读权限 data read GET http://localhost:8888/api/data Cookie: current_subject=93b9b663-2d63-4ba8-a80e-95743262be4c-Alice; ### 断言无写权限 data write POST http://localhost:8888/api/data Cookie: current_subject=93b9b663-2d63-4ba8-a80e-95743262be4c-Alice; ### 测试 Alice 角色为 admin ``` 测试 Alcie ``` # 测试 Alcie 角色为 admin POST http://localhost:8888/user/login Content-Type: application/x-www-form-urlencoded username=Alice&password=123456 ### 断言有可读权限 data read GET http://localhost:8888/api/data Cookie: current_subject=93b9b663-2d63-4ba8-a80e-95743262be4c-Alice; ### 断言有写权限 data write POST http://localhost:8888/api/data Cookie: current_subject=93b9b663-2d63-4ba8-a80e-95743262be4c-Alice; ```
';