MST

星途 面试题库

面试题:微服务架构中gRPC如何实现基于角色的访问控制(RBAC)授权

假设你正在负责一个基于gRPC的微服务项目,需要实现基于角色的访问控制(RBAC)授权。请详细描述实现思路,包括如何设计数据库表结构存储角色、权限相关信息,以及在gRPC服务端和客户端如何进行授权逻辑的编写。
44.5万 热度难度
后端开发微服务架构

知识考点

AI 面试

面试题答案

一键面试

1. 数据库表结构设计

  • 用户表(users)
    字段名类型描述
    idint(主键,自增)用户唯一标识
    usernamevarchar(255)用户名
    passwordvarchar(255)用户密码(建议加密存储)
    role_idint关联角色表的外键
  • 角色表(roles)
    字段名类型描述
    idint(主键,自增)角色唯一标识
    role_namevarchar(255)角色名称,如“admin”“user”等
  • 权限表(permissions)
    字段名类型描述
    idint(主键,自增)权限唯一标识
    permission_namevarchar(255)权限名称,如“create_user”“delete_file”等
  • 角色 - 权限关联表(role_permissions)
    字段名类型描述
    idint(主键,自增)关联记录唯一标识
    role_idint关联角色表的外键
    permission_idint关联权限表的外键

2. gRPC服务端授权逻辑编写

  1. 中间件实现:在gRPC服务端创建一个授权中间件。例如,在Go语言中可以这样实现:
func AuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    // 从上下文获取用户信息,假设已经通过认证中间件添加了用户信息
    user, ok := ctx.Value("user").(*User)
    if!ok {
        return nil, status.Errorf(codes.Unauthenticated, "用户未认证")
    }
    // 根据用户角色查询其拥有的权限
    var permissions []string
    err := db.Table("roles").
        Select("permissions.permission_name").
        Joins("JOIN role_permissions ON roles.id = role_permissions.role_id").
        Joins("JOIN permissions ON role_permissions.permission_id = permissions.id").
        Where("roles.id =?", user.role_id).
        Scan(&permissions).Error
    if err!= nil {
        return nil, status.Errorf(codes.Internal, "查询权限失败: %v", err)
    }
    // 判断当前请求所需的权限是否在用户权限列表中
    requiredPermission := getRequiredPermission(info.FullMethod)
    for _, perm := range permissions {
        if perm == requiredPermission {
            return handler(ctx, req)
        }
    }
    return nil, status.Errorf(codes.PermissionDenied, "权限不足")
}
  1. 注册中间件:在gRPC服务注册时添加该授权中间件。例如:
s := grpc.NewServer(grpc.UnaryInterceptor(AuthInterceptor))
pb.RegisterYourServiceServer(s, &YourServiceImpl{})
  1. 获取请求所需权限:可以通过一个映射表或根据方法名约定来获取每个gRPC方法所需的权限。例如:
var methodPermissionMap = map[string]string{
    "/yourpackage.YourService/CreateUser": "create_user",
    "/yourpackage.YourService/DeleteFile": "delete_file",
}
func getRequiredPermission(method string) string {
    return methodPermissionMap[method]
}

3. gRPC客户端授权逻辑编写

  1. 携带认证信息:在客户端发起请求前,先进行认证获取用户信息,并将认证信息(如JWT令牌)添加到gRPC请求的元数据中。例如在Go语言中:
ctx := context.Background()
token, err := getToken()
if err!= nil {
    log.Fatalf("获取令牌失败: %v", err)
}
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token)
  1. 处理权限不足错误:在客户端处理gRPC调用返回的错误,如果是权限不足的错误(codes.PermissionDenied),则提示用户权限不足。例如:
resp, err := client.YourMethod(ctx, &pb.YourRequest{})
if err!= nil {
    status, ok := status.FromError(err)
    if ok && status.Code() == codes.PermissionDenied {
        log.Println("权限不足")
    } else {
        log.Printf("调用失败: %v", err)
    }
}