中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
使用 Casbin 在 Golang 項(xiàng)目中授權(quán)

你好開(kāi)發(fā)者????。在本文中,我們將在 Golang(GIN+GORM) 項(xiàng)目中實(shí)現(xiàn) Casbin 和 JWT 身份驗(yàn)證。

在閱讀本文之前,我強(qiáng)烈建議您查看另一篇我寫(xiě)的關(guān)于 casbin 基礎(chǔ)知識(shí)及其不同模型配置的文章。

執(zhí)行

我想事先展示文件夾結(jié)構(gòu),以便于理解。如果您在某個(gè)地方迷路了,可以查看我的 GitHub 存儲(chǔ)庫(kù)以獲取完整代碼。

文件夾結(jié)構(gòu)

RBAC 模型 (config/rbac_model.conf)

如果您對(duì) casbin 中的模型一無(wú)所知,請(qǐng)查看上一篇文章。你會(huì)在那里找到詳細(xì)的解釋。我將在本文中使用 RBAC 模型。

[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

數(shù)據(jù)庫(kù)

我們將使用GORM來(lái)訪問(wèn)數(shù)據(jù)庫(kù)。以下代碼只是創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)連接并返回該連接,該連接將在以下部分的代碼的其他部分中使用。

//DBConnection -> return db instancefunc DBConnection() (*gorm.DB, error) {    USER := "root"    PASS := "root"    HOST := "localhost"    PORT := "3306"    DBNAME := "casbin-golang"
newLogger := logger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer logger.Config{ SlowThreshold: time.Second, // Slow SQL threshold LogLevel: logger.Info, // Log level Colorful: true, // Disable color }, )
url := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", USER, PASS, HOST, PORT, DBNAME)
return gorm.Open(mysql.Open(url), &gorm.Config{Logger: newLogger})}

路由

這是連接 casbin、我們的自定義中間件和處理程序方法(我們將在以下部分中創(chuàng)建)的文章的主要部分

//SetupRoutes : all the routes are defined herefunc SetupRoutes(db *gorm.DB) {    httpRouter := gin.Default()
// Initialize casbin adapter adapter, err := gormadapter.NewAdapterByDB(db) if err != nil { panic(fmt.Sprintf("failed to initialize casbin adapter: %v", err)) }
// Load model configuration file and policy store adapter enforcer, err := casbin.NewEnforcer("config/rbac_model.conf", adapter) if err != nil { panic(fmt.Sprintf("failed to create casbin enforcer: %v", err)) }
//add policy if hasPolicy := enforcer.HasPolicy("doctor", "report", "read"); !hasPolicy { enforcer.AddPolicy("doctor", "report", "read") } if hasPolicy := enforcer.HasPolicy("doctor", "report", "write"); !hasPolicy { enforcer.AddPolicy("doctor", "report", "write") } if hasPolicy := enforcer.HasPolicy("patient", "report", "read"); !hasPolicy { enforcer.AddPolicy("patient", "report", "read") }
userRepository := repository.NewUserRepository(db)
if err := userRepository.Migrate(); err != nil { log.Fatal("User migrate err", err) }
userController := controller.NewUserController(userRepository)
apiRoutes := httpRouter.Group("/api")
{ apiRoutes.POST("/register", userController.AddUser(enforcer)) apiRoutes.POST("/signin", userController.SignInUser) }
userProtectedRoutes := apiRoutes.Group("/users", middleware.AuthorizeJWT()) { userProtectedRoutes.GET("/", middleware.Authorize("report", "read", enforcer), userController.GetAllUser) userProtectedRoutes.GET("/:user", middleware.Authorize("report", "read", enforcer), userController.GetUser) userProtectedRoutes.PUT("/:user", middleware.Authorize("report", "write", enforcer), userController.UpdateUser) userProtectedRoutes.DELETE("/:user", middleware.Authorize("report", "write", enforcer), userController.DeleteUser) }
httpRouter.Run()}

讓我簡(jiǎn)單解釋一下上面的代碼

  • [第 3 行]:這里我們?cè)O(shè)置默認(rèn)的 Gin Router

  • [第 6 行]:這里我們?yōu)镃asbin設(shè)置了Gorm適配器。使用此適配器,Casbin 可以從 Gorm 支持的數(shù)據(jù)庫(kù)加載策略或?qū)⒉呗员4娴狡渲小_@還將創(chuàng)建一個(gè)名為casbin_rule

  • [第 12 行]:在這里,我們通過(guò)提供我們的模態(tài)(上面構(gòu)造的 RBAC)和 gorm 適配器(來(lái)自第 6 行)作為參數(shù)來(lái)構(gòu)造一個(gè) casbin 執(zhí)行器。

  • [第 17 - 26 行]:這里我們將應(yīng)用程序所需的策略加載到數(shù)據(jù)庫(kù)中。這是一次性操作。所以最好用一些獨(dú)立于主程序流程的命令來(lái)添加策略。但為簡(jiǎn)單起見(jiàn),我在主程序流本身中添加了策略 (enforcer.AddPolicy),但前提是確保數(shù)據(jù)庫(kù)中不存在該策略 (enforcer.HasPolicy)。

  • [第 44–49 行]:這里我們使用 casbin 中間件(我們將在稍后構(gòu)建)保護(hù)各個(gè)路由。

實(shí)用程序

我想預(yù)先展示一些實(shí)用函數(shù),這些函數(shù)將在下面代碼的不同部分中使用。所有這些都非常簡(jiǎn)單。

func HashPassword(pass *string) {    bytePass := []byte(*pass)    hPass, _ := bcrypt.GenerateFromPassword(bytePass, bcrypt.DefaultCost)    *pass = string(hPass)}
func ComparePassword(dbPass, pass string) bool { return bcrypt.CompareHashAndPassword([]byte(dbPass), []byte(pass)) == nil}
//GenerateToken -> generates tokenfunc GenerateToken(userid uint) string { claims := jwt.MapClaims{ "exp": time.Now().Add(time.Hour * 3).Unix(), "iat": time.Now().Unix(), "userID": userid, }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) t, _ := token.SignedString([]byte(os.Getenv("JWT_SECRET"))) return t
}
//ValidateToken --> validate the given tokenfunc ValidateToken(token string) (*jwt.Token, error) {
//2nd arg function return secret key after checking if the signing method is HMAC and returned key is used by 'Parse' to decode the token) return jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { //nil secret key return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return []byte(os.Getenv("JWT_SECRET")), nil })}

中間件

JWT認(rèn)證

我不會(huì)在本文中詳細(xì)介紹身份驗(yàn)證。您可以使用任何身份驗(yàn)證模塊,如 JWT、Firebase 身份驗(yàn)證、AWS Cognito。我將在這里展示 JWT 代碼。

//AuthorizeJWT -> to authorize JWT Tokenfunc AuthorizeJWT() gin.HandlerFunc {    return func(ctx *gin.Context) {        const BearerSchema string = "Bearer "        authHeader := ctx.GetHeader("Authorization")        if authHeader == "" {            ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{                "error": "No Authorization header found"})        }        tokenString := authHeader[len(BearerSchema):]
if token, err := utils.ValidateToken(tokenString); err != nil { ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ "error": "Not Valid Token"}) } else { if claims, ok := token.Claims.(jwt.MapClaims); !ok { ctx.AbortWithStatus(http.StatusUnauthorized) } else { if token.Valid { ctx.Set("userID", claims["userID"]) } else { ctx.AbortWithStatus(http.StatusUnauthorized) } } } }}

上面的代碼是一個(gè)中間件,用于確保用戶是否擁有有效的令牌。

我在這里唯一想強(qiáng)調(diào)的是我們userID在從令牌檢索的上下文 [第 22 行] 中進(jìn)行設(shè)置。這userID將用于稍后的授權(quán)。

CASBIN中間件

該中間件的目的是確保用戶是否具有正確的權(quán)限或是否有權(quán)執(zhí)行某項(xiàng)任務(wù)。

// Authorize determines if current user has been authorized to take an action on an object.func Authorize(obj string, act string, enforcer *casbin.Enforcer) gin.HandlerFunc {    return func(c *gin.Context) {        // Get current user/subject        sub, existed := c.Get("userID")        if !existed {            c.AbortWithStatusJSON(401, gin.H{"msg": "User hasn't logged in yet"})            return        }
// Load policy from Database err := enforcer.LoadPolicy() if err != nil { c.AbortWithStatusJSON(500, gin.H{"msg": "Failed to load policy from DB"}) return }
// Casbin enforces policy ok, err := enforcer.Enforce(fmt.Sprint(sub), obj, act)
if err != nil { c.AbortWithStatusJSON(500, gin.H{"msg": "Error occurred when authorizing user"}) return }
if !ok { c.AbortWithStatusJSON(403, gin.H{"msg": "You are not authorized"}) return } c.Next() }}

Authorize方法接受 3 個(gè)參數(shù),即obj,actenforcer。obj是他試圖訪問(wèn)的資源。act是他試圖對(duì)該資源執(zhí)行的操作。enforcer就是我們?cè)谏厦媛酚刹糠殖跏蓟腸asbin Enforcer。

  • [第 5 行]:這里我們從上下文中檢索subwhichuserID以了解誰(shuí)正在嘗試對(duì)資源執(zhí)行特定操作

  • [第 12 行]:這里我們加載數(shù)據(jù)庫(kù)中所有可用的策略(我們?cè)谏厦娴穆酚刹糠痔砑拥模?/p>

  • [第 19 行]:這里我們使用 casbinenforce方法來(lái)確定用戶是應(yīng)該被授予訪問(wèn)權(quán)限還是應(yīng)該被拒絕。

用戶模型

這是我們將用于注冊(cè)/登錄過(guò)程的模型[在后面的部分]。這里role只是為了從前端檢索用戶的角色。它不會(huì)存儲(chǔ)在數(shù)據(jù)庫(kù)中。因此gorm:”-”使用

//User -> model for users tabletype User struct {    gorm.Model    Name     string `json:"name" `    Email    string `json:"email"  gorm:"unique"`    Role     string `json:"role" gorm:"-"`    Password string `json:"password" `}
//TableName --> Table for Product Modelfunc (User) TableName() string { return "users"}

注冊(cè)

此方法的目的是注冊(cè)用戶。

//AddUser - Register a userfunc (h userController) AddUser(enforcer *casbin.Enforcer) gin.HandlerFunc {    return func(ctx *gin.Context) {        var user model.User        if err := ctx.ShouldBindJSON(&user); err != nil {            ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})            return        }        utils.HashPassword(&user.Password)                user, err := h.userRepo.AddUser(user)        if err != nil {            ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})            return
} enforcer.AddGroupingPolicy(fmt.Sprint(user.ID), user.Role) user.Password = "" ctx.JSON(http.StatusOK, user) }}
  • [第 4–8 行]:我們正在從前端檢索用戶信息

  • [第 11–16 行]:我們將檢索到的用戶信息添加到數(shù)據(jù)庫(kù)中

  • [第 17 行]:我們正在使用方法將用戶角色添加到casbin_rule表中enforcer.AddGroupingPolicy

當(dāng)我們打POST /register

前端請(qǐng)求

用戶表

Johnwith id8被添加到users表中。

Casbin表

user_id 為 8(John) (v0) 和 role 為 doctor (v1) 的角色添加到 casbin_rule 表中。

登錄

這個(gè)方法的目的是讓用戶能夠登錄我們的系統(tǒng)。

func (h userController) SignInUser(ctx *gin.Context) {    var user model.User    if err := ctx.ShouldBindJSON(&user); err != nil {        ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})    }
dbUser, err := h.userRepo.GetByEmail(user.Email) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"msg": "No Such User Found"}) return
} if isTrue := utils.ComparePassword(dbUser.Password, user.Password); isTrue { token := utils.GenerateToken(dbUser.ID) ctx.JSON(http.StatusOK, gin.H{"msg": "Successfully SignIN", "token": token}) return } ctx.JSON(http.StatusInternalServerError, gin.H{"msg": "Password not matched"}) return}

上面的代碼檢索登錄信息[第 2-5 行] 并檢查該電子郵件是否在數(shù)據(jù)庫(kù)中可用[第 7-12 行] 并驗(yàn)證密碼是否正確[第 13-18 行]。

主程序

func main() {    db, _ := model.DBConnection()    route.SetupRoutes(db)}

運(yùn)行項(xiàng)目

如果具有patient角色的用戶試圖訪問(wèn)PUT users/:user即試圖更新用戶,他將不會(huì)被允許這樣做。

結(jié)論

如果你對(duì)任何部分感到困惑,你可以查看我的 Github 項(xiàng)目。

我們對(duì) casbin 的理解和在 golang 項(xiàng)目上實(shí)現(xiàn) casbin 的短暫旅程到此結(jié)束。希望這對(duì)您的項(xiàng)目有所幫助。任何類型的建議都將不勝感激??鞓?lè)編碼。

如果你喜歡我的文章,點(diǎn)贊,關(guān)注,轉(zhuǎn)發(fā)!

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
在Gin下實(shí)現(xiàn)簡(jiǎn)單的注冊(cè)登錄和狀態(tài)管理
[系列] Go - 常用簽名算法的基準(zhǔn)測(cè)試
整潔架構(gòu)的正確之路
【Go微服務(wù)開(kāi)發(fā)】gin+micro v4+rabbitmq+etcd 重構(gòu)備忘錄
Golang工程經(jīng)驗(yàn)
通過(guò)Consul Raft庫(kù)打造自己的分布式系統(tǒng)
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服