1. 数据库表结构设计
- 用户表(users)
字段名 | 类型 | 描述 |
---|
id | int(主键,自增) | 用户唯一标识 |
username | varchar(255) | 用户名 |
password | varchar(255) | 用户密码(建议加密存储) |
role_id | int | 关联角色表的外键 |
- 角色表(roles)
字段名 | 类型 | 描述 |
---|
id | int(主键,自增) | 角色唯一标识 |
role_name | varchar(255) | 角色名称,如“admin”“user”等 |
- 权限表(permissions)
字段名 | 类型 | 描述 |
---|
id | int(主键,自增) | 权限唯一标识 |
permission_name | varchar(255) | 权限名称,如“create_user”“delete_file”等 |
- 角色 - 权限关联表(role_permissions)
字段名 | 类型 | 描述 |
---|
id | int(主键,自增) | 关联记录唯一标识 |
role_id | int | 关联角色表的外键 |
permission_id | int | 关联权限表的外键 |
2. gRPC服务端授权逻辑编写
- 中间件实现:在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, "权限不足")
}
- 注册中间件:在gRPC服务注册时添加该授权中间件。例如:
s := grpc.NewServer(grpc.UnaryInterceptor(AuthInterceptor))
pb.RegisterYourServiceServer(s, &YourServiceImpl{})
- 获取请求所需权限:可以通过一个映射表或根据方法名约定来获取每个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客户端授权逻辑编写
- 携带认证信息:在客户端发起请求前,先进行认证获取用户信息,并将认证信息(如JWT令牌)添加到gRPC请求的元数据中。例如在Go语言中:
ctx := context.Background()
token, err := getToken()
if err!= nil {
log.Fatalf("获取令牌失败: %v", err)
}
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token)
- 处理权限不足错误:在客户端处理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)
}
}