Spring 安全自定义 PermissionEvaluator
Spring Security custom PermissionEvaluator
我正在使用 JWT 令牌授权。
我正在尝试限制对某些 REST api 端点的访问。我希望它像这样工作:下面的代码应该只在 @PathVariable
中给出的经过身份验证的用户 id == id 时执行
@PreAuthorize("hasAnyAuthority('ADMIN', 'USER')")
@GetMapping(value = "orders/{id}", produces = "application/json")
public EntityModel<Order> getOrders(@PathVariable Long id) { ... )
这些文章或多或少描述了我试图完成的事情:similar post, article
我不明白在我的情况下第二个 link CustomPermissionEvaluator implements PermissionEvaluator
应该是什么样子。如果有人能给我一些提示,我将不胜感激。
我的第二个担心是我以后能做的就是获取用户名authentication.getName()
,然后我必须使用UserRepository
来findUserByUsername(String username)
。这样做正常吗?那是额外的数据库查询。我想知道是否有可能以某种方式将 userID 添加到令牌(因为 returns UsernamePasswordAuthenticationToken
已经通过用户名获取用户的功能)。
要触发 PermissionEvaluator
,您必须在 @PreAuthorize
中使用 hasPermission()
。
hasPermission()
有 2 个版本,它们是:
(1) @PreAuthorize("hasPermission('foo' ,'bar')")
将调用
boolean hasPermission(Authentication authentication, Object targetDomainObject,Object permission);
/** targetDomainObject = 'foo', permission = 'bar' **/
(2) @PreAuthorize("hasPermission('foo' ,'bar','baz')")
将调用
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);
/** targetId = 'foo' , targetType = 'bar' , permission = 'baz' **/
在这两种情况下,Authentication
参数是从 SecurityContext
.
获取的 Authentication
令牌
需要注意的一点是,在配置@PreAuthorize("hasPermission()")
时,可以使用spring数据中的#foo
、@P
或@Param
来指定参数在受保护的方法中将用于调用 PermissionEvaluator
。有关详细信息,请参阅 this。
在你的情况下,你可以这样做:
@PreAuthorize("hasPermission('#id', 'getOrder')")
public EntityModel<Order> getOrders(@PathVariable Long id) {
}
并且 PermissionEvaluator
看起来像:
public class MyPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {
MyAuthentication myAuth = (MyAuthentication) auth;
Long targetId = (Long) id;
String permssionStr = (String) permission;
if(permssionStr.equals("getOrder")){
return myAuth.getUserId().equals(targetId);
}else if(permssionStr.equals("xxxx"){
//other permission checking
}
}
}
请注意,它假定您还将 Authentication
令牌自定义为 MyAuthentication
,其中包括用户 ID。它还回答了您的第二个问题,即您可以将身份验证过程自定义为 return 自定义 Authentication
令牌,您可以在加载用户记录进行身份验证后将 userId 设置到其中。这样,userId
将存储在 MyAuthentication
中,您无需在 PermissionEvaluator
.
中再次查询它
或者,您也可以考虑直接在 @PreAuthorize
中表达授权逻辑,而不使用 hasPermission()
对于这种简单的情况:
@PreAuthorize("#id == authentication.userId")
public EntityModel<Order> getOrders(@PathVariable Long id) {
}
我正在使用 JWT 令牌授权。
我正在尝试限制对某些 REST api 端点的访问。我希望它像这样工作:下面的代码应该只在 @PathVariable
@PreAuthorize("hasAnyAuthority('ADMIN', 'USER')")
@GetMapping(value = "orders/{id}", produces = "application/json")
public EntityModel<Order> getOrders(@PathVariable Long id) { ... )
这些文章或多或少描述了我试图完成的事情:similar post, article
我不明白在我的情况下第二个 link CustomPermissionEvaluator implements PermissionEvaluator
应该是什么样子。如果有人能给我一些提示,我将不胜感激。
我的第二个担心是我以后能做的就是获取用户名authentication.getName()
,然后我必须使用UserRepository
来findUserByUsername(String username)
。这样做正常吗?那是额外的数据库查询。我想知道是否有可能以某种方式将 userID 添加到令牌(因为 returns UsernamePasswordAuthenticationToken
已经通过用户名获取用户的功能)。
要触发 PermissionEvaluator
,您必须在 @PreAuthorize
中使用 hasPermission()
。
hasPermission()
有 2 个版本,它们是:
(1) @PreAuthorize("hasPermission('foo' ,'bar')")
将调用
boolean hasPermission(Authentication authentication, Object targetDomainObject,Object permission);
/** targetDomainObject = 'foo', permission = 'bar' **/
(2) @PreAuthorize("hasPermission('foo' ,'bar','baz')")
将调用
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);
/** targetId = 'foo' , targetType = 'bar' , permission = 'baz' **/
在这两种情况下,Authentication
参数是从 SecurityContext
.
Authentication
令牌
需要注意的一点是,在配置@PreAuthorize("hasPermission()")
时,可以使用spring数据中的#foo
、@P
或@Param
来指定参数在受保护的方法中将用于调用 PermissionEvaluator
。有关详细信息,请参阅 this。
在你的情况下,你可以这样做:
@PreAuthorize("hasPermission('#id', 'getOrder')")
public EntityModel<Order> getOrders(@PathVariable Long id) {
}
并且 PermissionEvaluator
看起来像:
public class MyPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {
MyAuthentication myAuth = (MyAuthentication) auth;
Long targetId = (Long) id;
String permssionStr = (String) permission;
if(permssionStr.equals("getOrder")){
return myAuth.getUserId().equals(targetId);
}else if(permssionStr.equals("xxxx"){
//other permission checking
}
}
}
请注意,它假定您还将 Authentication
令牌自定义为 MyAuthentication
,其中包括用户 ID。它还回答了您的第二个问题,即您可以将身份验证过程自定义为 return 自定义 Authentication
令牌,您可以在加载用户记录进行身份验证后将 userId 设置到其中。这样,userId
将存储在 MyAuthentication
中,您无需在 PermissionEvaluator
.
或者,您也可以考虑直接在 @PreAuthorize
中表达授权逻辑,而不使用 hasPermission()
对于这种简单的情况:
@PreAuthorize("#id == authentication.userId")
public EntityModel<Order> getOrders(@PathVariable Long id) {
}