我在创建可重复用于多个 Controller 的自定义授权时遇到问题。来自示例 here看起来自定义授权不能重用到另一个具有不同签名的 Controller 中。
在我的例子中,我有一些具有某些 Angular 色授权的端点,以防止未经授权的用户访问某些端点。
RoomsUsersController
POST /rooms/:roomId/users/:userId/kick -> role: RoomAdmin
POST /rooms/:roomId/users/:userId/promote -> role: RoomAdmin
GET /rooms/:roomId/users?offset&limit -> role: RoomAdmin, RoomUser
只有 RoomAdmin
可以踢出和提升用户,并且只有注册的 RoomUser
或 RoomAdmin
才能看到房间的用户列表。
我还创建了另一个由另一个 Controller 处理的速记端点,通过在帖子正文中提供 roomId
来踢和提升用户,如下所示。
UsersController
POST /users/:id/kick body: {roomId} -> role: RoomAdmin
POST /users/:id/promote body: {roomId} -> role: RoomAdmin
RoomsUsersController
和 UsersController
的签名如下所示(方法主体和验证已删除)
@route.root("/rooms/:roomId/users")
export class RoomsUsersController {
@route.post(":userId/kick")
kick(roomId:string, userId:string){}
@route.post(":userId/promote")
promote(roomId:string, userId:string){}
}
export class UsersController {
@route.post(":id/kick")
kick(id: string, @bind.body() body: { roomId: string }) { }
@route.post(":id/promote")
promote(id: string, @bind.body() body: { roomId: string }) { }
}
我目前的解决方案是为 RoomsUsersController
和 UsersController
创建不同的自定义授权,但它不那么干而且看起来很麻烦。
//custom authorization for RoomsUsersController
function roomUser(...roles: string[]) {
return authorize.custom(async ({ user, parameters }) => {
const room = await RoomModel.findById(parameters[0]).populate("users")
const roomUser = room.users.find(x => x.userId === user.userId)
if(!roomUser) throw new HttpStatusError(401, "Unauthorized user")
return roles.some(x => x === roomUser.role)
})
}
parameter[0]
不适用于 UsersController
,因为它具有不同的签名。有人遇到同样的问题吗?
最佳答案
这是您需要的 route
属性。它包含有关当前 Controller/Action 处理请求的元数据信息。您可以使用 route.action.parameters
等获取当前操作参数元数据列表,其中包含参数名称、参数类型等,您可以查看完整架构 here .
您的 Controller 的问题是它们之间没有相似之处,无法检索自定义授权将使用的 roomId
。您可以像下面这样修改您的 UsersController
:
export class UsersController {
@route.post(":id/kick")
kick(id: string, roomId: string) { }
@route.post(":id/promote")
promote(id: string, roomId: string) { }
}
请注意,您可以将请求正文属性分解为多个参数。上面的代码显示之前的body: { roomId: string }
参数可以简化为roomId:string
。如果 body
参数有更多属性,您可以简单地将它们分解为具有适当名称和类型的参数。
现在使用上面的 Controller ,UsersController
和 RoomsUsersController
之间有相似之处,即 roomId
参数。您现在可以使用 route.action.parameters
访问它们,现在完整的自定义授权如下所示:
function roomUser(...roles: string[]) {
return authorize.custom(async ({ user, route, parameters }) => {
const index = route.action.parameters.findIndex(x => x.name === "roomId")
if(index === -1)
throw new Error(`No parameter roomId found in
{route.controller.name}.${route.action.name}`)
const room = await RoomModel.findById(parameters[index]).populate("users")
const roomUser = room.users.find(x => x.userId === user.userId)
if(!roomUser) throw new HttpStatusError(401, "Unauthorized user")
return roles.some(x => x === roomUser.role)
})
}
上面代码的想法是:你在 Action 参数中找到名为roomId
的参数的位置,而不是从带有索引的parameters
中获取值。
关于javascript - 如何在Plumier中创建基于用户权限的可重用自定义授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58481395/