3

routing

The routing library used in the gin framework is based on httprouter

Restful style API

Representational State Transfer is an API design concept for Internet applications:

  • The URL locates the resource and uses HTTP to describe the operation
  • Add POST / delete DELETE / change PUT / check GET

parameter

  • API parameters : Param method

     r.GET("/:name/*action", func(c *gin.Context) {
            name := c.Param("name")
            action := c.Param("action")        // action = /yyy
            action = strings.Trim(action, "/") //截取
            c.String(http.StatusOK, name+" - "+action)
        })
  • URL parameters :

    • DefaultQuery(): parameter does not exist, return default value

       c.DefaultQuery("name", "枯藤")
    • Query(): The parameter does not exist, return empty
  • Form parameters : PostForm method

     types := c.DefaultPostForm("type", "post")
    username := c.PostForm("username")

upload files

  • multipart/form-data format for file upload
  • The gin file upload is similar to the native net/http method, the difference is that gin encapsulates the native request into c.Request

Upload a single file

 <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
      上传文件:<input type="file" name="file" >
      <input type="submit" value="提交">
</form>
 //限制上传最大尺寸
r.MaxMultipartMemory = 8 << 20
r.POST("/upload", func(c *gin.Context) {
    file, err := c.FormFile("file")
    if err != nil {
        c.String(500, "上传图片出错")
        return
    }
    // c.JSON(200, gin.H{"message": file.Header.Context})
    c.SaveUploadedFile(file, file.Filename)
    c.String(http.StatusOK, file.Filename)
})

upload limit

  • Restrict file types: headers.Header.Get("Content-Type")
  • Limit file size: headers.Size
 r.POST("/upload-limit", func(c *gin.Context) {
    _, headers, err := c.Request.FormFile("file")
    if err != nil {
        c.String(500, "上传图片出错")
    return
    }
    if headers.Size > 1024*1024*2 {
        c.String(500, "图片太大了")
        return
    }
    t := headers.Header.Get("Content-Type")
    if t != "image/jpeg" {
        c.String(500, "图片格式错误:"+t)
        return
    }
    // c.JSON(200, gin.H{"message": file.Header.Context})
    c.SaveUploadedFile(headers, "./upload/"+headers.Filename)
    c.String(http.StatusOK, headers.Filename)
})

Upload multiple files

 <form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
      上传文件:<input type="file" name="files" multiple>
      <input type="submit" value="提交">
</form>
 r.POST("/upload", func(c *gin.Context) {
   form, err := c.MultipartForm()
   if err != nil {
      c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
   }
   // 获取所有图片
   files := form.File["files"]
   // 遍历所有图片
   for _, file := range files {
      // 逐个存
      if err := c.SaveUploadedFile(file, file.Filename); err != nil {
         c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
         return
      }
   }
   c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
})

Group Group

 v1 := r.Group("/v1")
{
   v1.GET("/login", login)
   v1.GET("submit", submit)
}

Data parsing and binding

 type Login struct {
   // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
   User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
   Password string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

Json

 var json Login
// 将request的body中的数据,自动按照json格式解析到结构体
c.ShouldBindJSON(&json); //json.User json.Password

form

 <form action="http://localhost:8000/loginForm" method="post" enctype="application/x-www-form-urlencoded">
    用户名<input type="text" name="username"><br>
    密码<input type="password" name="password">
    <input type="submit" value="提交">
</form>
 var form Login
c.Bind(&form); // form.User form.Password

URI

 r.GET("/:user/:password", func(c *gin.Context) {
    var login Login
    c.ShouldBindUri(&login);// login.User login.Password
}

render

data rendering

  • JSON

     c.JSON(200, gin.H{"msg": "json"}) //{"msg":"json"}
  • XML

     c.XML(200, gin.H{"msg": "xml"}) //<map><msg>xml</msg></map>
  • YAML

     c.YAML(200, gin.H{"msg": "yaml"}) //文件下载 msg: yaml
  • ProtoBuf

     reps := []int64{int64(1), int64(2)}
    // 定义数据
    label := "label"
    // 传protobuf格式数据
    data := &protoexample.Test{
        Label: &label,
        Reps:  reps,
    }
    r.GET("/proto", func(c *gin.Context) {
        c.ProtoBuf(200, data) //文件下载 label
    })

HTML rendering

  • gin supports loading HTML templates, and then configures and returns the corresponding data according to the template parameters, which is essentially string replacement
  • LoadHTMLGlob() method can load template files
 r.LoadHTMLGlob("html/*.html")
r.GET("/html", func(c *gin.Context) {
    c.HTML(200, "index.html", gin.H{"name": "test"})
})

redirect

 //301重定向
c.Redirect(http.StatusMovedPermanently, "https://blog.itxiaoma.cn")

asynchronous

  • When starting a new goroutine, the original context should not be used, a read-only copy of it must be used
 copyContext := c.Copy() // Context副本
go func() {
   time.Sleep(3 * time.Second)
   log.Println("异步执行:" + copyContext.Request.URL.Path) //异步执行:/async
}()
c.String(200, "Sync...")

middleware

  • Middleware refers to special functions that can intercept the http request-response life cycle
  • Gin uses Logger(), Recovery() two global middleware by default

     //去除默认全局中间件
    r := gin.New()//不带中间件

global middleware

 func FullMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Set("request", "中间件")
    }
}
 r.Use(FullMiddleware())//使用中间件
r.GET("/middleware", func(c *gin.Context) {
   req, _ := c.Get("request")
   c.String(200, req.(string)) 
})

Next

  • The operation before c.Next() is executed before the Handler is executed; generally, the verification process is performed to check whether the access is allowed.
  • The operations after c.Next() are executed after the Handler is executed; generally, summary processing is performed, such as formatted output, response end time, and response duration calculation.
 func NextMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("中间件开始执行")
        c.Next()
        fmt.Println("中间件执行完毕")
    }
}

func NextResponse(r *gin.Engine) {
    r.Use(NextMiddleware())
    r.GET("/next", func(c *gin.Context) {
        fmt.Println("请求执行...")
        // 中间件开始执行
        // 请求执行...
        // 中间件执行完毕
    })
}

Abort

  • Indicates termination, that is, when Abort is executed, all subsequent middleware function calls will be stopped.
 func AbortMiddleware() gin.HandlerFunc {
   return func(c *gin.Context) {
      fmt.Println("中间件开始执行")
      if c.Query("key") == "abort" {
         c.String(200, "Abort")
         c.Abort()
      }
   }
}

func AbortResponse(r *gin.Engine) {
   r.Use(AbortMiddleware())
   r.GET("/abort", func(c *gin.Context) {
      fmt.Println("请求执行...")
      c.String(200, "OK")
   })
}

local middleware

 //局部中间件
func PartMiddleware() gin.HandlerFunc {
   return func(c *gin.Context) {
      fmt.Println("中间件开始执行")
   }
}

func PartResponse(r *gin.Engine) {
   r.GET("/part", PartMiddleware(), func(c *gin.Context) {
      fmt.Println("请求执行...")
      // 中间件开始执行
      // 请求执行...
   })
}

Middleware recommendation

  • RestGate - Secure authentication for REST API endpoints
  • staticbin - Middleware/handler for serving static files from binary data
  • gin-cors - Official middleware for CORS gin
  • gin-csrf - CSRF protection
  • gin-health - Middleware for reporting via gocraft/health
  • gin-merry - Pretty- printed error middleware with context
  • gin-revision - Revision middleware for the Gin framework
  • gin-jwt - JWT middleware for the Gin framework
  • gin-sessions - Session middleware based on mongodb and mysql
  • gin-location - Middleware for exposing a server's hostname and scheme
  • gin-nice-recovery - Emergency recovery middleware that lets you build better user experiences
  • gin-limit - Limit simultaneous requests; can help increase traffic
  • gin-limit-by-key - An in-memory middleware for rate limiting access by custom key and rate.
  • ez-gin-template - Simple template wrapper for gin
  • gin-hydra - gin middleware Hydra
  • gin-glog - Designed to replace Gin's default log
  • gin-gomonitor - For exposing metrics via Go-Monitor
  • gin-oauth2 - for OAuth2
  • Alternative static asset handler for the static gin framework.
  • xss-mw - XssMw is a middleware designed to "automatically remove XSS" from user submitted input
  • gin-helmet - Simple collection of security middleware.
  • gin-jwt-session - Provides middleware for JWT/Session/Flash, easy to use, while also providing necessary tuning options. Samples are also available.
  • gin-template - Easy-to-use html/template for the gin framework.
  • gin-redis-ip-limiter - IP address based request limiter. It can be used with redis and sliding window mechanism.
  • gin-method-override - _method method to be overridden by POST formal parameters, inspired by Ruby's rack of the same name
  • gin-access-limit - limit - Access control middleware by specifying allowed source CIDR notation.
  • gin-session - Session middleware for Gin
  • gin-stats - Lightweight and useful request metrics middleware
  • gin-statsd - Gin middleware that reports to the statsd daemon
  • gin-health-check - check-health check middleware for Gin
  • gin-session-middleware - An efficient, secure and easy-to-use Go Session library.
  • ginception - Pretty exceptions page
  • gin-inspector - Gin middleware for investigating http requests.
  • gin-dump - Gin middleware/handler to dump request and response headers/body. Very helpful for debugging applications.
  • go-gin-prometheus - Gin Prometheus metrics exporter
  • ginprom - Prometheus metrics exporter for Gin
  • gin-go-metrics - Gin middleware to gather and store metrics using rcrowley/go-metrics
  • ginrpc - Gin middleware/processor autobinding tool. Object registration is supported through annotation routes like beego

Original: https://github.com/gin-gonic/contrib/blob/master/README.md

session control

cookies

 func CookieHandler(r *gin.Engine) {
   r.GET("/cookie", func(c *gin.Context) {
      cookie, err := c.Cookie("test")
      if err != nil {
         c.SetCookie(
            "test",
            "value",
            60,          //maxAge int, 单位为秒
            "/",         //path,cookie所在目录
            "localhost", //domain string,域名
            false,       //secure 是否智能通过https访问
            true,        //httpOnly bool  是否允许别人通过js获取自己的cookie
         )
      }
      c.String(200, cookie)
   })
}

Cookie verification

 func AuthMiddleWare() gin.HandlerFunc {
   return func(c *gin.Context) {
      // 获取客户端cookie并校验
      if cookie, err := c.Cookie("abc"); err == nil {
         if cookie == "123" {
            c.Next()
            return
         }
      }
      // 返回错误
      c.JSON(http.StatusUnauthorized, gin.H{"error": "err"})
      // 若验证不通过,不再调用后续的函数处理
      c.Abort()
      return
   }
}

Session

Install

 go get github.com/gin-contrib/sessions

use

 var store = cookie.NewStore([]byte("secret"))

func SessionHandler(r *gin.Engine) {
   r.GET("/session",
      sessions.Sessions("mysession", store), //路由上加入session中间件
      func(c *gin.Context) {
         session := sessions.Default(c)
         if session.Get("name") != "itxiaoma" {
            session.Set("name", "itxiaoma")
            //记着调用save方法,写入session
            session.Save()
         }
         c.JSON(200, gin.H{"name": session.Get("name")}) //{"name":"itxiaoma"}
      })
}

parameter validation

Using the data verification of the gin framework, you can save the data without parsing the data and reduce the if else, which will be much simpler.

Struct Validation

 type Person struct {
   Name string `form:"name"`
}

func JsonHandler(r *gin.Engine) {
   r.GET("/structure", func(c *gin.Context) {
      var person Person
      c.ShouldBind(&person)
      c.String(200, fmt.Sprintf("%#v", person))
      //访问:http://localhost:8080/structure?name=xxx
      //输出:structure.Person{Name:"xxx"}
   })
}

custom validation

Customize the validation function for the parameters bound to the structure

Official website example: https://github.com/gin-gonic/gin#custom-validators

introduce

 go get github.com/go-playground/validator/v10

use:

 package validator

import (
    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
    "github.com/go-playground/validator/v10"
    "net/http"
)

type Person struct {
    Name string `form:"name" binding:"NotAdmin"` // 1、自定义注册名称
}

// 2、自定义校验方法
var notAdmin validator.Func = func(fl validator.FieldLevel) bool {
    name, ok := fl.Field().Interface().(string)
    if ok {
        return name != "admin"
    }
    return true
}

func MyValidatorHandler(r *gin.Engine) {
    // 3、将自定义的校验方法注册到 validator 中
    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
        // 这里的 key 和 fn 可以不一样最终在 struct 使用的是 key
        v.RegisterValidation("NotAdmin", notAdmin)
    }
    r.GET("/validator", func(c *gin.Context) {
        var person Person
        if e := c.ShouldBind(&person); e == nil {
            c.String(http.StatusOK, "%v", person)
        } else {
            c.String(http.StatusOK, "person bind err:%v", e.Error())
            //person bind err:Key: 'Person.Name' Error:Field validation for 'Name' failed on the 'NotAdmin' tag
        }
    })
}

Multipart/Urlencoded binding

 package main

import (
    "github.com/gin-gonic/gin"
)

type LoginForm struct {
    User     string `form:"user" binding:"required"`
    Password string `form:"password" binding:"required"`
}

func main() {
    router := gin.Default()
    router.POST("/login", func(c *gin.Context) {
        // 你可以使用显式绑定声明绑定 multipart form:
        // c.ShouldBindWith(&form, binding.Form)
        // 或者简单地使用 ShouldBind 方法自动绑定:
        var form LoginForm
        // 在这种情况下,将自动选择合适的绑定
        if c.ShouldBind(&form) == nil {
            if form.User == "user" && form.Password == "password" {
                c.JSON(200, gin.H{"status": "you are logged in"})
            } else {
                c.JSON(401, gin.H{"status": "unauthorized"})
            }
        }
    })
    router.Run(":8080")
}

test:

 curl -v --form user=user --form password=password http://localhost:8080/login

Other types: URL parameters

 c.ShouldBindWith(&p, binding.Query);

other

log

 f, _ := os.Create("log/gin.log")
gin.DefaultWriter = io.MultiWriter(f)
r = gin.Default()
r.GET("/log", func(c *gin.Context) {
   //同时将日志写入文件和控制台
   //gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
   c.String(200, "log ok")
})
r.Run()

verification code

Execute logic:

  1. First write the key-value pair (k->v) in the session, write the value on the image, then generate the image and display it on the browser
  2. The front-end sends the verification code to the back-end, and the back-end obtains v according to the k in the session, and compares and checks
 package captcha

import (
   "bytes"
   "github.com/dchest/captcha"
   "github.com/gin-contrib/sessions"
   "github.com/gin-contrib/sessions/cookie"
   "github.com/gin-gonic/gin"
   "net/http"
   "time"
)

var sessionMaxAge = 3600
var sessionSecret = "itxiaoma"

func SessionMiddleware(keyPairs string) gin.HandlerFunc {
   var store sessions.Store
   store = cookie.NewStore([]byte(sessionSecret))
   store.Options(sessions.Options{
      MaxAge: sessionMaxAge, //seconds
      Path:   "/",
   })
   return sessions.Sessions(keyPairs, store)
}

func Captcha(c *gin.Context, length ...int) {
   l := captcha.DefaultLen
   w, h := 107, 36
   if len(length) == 1 {
      l = length[0]
   }
   if len(length) == 2 {
      w = length[1]
   }
   if len(length) == 3 {
      h = length[2]
   }
   captchaId := captcha.NewLen(l)
   session := sessions.Default(c)
   session.Set("captcha", captchaId)
   _ = session.Save()
   _ = Serve(c.Writer, c.Request, captchaId, ".png", "zh", false, w, h)
}

func CaptchaVerify(c *gin.Context, code string) bool {
   session := sessions.Default(c)
   if captchaId := session.Get("captcha"); captchaId != nil {
      session.Delete("captcha")
      _ = session.Save()
      if captcha.VerifyString(captchaId.(string), code) {
         return true
      } else {
         return false
      }
   } else {
      return false
   }
}

func Serve(w http.ResponseWriter, r *http.Request, id, ext, lang string, download bool, width, height int) error {
   w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
   w.Header().Set("Pragma", "no-cache")
   w.Header().Set("Expires", "0")

   var content bytes.Buffer
   switch ext {
   case ".png":
      w.Header().Set("Content-Type", "image/png")
      _ = captcha.WriteImage(&content, id, width, height)
   case ".wav":
      w.Header().Set("Content-Type", "audio/x-wav")
      _ = captcha.WriteAudio(&content, id, lang)
   default:
      return captcha.ErrNotFound
   }

   if download {
      w.Header().Set("Content-Type", "application/octet-stream")
   }
   http.ServeContent(w, r, id+ext, time.Time{}, bytes.NewReader(content.Bytes()))
   return nil
}

func main() {
  r := gin.Default()
  r.LoadHTMLGlob("captcha/*.html")
  r.Use(SessionMiddleware("itxiaoma"))
  r.GET("/captcha", func(c *gin.Context) {
     Captcha(c, 4)
  })
  r.GET("/captcha-html", func(c *gin.Context) {
     c.HTML(http.StatusOK, "captcha.html", nil)
  })
  r.GET("/captcha/verify/:value", func(c *gin.Context) {
     value := c.Param("value")
     if CaptchaVerify(c, value) {
        c.JSON(http.StatusOK, gin.H{"status": 0, "msg": "success"})
     } else {
        c.JSON(http.StatusOK, gin.H{"status": 1, "msg": "failed"})
     }
  })
  r.Run()
}

JWT

 package jwt

import (
   "fmt"
   "github.com/dgrijalva/jwt-go"
   "github.com/gin-gonic/gin"
   "net/http"
   "time"
)

//自定义字符串
var jwtkey = []byte("itxiaoma")
var str string

type Claims struct {
   UserId uint
   jwt.StandardClaims
}

//颁发token
func setting(ctx *gin.Context) {
   expireTime := time.Now().Add(7 * 24 * time.Hour)
   claims := &Claims{
      UserId: 2,
      StandardClaims: jwt.StandardClaims{
         ExpiresAt: expireTime.Unix(), //过期时间
         IssuedAt:  time.Now().Unix(),
         Issuer:    "127.0.0.1",  // 签名颁发者
         Subject:   "user token", //签名主题
      },
   }
   token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
   // fmt.Println(token)
   tokenString, err := token.SignedString(jwtkey)
   if err != nil {
      fmt.Println(err)
   }
   str = tokenString
   ctx.JSON(200, gin.H{"token": tokenString})
}

//解析token
func getting(ctx *gin.Context) {
   tokenString := ctx.GetHeader("Authorization")
   fmt.Println(tokenString)
   //vcalidate token formate
   if tokenString == "" {
      ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足1"})
      ctx.Abort()
      return
   }

   token, claims, err := ParseToken(tokenString)
   if err != nil || !token.Valid {
      ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足2"})
      ctx.Abort()
      return
   }
   ctx.JSON(200, gin.H{"claims": claims})
}

func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
   Claims := &Claims{}
   token, err := jwt.ParseWithClaims(tokenString, Claims, func(token *jwt.Token) (i interface{}, err error) {
      return jwtkey, nil
   })
   return token, Claims, err
}

func main() {
     r := gin.Default()
   r.GET("/set-jwt", setting)
   r.GET("/get-jwt", getting)
   r.Run()
}

debugging

 curl http://localhost:8080/get-jwt -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjIsImV4cCI6MTY1NDA1NzQ1MCwiaWF0IjoxNjUzNDUyNjUwLCJpc3MiOiIxMjcuMC4wLjEiLCJzdWIiOiJ1c2VyIHRva2VuIn0.IN_Tj-M6CMHFlunnRIvUgog2GMDyWpj7iOsjwUeD0Sk"

Rights Management Casbin

Casbin is a powerful and efficient open source access control library for Golang projects.

Permissions actually control who can perform what operations on what resources .

Casbin abstracts the access control model into a configuration file (model file) based on the PERM (Policy, Effect, Request, Matchers) metamodel. So switching or updating the authorization mechanism simply requires modifying the configuration file.

Introduce:

 go get github.com/casbin/casbin/v2

Model file 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
  • request is an abstraction of an access request, which corresponds to the parameters of the e.Enforce() function one-to-one
  • Policy is the definition of policies or rules. It defines specific rules.
  • request is an abstraction of an access request. It has a one-to-one correspondence with the parameters of the e.Enforce() function. The matcher matcher will match the request with each defined policy one by one, and generate multiple matching results.
  • The effect decides whether to allow or deny the request by summing up all the results from applying the matcher to the request.

sub: access entity obj: access object act: access action

The above model file specifies that the permission is composed of three elements sub,obj,act , and the request can only be passed if there is an exact same policy as it in the policy list. The result of the matcher can be obtained by p.eft , some(where (p.eft == allow)) means that only one policy allows it.

File storage policy policy.csv

That is, who can perform what operations on what resources:

 p, dajun, data1, read
p, lizi, data2, write

文件的两行内容表示dajun data1read权限, lizi对数据data2write permissions

 func check(e *casbin.Enforcer, sub, obj, act string) {
  ok, _ := e.Enforce(sub, obj, act)
  if ok {
    fmt.Printf("%s CAN %s %s\n", sub, act, obj)
  } else {
    fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
  }
}

func main() {
  e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
  if err != nil {
    log.Fatalf("NewEnforecer failed:%v\n", err)
  }

  check(e, "dajun", "data1", "read") //dajun CAN read data1
  check(e, "lizi", "data2", "write") //lizi CAN write data2
  check(e, "dajun", "data1", "write")//dajun CANNOT write data1
  check(e, "dajun", "data2", "read") //dajun CANNOT read data2
}

Super administrator:

 [matchers]
e = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"

check

 check(e, "root", "data1", "read")
check(e, "root", "data2", "write")
check(e, "root", "data1", "execute")
check(e, "root", "data3", "rwx")

RBAC model

Model file:

 [role_definition]
g = _, _

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

g = _,_ defines the mapping relationship of user-role, role-role, the former is a member of the latter and has the permissions of the latter.

g(r.sub, p.sub) is used to determine whether the request subject r.sub belongs to the role of p.sub .

Gorm Adapter

data:

 CREATE DATABASE IF NOT EXISTS casbin;

USE casbin;

CREATE TABLE IF NOT EXISTS casbin_rule (
  p_type VARCHAR(100) NOT NULL,
  v0 VARCHAR(100),
  v1 VARCHAR(100),
  v2 VARCHAR(100),
  v3 VARCHAR(100),
  v4 VARCHAR(100),
  v5 VARCHAR(100)
);

INSERT INTO casbin_rule VALUES
('p', 'dajun', 'data1', 'read', '', '', ''),
('p', 'lizi', 'data2', 'write', '', '', '');

Gorm Adapter 15301f0d4d60a2d929e3463a6c6614bc---加载policyGorm Adapter casbin库中的casbin_rule表:

 package main

import (
  "fmt"

  "github.com/casbin/casbin/v2"
  gormadapter "github.com/casbin/gorm-adapter/v2"
  _ "github.com/go-sql-driver/mysql"
)

func check(e *casbin.Enforcer, sub, obj, act string) {
  ok, _ := e.Enforce(sub, obj, act)
  if ok {
    fmt.Printf("%s CAN %s %s\n", sub, act, obj)
  } else {
    fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
  }
}

func main() {
  a, _ := gormadapter.NewAdapter("mysql", "root:12345@tcp(127.0.0.1:3306)/")
  e, _ := casbin.NewEnforcer("./model.conf", a)

  check(e, "dajun", "data1", "read")
  check(e, "lizi", "data2", "write")
  check(e, "dajun", "data1", "write")
  check(e, "dajun", "data2", "read")
}

run:

 dajun CAN read data1
lizi CAN write data2
dajun CANNOT write data1
dajun CANNOT read data2

other

Gin Chinese documentation: https://learnku.com/docs/gin-gonic/1.7

Reference

gin frame

Gin Framework Chinese Documentation

Use session in gin

Gin+Gorm+Casbin implements permission control demo

Go daily library casbin


IT小马
1.2k 声望166 粉丝

Php - Go - Vue - 云原生