JWT-GO中的Golang基于角色的访问控制
#go #rbac #devsecops #apisec

作为安全工程师,在测试Web或移动应用程序时,您检查的第一件事是什么?如果您想先选择低悬挂的水果,则访问控制错误配置必须位于列表的顶部。

令人惊讶的是,访问控制是应用程序开发人员认为很简单的组件之一,但最终花费了很多时间,没有最终用户的价值,甚至更糟的结局都做得不好,大量将应用程序暴露于安全风险中。身份验证和授权问题与普通API安全风险的OWASP Top 10 - 2023列表中的前两个职位相关并非偶然。

因此,在本文中,我将说明基于角色的访问控制的应用;企业API的简单但安全的访问控制方法。

基于角色的访问控制(RBAC)
Role-based Access Control是一种限制基于用户作业或角色对信息系统的访问的机制。访问数据是通过附加到不同用户角色的权限和特权来限制的。

让我们构建
到本文结束时,我们将建立一个简单的房间,以使用Go预订API,该房间将允许访客查看可用的房间以及注册,登录和预订房间。还将有一个管理员将权限映射用户用于权限以及添加和更新房间信息。

Image description

先决条件

  • GO的基本知识
  • Patience 😄

源代码

$ git clone https://github.com/bensonmacharia/jwt-go-rbac.git

步骤1:设置环境

  • 获得您最喜欢的IDE,我的是vscode
  • 下载并安装Go
  • 确认已安装的GO版本
$ go version    
go version go1.19.4 darwin/arm64
// confirm installation by checking version
$ mysql --version
mysql  Ver 8.0.32 for macos12.6 on arm64 (Homebrew)

// test connection
$ mysql -u bmacharia -p
Enter password: 
mysql>
  • 创建一个数据库
// create database
$ mysql> CREATE DATABASE jwt_go_rbac;
Query OK, 1 row affected (0.07 sec)

// confirm database creation
$ mysql> show databases;
+-------------------------+
| Database                |
+-------------------------+             |
| information_schema      |
| jwt_go_rbac             |
| mysql                   |
| performance_schema      |
| sys                     |
+-------------------------+
5 rows in set (0.01 sec)

步骤2:设置项目

  • 创建项目文件夹和初始GO项目
$ mkdir jwt-go-rbac                                                                
$ cd jwt-go-rbac            
$ go mod init bmacharia/jwt-go-rbac            
go: creating new go.mod: module bmacharia/jwt-go-rbac
  • 创建main.go和.env文件
// main go application file
$ touch main.go

// environment variables configuration file
$ touch .env
  • 在您的IDE中打开项目,并在下面编辑.env文件

步骤3:配置数据库连接

  • 安装所需软件包
// package to load .env file
$ go get github.com/joho/godotenv
// package to allow connection to MySQL database
$ go get -u gorm.io/driver/mysql
// a simple Go HTTP web framework
$ go get github.com/gin-gonic/gin
// ORM (Object Relational Mapping) library for Go
$ go get -u gorm.io/gorm
// jwt-go package
$ go get -u github.com/golang-jwt/jwt/v5
  • 创建一个数据库连接文件
// create a database folder and a file inside it
$ mkdir database
$ touch database/database.go
  • 如下编辑数据库。文件。我们正在使用GORM启动与MySQL数据库的连接。
  • 编辑main.go文件以测试数据库连接。在此文件中,我们正在加载.env文件,数据库连接并在端口8000上启动Gin Web服务器

  • 通过运行go run命令测试数据库连接

$ go run main.go
2023/05/01 23:25:13 .env file loaded successfully
2023/05/01 23:25:13 `Successfully connected to the database
[GIN-debug] Listening and serving HTTP on :8000

步骤4:创建数据库模型

  • 创建用户和榜样。用户模型定义了用户对象详细信息以及数据库中的用户属性。另一方面,榜样详细介绍了要分配给每个用户的角色的属性。
// create a user.go file inside the model directory
$ mkdir model
$ touch model/user.go
$ touch model/role.go
  • 模型/用户。
  • 模型/角色。
  • 运行数据库迁移
//edit main.go file to add automigration script
...
// run database migrations and add seed data
func loadDatabase() {
    database.InitDb()
    database.Db.AutoMigrate(&model.Role{})
    database.Db.AutoMigrate(&model.User{})
    seedData()
}

// load seed data into the database
func seedData() {
    var roles = []model.Role{{Name: "admin", Description: "Administrator role"}, {Name: "customer", Description: "Authenticated customer role"}, {Name: "anonymous", Description: "Unauthenticated customer role"}}
    var user = []model.User{{Username: os.Getenv("ADMIN_USERNAME"), Email: os.Getenv("ADMIN_EMAIL"), Password: os.Getenv("ADMIN_PASSWORD"), RoleID: 1}}
    database.Db.Save(&roles)
    database.Db.Save(&user)
}
// run migration
$ go run main.go
..
  • 确认已经创建了用户和角色表
mysql> show tables;
+-----------------------+
| Tables_in_jwt_go_rbac |
+-----------------------+
| roles                 |
| users                 |
+-----------------------+
2 rows in set (0.01 sec)
mysql> desc users;
mysql> desc roles;
mysql> select * from roles;
mysql> select * from users;
  • 创建用于用户注册,登录和更新的模型
$ touch model/register.go
$ touch model/login.go
$ touch model/update.go
  • 模型/寄存器。
  • 模型/登录
  • 模型/更新。

步骤5:创建控制器与数据库内容交互

  • 创建角色和用户控制器文件
$ touch controller/role.go
$ touch controller/user.go
  • 控制器/角色。
  • 控制器/user.go
  • 添加注册和登录路线
// edit main.go
func serveApplication() {
    router := gin.Default()
    authRoutes := router.Group("/auth/user")
        // registration route
    authRoutes.POST("/register", controller.Register)
        // login route
    authRoutes.POST("/login", controller.Login)

    router.Run(":8000")
    fmt.Println("Server running on port 8000")
}
  • 测试用户注册和登录
// run the application
$ go run main.go
// register user
$ curl -X POST http://localhost:8000/auth/user/register \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -d '{"username": "test","email":"test@bmacharia.com","password":"super^Secret!007"}'
// test user login
$ curl -X POST http://localhost:8000/auth/user/login \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -d '{"username": "test","password":"super^Secret!007"}' 

步骤6.添加JWT实用程序以创建令牌和验证

  • 要创建一个基于角色的安全身份验证方案,我们需要在用户身份验证时生成唯一的令牌。然后将其用于跟踪其分配的角色,因为他们消耗了可用的资源。在此项目中,我们将使用jwt-go软件包生成一个JWT代币,该令牌将封装用户详细信息,分配的角色和权限。
  • 创建JWT实用程序文件
// create a util directory
$ mkdir util
$ touch jwt.go
$ touch jwtAuth.go
  • jwt.go
  • jwtauth.go

  • 将令牌添加到用户登录响应

// edit user controller and append 
func Login(context *gin.Context) {
     jwt, err := util.GenerateJWT(user)
     if err != nil {
        context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    context.JSON(http.StatusOK, gin.H{"token": jwt, "username": input.Username, "message": "Successfully logged in"})
}
  • 添加管理功能路由
// edit main.go
func serveApplication() {
        adminRoutes := router.Group("/admin")
    adminRoutes.Use(util.JWTAuth())
    adminRoutes.GET("/users", controller.GetUsers)
    adminRoutes.GET("/user/:id", controller.GetUser)
    adminRoutes.PUT("/user/:id", controller.UpdateUser)
    adminRoutes.POST("/user/role", controller.CreateRole)
    adminRoutes.GET("/user/roles", controller.GetRoles)
    adminRoutes.PUT("/user/role/:id", controller.UpdateRole)
}

步骤7:运行和测试admin funtions

  • Run API
$  go run main.go
  • 管理员登录
// admin user login
$ curl -X POST http://localhost:8000/auth/user/login \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -d '{"username": "test","password":"super^Secret!007"}' 
  • 获取所有用户
// use admin token from login response
$ curl -X GET http://localhost:8000/admin/users \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODQ0NDc1ODQsImlhdCI6MTY4NDQ0NTc4NCwiaWQiOjEsInJvbGUiOjF9.CKQf2GggCP1cnGqfTp_2R77Q7GsQBX_dxf5PSLEbTx8" \
     -d '{"username": "test","password":"super^Secret!007"}'
  • 通过ID获取用户
$ curl -X GET http://localhost:8000/admin/user/1 \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODQ0NDc1ODQsImlhdCI6MTY4NDQ0NTc4NCwiaWQiOjEsInJvbGUiOjF9.CKQf2GggCP1cnGqfTp_2R77Q7GsQBX_dxf5PSLEbTx8"
  • 更新用户
$ curl -X PUT http://localhost:8000/admin/user/2 \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODQ0NDc1ODQsImlhdCI6MTY4NDQ0NTc4NCwiaWQiOjEsInJvbGUiOjF9.CKQf2GggCP1cnGqfTp_2R77Q7GsQBX_dxf5PSLEbTx8" \
     -d '{"username": "test","email":"test@gmail.com","role_id":"2"}'
  • 创造角色
$ curl -X POST http://localhost:8000/admin/user/role \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODQ0NDc1ODQsImlhdCI6MTY4NDQ0NTc4NCwiaWQiOjEsInJvbGUiOjF9.CKQf2GggCP1cnGqfTp_2R77Q7GsQBX_dxf5PSLEbTx8" \
     -d '{"name": "testing","description":"Test user role"}'
  • 扮演所有角色
$ curl -X GET http://localhost:8000/admin/user/roles \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODQ0NDc1ODQsImlhdCI6MTY4NDQ0NTc4NCwiaWQiOjEsInJvbGUiOjF9.CKQf2GggCP1cnGqfTp_2R77Q7GsQBX_dxf5PSLEbTx8"
  • 更新角色
$ curl -X PUT http://localhost:8000/admin/user/role/4 \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODQ0NDc1ODQsImlhdCI6MTY4NDQ0NTc4NCwiaWQiOjEsInJvbGUiOjF9.CKQf2GggCP1cnGqfTp_2R77Q7GsQBX_dxf5PSLEbTx8" \
     -d '{"name":"accountant","description":"Accountant user role"}'
  • 添加房间
$ curl -X POST http://localhost:8000/admin/room/add \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODQ0NDc1ODQsImlhdCI6MTY4NDQ0NTc4NCwiaWQiOjEsInJvbGUiOjF9.CKQf2GggCP1cnGqfTp_2R77Q7GsQBX_dxf5PSLEbTx8" \
     -d '{"name": "Room 9","location":"Second Floor"}'
  • 列出所有房间
$ curl -X GET http://localhost:8000/api/view/rooms \
     -H "Content-Type: application/json" \
     -H "Accept: application/json"
  • 通过ID获取空间
$ curl -X GET http://localhost:8000/api/view/room/3 \
     -H "Content-Type: application/json" \
     -H "Accept: application/json"

步骤8:测试房间预订服务

  • 预订房间
$ curl -X POST http://localhost:8000/api/room/book \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODUyMjk0MDEsImlhdCI6MTY4NTIyNzYwMSwiaWQiOjI0LCJyb2xlIjoyfQ.h8R51DA5N_xeCa8xR1HLeOo4JTmIGjUp3oMPJLuBv3g" \
     -d '{"room_id": 3}'
  • 列出所有预订
$ curl -X GET http://localhost:8000/admin/room/bookings \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODcyOTkyMTAsImlhdCI6MTY4NzI5NzQxMCwiaWQiOjEsInJvbGUiOjF9.3oztz8EgE-l3byKWzCI760FE-BmRY7B-BohnYydDElc" \
     -H "Accept: application/json"
  • 列出所有用户预订
$ curl -X GET http://localhost:8000/api/rooms/booked \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlYXQiOjE2ODUyMjk0MDEsImlhdCI6MTY4NTIyNzYwMSwiaWQiOjI0LCJyb2xlIjoyfQ.h8R51DA5N_xeCa8xR1HLeOo4JTmIGjUp3oMPJLuBv3g" \
     -H "Accept: application/json"

结束
RBAC介绍了最简单的访问控制形式,可以帮助防止未经授权访问数据。这有助于遵守各种监管和合规性要求,尤其是与数据保护,隐私和系统访问有关的要求。使用RBAC,也很容易维护所有用户活动的审核跟踪,这可以大大帮助加快事件响应过程。

让我们连接
LinkedIn
Twitter
Blog