当前位置: 代码网 > it编程>数据库>Redis > Redis实现RBAC权限管理

Redis实现RBAC权限管理

2025年03月10日 Redis 我要评论
1. 什么是 rbac?rbac(role-based access control,基于角色的访问控制)是一种常见的权限管理模型,它通过用户(user)、角色(role)、权限(permission

1. 什么是 rbac?

rbac(role-based access control,基于角色的访问控制)是一种常见的权限管理模型,它通过用户(user)、角色(role)、权限(permission) 及其映射关系来控制访问权限。rbac 的基本思路是:

  • 用户被分配一个或多个角色;
  • 每个角色拥有一定的权限;
  • 通过用户所属角色来决定其是否有权限访问某个资源。

2. 为什么使用 redis 实现 rbac?

在传统的 rbac 设计中,权限数据通常存储在 数据库(如 mysql),但这种方式可能存在以下问题:

  • 查询性能低:每次鉴权都需要查询多张表,影响 api 响应速度;
  • 不适用于高并发:数据库连接池有限,在高并发场景下可能成为瓶颈;
  • 权限变更不灵活:数据库方案通常需要定期同步缓存,否则变更不会立即生效。

使用 redis 作为 rbac 权限存储的优势:

  • 高性能:redis 作为内存数据库,查询速度极快;
  • 低延迟:可以直接 o(1) 查询权限数据,而无需复杂的 sql 语句;
  • 支持动态权限变更:用户权限变更可以实时生效,而不需要等待数据库更新;
  • 适用于分布式系统:多台服务器可以共享 redis 权限数据,避免不同实例状态不一致的问题。

3. 设计 rbac 数据结构

我们使用 redis 作为权限存储,并设计以下 key 结构:

keyvalue说明
user_roles:{user_id}["admin", "editor"]用户的角色列表
role_permissions:{role}["read", "write", "delete"]角色的权限列表
permission_routes:{permission}["get:/users", "post:/articles"]权限对应的 api
blacklist_tokens存储已注销的 token使 jwt 失效,支持主动登出

4. 代码实现

我们使用 gin 作为 web 框架,并结合 redis 进行权限管理。

📌 4.1 安装依赖

go get -u github.com/gin-gonic/gin
go get -u github.com/golang-jwt/jwt/v5
go get -u github.com/redis/go-redis/v9

📌 4.2 初始化 redis

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/redis/go-redis/v9"
)

// 初始化 redis 客户端
var ctx = context.background()
var redisclient = redis.newclient(&redis.options{
	addr: "127.0.0.1:6379", // 连接 redis
})

// 初始化 rbac 角色 & 权限映射
func setuprbac() {
	// 角色 → 权限
	redisclient.sadd(ctx, "role_permissions:admin", "read", "write", "delete")
	redisclient.sadd(ctx, "role_permissions:editor", "read", "write")
	redisclient.sadd(ctx, "role_permissions:viewer", "read")

	// 权限 → api
	redisclient.sadd(ctx, "permission_routes:read", "get:/users", "get:/articles")
	redisclient.sadd(ctx, "permission_routes:write", "post:/articles", "put:/articles")
	redisclient.sadd(ctx, "permission_routes:delete", "delete:/articles")

	// 用户 → 角色
	redisclient.sadd(ctx, "user_roles:1", "admin")
	redisclient.sadd(ctx, "user_roles:2", "editor")
	redisclient.sadd(ctx, "user_roles:3", "viewer")

	log.println("rbac 权限映射初始化完成")
}

📌 4.3 生成 jwt 令牌

package main

import (
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

// jwt 密钥
var jwtsecret = []byte("supersecretkey")

// 生成 jwt 令牌
func generatejwt(userid int) (string, error) {
	token := jwt.newwithclaims(jwt.signingmethodhs256, jwt.mapclaims{
		"user_id": userid,
		"exp":     time.now().add(24 * time.hour).unix(), // 24 小时有效
	})
	return token.signedstring(jwtsecret)
}

// 解析 jwt 令牌
func parsejwt(tokenstring string) (int, error) {
	token, err := jwt.parse(tokenstring, func(token *jwt.token) (interface{}, error) {
		return jwtsecret, nil
	})
	if err != nil || !token.valid {
		return 0, fmt.errorf("invalid token")
	}

	claims, ok := token.claims.(jwt.mapclaims)
	if !ok {
		return 0, fmt.errorf("invalid claims")
	}

	return int(claims["user_id"].(float64)), nil
}

📌 4.4 鉴权中间件

// 访问权限检查
func hasaccess(userid int, method, path string) bool {
	// 1. 获取用户角色
	roles, err := redisclient.smembers(ctx, fmt.sprintf("user_roles:%d", userid)).result()
	if err != nil || len(roles) == 0 {
		return false
	}

	// 2. 遍历角色,获取权限
	for _, role := range roles {
		permissions, _ := redisclient.smembers(ctx, fmt.sprintf("role_permissions:%s", role)).result()
		for _, permission := range permissions {
			routes, _ := redisclient.smembers(ctx, fmt.sprintf("permission_routes:%s", permission)).result()
			for _, route := range routes {
				if route == fmt.sprintf("%s:%s", method, path) {
					return true
				}
			}
		}
	}

	return false
}

// rbac 中间件
func rbacmiddleware() gin.handlerfunc {
	return func(c *gin.context) {
		tokenstring := c.getheader("authorization")
		if tokenstring == "" {
			c.json(401, gin.h{"error": "未提供 token"})
			c.abort()
			return
		}

		// 解析 jwt
		userid, err := parsejwt(tokenstring)
		if err != nil {
			c.json(401, gin.h{"error": "token 无效"})
			c.abort()
			return
		}

		// 检查权限
		if !hasaccess(userid, c.request.method, c.fullpath()) {
			c.json(403, gin.h{"error": "无访问权限"})
			c.abort()
			return
		}

		c.set("userid", userid)
		c.next()
	}
}

📌 4.5 api 接口

func main() {
	r := gin.default()
	setuprbac()

	// 登录
	r.post("/login", func(c *gin.context) {
		userid := 1 // 假设用户 1 登录
		token, _ := generatejwt(userid)
		c.json(200, gin.h{"token": token})
	})

	// 受保护 api
	api := r.group("/api", rbacmiddleware())

	api.get("/users", func(c *gin.context) {
		c.json(200, gin.h{"message": "获取用户列表"})
	})

	api.post("/articles", func(c *gin.context) {
		c.json(200, gin.h{"message": "创建文章"})
	})

	api.delete("/articles", func(c *gin.context) {
		c.json(200, gin.h{"message": "删除文章"})
	})

	r.run(":8080")
}

5. 方案总结

✅ redis 存权限(推荐):高效、适用于分布式
✅ rbac 权限映射:角色权限映射清晰
✅ jwt 认证:无状态,适用于 api 认证

这样,你就能 用 redis 设计一套高效的 rbac 权限管理,并支持 api 映射!

到此这篇关于redis实现rbac权限管理的文章就介绍到这了,更多相关redis rbac权限内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com