[英]How to interpret hasPermission in spring security?
I am new to spring security.我是春季安全的新手。 How do I interpret this?
我该如何解释?
@PreAuthorize("hasPermission(null, 'opetussuunnitelma', 'LUONTI')")
OpetussuunnitelmaDto addOpetussuunnitelma(OpetussuunnitelmaDto opetussuunnitelmaDto);
Which method from the permission evaluator would get called?权限评估器中的哪个方法会被调用? I think the one with three parameters would get called in this case.
我认为在这种情况下会调用具有三个参数的那个。 It is checking if the current user has the 'LUONTI' permission on the target of type - 'opetussuunnitelma' .
它正在检查当前用户是否对类型为 'opetussuunnitelma' 的目标具有 'LUONTI' 权限。 Am I right?
我对吗? Can't we just not include "null" and pass only two parameters.
我们不能不包含“null”并只传递两个参数。 I read that the first argument ( the Authentication object) is not supplied.
我读到没有提供第一个参数( Authentication 对象)。
+public class PermissionEvaluator implements org.springframework.security.access.PermissionEvaluator {
+
+ @Override
+ public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
+ LOG.error(" *** ei toteutettu *** ");
+ return true;
+ }
+
+ @Override
+ public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
+ LOG.error(" *** ei toteutettu *** ");
+ return true;
+ }
+
+ private static final Logger LOG = LoggerFactory.getLogger(PermissionEvaluator.class);
+}
Which method from the permission evaluator would get called?
来自权限评估程序的哪种方法会被调用?
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission)
Would get called. 会被叫到。
I read that the first argument ( the Authentication object) is not supplied.
我读到没有提供第一个参数(Authentication对象)。
It's not explicitly supplied in your annotation, but implicitly supplied by Spring. 它没有在您的注释中明确提供,而是由Spring隐式提供。 Your annotation should just read
您的注释应该只是阅读
@PreAuthorize("hasPermission(#opetussuunnitelmaDto, 'LUONTI')")
Ideally I would check if they're even authenticated before performing the authorization. 理想情况下,我会在执行授权之前检查它们是否经过身份验证。
@PreAuthorize("isAuthenticated() and hasPermission(#opetussuunnitelmaDto, 'LUONTI')")
Update to your comment 更新您的评论
Basically you can either call the PermissionEvaluator with either: 基本上,您可以使用以下任一方法调用PermissionEvaluator:
hasPermission('#targetDomainObject', 'permission') // method1
hasPermission('targetId', 'targetType', 'permission') // method2
Authentication will always be supplied by Spring. 身份验证将始终由Spring提供。 In your case, you are calling hasPermission the following way
在您的情况下,您通过以下方式调用hasPermission
hasPermission(null, 'opetussuunnitelma', 'LUONTI')")
hasPermission(null,'opetussuunnitelma','LUONTI')“)
which would match method2 , but passing in a null id doesn't make sense, what entity are you going to target the permission check on? 哪个会匹配method2 ,但是传入一个null id是没有意义的,你要将什么实体作为目标进行权限检查? Based on your method that you're applying the @PreAuthorize on,
根据您正在应用@PreAuthorize的方法,
OpetussuunnitelmaDto addOpetussuunnitelma(OpetussuunnitelmaDto opetussuunnitelmaDto);
OpetussuunnitelmaDto addOpetussuunnitelma(OpetussuunnitelmaDto opetussuunnitelmaDto);
it may make more sense to call method1 since you seem to have something that resembles an target domain object. 调用method1可能更有意义,因为你似乎有一些类似于目标域对象的东西。
This answer has already got to the heart of what it seems the OP was truly asking.这个答案已经触及了 OP 真正要问的问题的核心。 I will augment that answer with a slightly deeper dive into what is going on behind the scenes with the
hasPermission
expression.我将通过更深入地了解
hasPermission
表达式的幕后情况来增强该答案。
Let's first recap on this answer .让我们首先回顾一下这个答案。 The answerer detected that the OP really meant to be using an annotation with two parameters:
回答者检测到 OP 确实打算使用带有两个参数的注释:
@PreAuthorize("hasPermission(#opetussuunnitelmaDto, 'LUONTI')")
The confusion arose because the OP saw a method hasPermission
in the code which took three parameters, and couldn't figure out what to pass for the first parameter.出现混淆是因为 OP 在代码中看到一个方法
hasPermission
接受三个参数,并且无法弄清楚为第一个参数传递什么。 The answerer confirmed that the Spring framework itself provides that first parameter, namely the Authentication
object, so in the annotation we only need to pass two arguments.答主确认是Spring框架自己提供了第一个参数,即
Authentication
对象,所以在注解中我们只需要传递两个参数即可。
To understand what's going on in a little more detail, let's analyse how hasPermission
works in Spring OOTB.为了更详细地了解发生了什么,让我们分析
hasPermission
在 Spring OOTB 中是如何工作的。 I won't go into every last detail, but will sketch out the main flow of what is happening.我不会详细介绍每一个细节,但会勾勒出正在发生的事情的主要流程。 Hopefully this will shed light not only upon which overloaded method is linked to the
hasPermission
SpEL expression, as the OP asks, but also will reveal a bit about how the entire ACL framework interprets the hasPermission
expression under the hood;希望这不仅会阐明哪个重载方法链接到
hasPermission
SpEL 表达式,正如 OP 所要求的那样,而且还会揭示整个 ACL 框架如何在幕后解释hasPermission
表达式; this will give us a greater confidence of what the hasPermission
expression means, and thus how to interpret and use it.这将使我们对
hasPermission
表达式的含义以及如何解释和使用它更有信心。
So let's start from the top.所以让我们从顶部开始。
To understand the hasPermission
expression we really need to understand pre/post authorization.要理解
hasPermission
表达式,我们真的需要理解 pre/post 授权。 However, since the OP doesn't ask about that, it's assumed to be known, and I won't go into much detail about method protection via the @PreAuthorize
and @PostAuthorize
annotations.但是,由于 OP 没有询问这一点,因此假定它是已知的,并且我不会通过
@PreAuthorize
和@PostAuthorize
注释详细介绍方法保护。 The reader is referred here for more info on that.有关更多信息,请参阅此处的读者。 Suffice it to say here that we'll assume the
hasPermission
expression is embedded in such an annotation in order to protect a method or return object.在这里只想说,我们假设
hasPermission
表达式嵌入在这样的注释中,以保护方法或返回对象。 The hasPermission
expression in turn will evaluate to true or false. hasPermission
表达式反过来将评估为真或假。 If it evaluates to true, the Spring framework will allow the method call to proceed in the case of pre-authorization or will allow the object to be returned in the case of post authorization.如果它评估为真,则 Spring 框架将允许在预授权的情况下继续方法调用,或者在后授权的情况下允许返回对象。 Otherwise, it will block access.
否则,它将阻止访问。 That's enough about those annotations.
关于这些注释就足够了。 What we really want to know is how Spring interprets the
hasPermission
expression itself, to arrive at a true/false value.我们真正想知道的是 Spring 如何解释
hasPermission
表达式本身,以获得真/假值。
So, hasPermission
will evaluate to true or false.因此,
hasPermission
将评估为真或假。 But how?但是如何? Well, as alluded to by the OP, Spring delegates permission evaluation to the
PermissionEvaluator
object which is nested inside the MethodSecurityExpressionHandler
Bean.好吧,正如 OP 所暗示的那样,Spring 将权限评估委托给嵌套在
MethodSecurityExpressionHandler
Bean 中的PermissionEvaluator
对象。 If you've set up Spring ACL, then it's likely you've registered the AclPermissionEvaluator
as the permission evaluator for Spring to use.如果您已经设置了 Spring ACL,那么很可能您已经将
AclPermissionEvaluator
注册为 Spring 使用的权限评估器。 For example, if you configured Spring ACL with code you might have something like this:例如,如果您使用代码配置了 Spring ACL,您可能会遇到以下情况:
@Bean
public MethodSecurityExpressionHandler
defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler
= new DefaultMethodSecurityExpressionHandler();
AclPermissionEvaluator permissionEvaluator
= new AclPermissionEvaluator(aclService());
expressionHandler.setPermissionEvaluator(permissionEvaluator);
return expressionHandler;
}
Had you not done that, the default permission evaluator in place would have been the DenyAllPermissionEvaluator
, which as I'm sure you've guessed would just deny permission in all cases: a safe default for sure.如果您没有这样做,那么默认的权限评估器将是
DenyAllPermissionEvaluator
,正如我确定您已经猜到的DenyAllPermissionEvaluator
,它只会在所有情况下拒绝权限:肯定是安全的默认值。
So, with the AclPermissionEvaluator
class plugged into the Spring security framework as above, all hasPermission
expressions in the Spring expression language (SpEL) will be delegated to the AclPermissionEvaluator
for evaluation.因此,通过将
AclPermissionEvaluator
类插入到上述 Spring 安全框架中,Spring 表达式语言 (SpEL) 中的所有hasPermission
表达式都将委托给AclPermissionEvaluator
进行评估。 I have not looked into the exact details of how the SpEL expression eventually ends up resulting calling upon methods within AclPermissionEvaluator
, but I don't think such knowledge is needed to interpret what the hasPermission
expression means.我还没有研究 SpEL 表达式最终如何最终导致调用
AclPermissionEvaluator
方法的确切细节,但我认为不需要这些知识来解释hasPermission
表达式的含义。 IMO, all that's necessary to know, at this level, is which annotation results in which method call. IMO,在这个级别上,所有需要知道的是哪个注释导致哪个方法调用。 This has already been covered by this answer .
这个答案已经涵盖了这一点。 But let me recap it here.
但让我在这里回顾一下。 First of all, we note that the
hasPermission
method is overloaded in the AclPermissionEvaluator
and indeed in any implementation of PermissionEvaluator
.首先,我们注意到
hasPermission
方法在AclPermissionEvaluator
重载,实际上在PermissionEvaluator
任何实现中。 One of the methods takes 3 arguments and the other takes 4 arguments:其中一种方法需要 3 个参数,另一种方法需要 4 个参数:
//3-Arg-Method
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission);
//4-Arg-Method
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);
On the other hand, the hasPermission
expression has two use cases also.另一方面,
hasPermission
表达式也有两个用例。 One of them passes in 2 arguments, and the other passes in 3 arguments.其中一个传入 2 个参数,另一个传入 3 个参数。 These were already pointed out in this answer .
这些已经在这个答案中指出了。 But let's label them here as expressions, rather than methods, so as not to confuse the two:
但是让我们在这里将它们标记为表达式,而不是方法,以免混淆两者:
hasPermission('#targetDomainObject', 'permission') //2-arg-expression
hasPermission('targetId', 'targetType', 'permission') //3-arg-expression
We can now link the two:我们现在可以将两者联系起来:
//2-arg-expression
is used, then the //3-Arg-Method
is called.//2-arg-expression
,则调用//3-Arg-Method
。//3-arg-expression
is used, then the //4-Arg-Method
is called.//3-arg-expression
,则调用//4-Arg-Method
。 Where do the methods get their extra argument?这些方法从哪里获得额外的参数? Again, this was already answered here , but to recap, the extra argument that the Spring security framework provides based on the security context is the first argument in both cases, namely the
Authentication
parameter by the name of authentication
.同样,这里已经回答了这个问题,但回顾一下,Spring 安全框架基于安全上下文提供的额外参数是这两种情况下的第一个参数,即名称为
authentication
的Authentication
参数。 I haven't looked into how the Spring framework does this exactly, but for me it was enough to just know that Spring security can get an authentication object in this context.我还没有研究 Spring 框架究竟是如何做到这一点的,但对我来说,只知道 Spring 安全性可以在这种情况下获取身份验证对象就足够了。
OK, but what about the other arguments?好的,但是其他参数呢? Let's see this next.
让我们接下来看看这个。 To avoid this answer getting too large, I'll just focus on the case where the
//2-arg-expression
is used and the //3-Arg-Method
is called.为了避免这个答案变得太大,我将只关注使用
//2-arg-expression
并调用//3-Arg-Method
情况。
hasPermission
Method hasPermission
方法的参数As mentioned, let's just focus on this method:如前所述,让我们只关注这个方法:
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission);
As discussed already, the first argument, the authentication
object is inferred via Spring security.如前所述,第一个参数,
authentication
对象是通过 Spring 安全性推断的。 I haven't looked into exactly how that happens, but I believe all we need to know for the purposes of this post is to understand that the authentication object contains:我还没有研究具体是如何发生的,但我相信为了这篇文章的目的,我们需要知道的就是了解身份验证对象包含:
In Spring ACL, we refer to a principal such as "Alice", or an authority such as "editor" using the common term SID.在 Spring ACL 中,我们使用通用术语 SID 来引用主体(例如“Alice”)或权限(例如“编辑器”)。 As such, the
authentication
object contains not just one SID, but a whole list of them.因此,
authentication
对象不仅包含一个 SID,还包含它们的整个列表。 The order of this list matters, as we'll see later on.这个列表的顺序很重要,我们稍后会看到。
The remaining parameters to the hasPermission
method are passed via the hasPermission
expression. hasPermission
方法的其余参数通过hasPermission
表达式传递。 These are both typed as Object
.这些都被输入为
Object
。 Again, I'll just focus on one use case for the sake of keeping this post a bit shorter.同样,为了让这篇文章更短一些,我将只关注一个用例。 Indeed, let's focus on a slightly modified version of the original use case that the OP mentions:
实际上,让我们关注 OP 提到的原始用例的稍微修改版本:
@PreAuthorize("hasPermission(#opetussuunnitelmaDto, 'READ')")
OpetussuunnitelmaDto addOpetussuunnitelma(OpetussuunnitelmaDto opetussuunnitelmaDto);
#opetussuunnitelmaDto
is a way of specifying in SpEL that the opetussuunnitelmaDto
parameter of the method addOpetussuunnitelma
is passed in as the targetDomainObject
of the hasPermission
method.#opetussuunnitelmaDto
是在使用SpEL指定的一种方式, opetussuunnitelmaDto
方法的参数addOpetussuunnitelma
在作为传递targetDomainObject
所述的hasPermission
方法。'READ'
parameter is simpler: it's simply passed as a String
straight to the permission
parameter of the hasPermission
method. 'READ'
参数更简单:它只是作为String
直接传递给hasPermission
方法的permission
参数。So, we now know how all the parameters are supplied to this method:所以,我们现在知道所有参数是如何提供给这个方法的:
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission);
But parameters of type Object
are never much use.但是
Object
类型的参数从来没有多大用处。 Spring ACL needs to convert those parameters into information is can use to access the relevant ACL info from the database and do its permission checking. Spring ACL 需要将这些参数转换为信息,以便从数据库中访问相关的 ACL 信息并进行权限检查。 It does so by delegating to the
checkPermission
method, which extracts info as follows:它通过委托
checkPermission
方法来实现,该方法提取信息如下:
List<Sid> sids
.List<Sid> sids
。 Now, the order of this list is important.SidRetrievalStrategy
, which by default is SidRetrievalStrategyImpl
.SidRetrievalStrategy
,默认情况下是SidRetrievalStrategyImpl
。 By looking at this class's getSids
method, we see that the principal SID, ie Alice, is given the prime position in the list.getSids
方法,我们看到主要 SID,即 Alice,被赋予了列表中的主要位置。 Thereafter follow the granted authorities.Object
, is resolved into a list of Permission
objects via the method resolvePermission
.Object
,通过方法resolvePermission
被解析为一个Permission
对象列表。 The variable that stores this is List<Permission> requiredPermission
.List<Permission> requiredPermission
。 Now, recall that we are focusing on the case where this permission is a single string, namely "READ"
."READ"
。 In this case, if Spring is left to its default behaviour, the permission resolver will use reflection to check this String
against all the static constants in the class BasePermission
, and will return the matching constant.BasePermission
中的所有静态常量检查此String
,并将返回匹配的常量。 The code that actually does the final conversion is the method buildFromMask
in the class DefaultPermissionFactory
.DefaultPermissionFactory
类中的buildFromMask
方法。 If no member of BasePermission
is found whose name matches "READ"
, then the code will throw an exception."READ"
匹配的BasePermission
成员,则代码将引发异常。 Indeed, in the case of the OP's use case, the permission given is "LUONTI"
, which won't match anything in BasePermission
- in that case the developer would need to override BasePermission
or create their own class for permissions."LUONTI"
,它与BasePermission
中的任何内容都不匹配 - 在这种情况下,开发人员需要覆盖BasePermission
或创建自己的权限类。 But we won't cover that here.hasPermission
method, the domain object gets converted to an object ID, which checkPermission
then uses to query the DB for that ACL via the ACL service: Acl acl = this.aclService.readAclById(oid, sids);
hasPermission
方法中,域对象被转换为对象 ID,然后checkPermission
使用该 ID 通过 ACL 服务查询该 ACL 的数据库: Acl acl = this.aclService.readAclById(oid, sids);
. Spring now has all the information it needs to do a YES/NO check: does the currently logged in user have access to this object or not? Spring 现在拥有执行 YES/NO 检查所需的所有信息:当前登录的用户是否有权访问此对象? It does so by delegating to the
isGranted
method on the PermissionGrantingStrategy
Bean.它通过委托给
PermissionGrantingStrategy
Bean 上的isGranted
方法来实现。 By default, this is implemented via the DefaultPermissionGrantingStrategy
.默认情况下,这是通过
DefaultPermissionGrantingStrategy
实现的。
isGranted
...We're Almost There isGranted
...我们isGranted
When we look at this method, it becomes apparent that order is indeed important for the list of ACEs within the ACL and the list of SIDs.当我们查看此方法时,很明显顺序对于 ACL 中的 ACE 列表和 SID 列表确实很重要。 Order is somewhat important for the list of permissions too, but less so - all it determines is which permission is interpreted as the "first" permission that denied access, if the result of the (public*)
isGranted
expression evaluates to false;顺序对于权限列表也有些重要,但不那么重要 - 它决定的是哪个权限被解释为拒绝访问的“第一个”权限,如果 (public*)
isGranted
表达式的结果计算为 false; and from what I can see this is just used for logging/debugging purposes so that an admin can try fix the most likely permission that's broken first.从我所见,这仅用于日志记录/调试目的,以便管理员可以尝试修复最有可能首先被破坏的权限。
For the ACEs and SIDs, order is indeed important because the first matching ACE to an SID takes precedence, and no other matches are performed for that permission.对于 ACE 和 SID,顺序确实很重要,因为第一个与 SID 匹配的 ACE 优先,并且不会为该权限执行其他匹配。 If the match results in an allow, then the entire
isGranted
function returns true.如果匹配结果为允许,则整个
isGranted
函数返回 true。 Else if there is no match for that permission or if there is a deny, the code moves on to the next permission and tries that.否则,如果该权限没有匹配项,或者存在拒绝,则代码会转到下一个权限并尝试该权限。 In this way, we can see that the list of permissions are checked with an OR type of logic: only one of them need to be granted for
isGranted
to succeed.通过这种方式,我们可以看到权限列表是用 OR 类型的逻辑检查的:只需授予其中一个即可,
isGranted
才能成功。
What about the actual logic that checks does a given ACE match a given permission and SID?检查给定 ACE 是否与给定权限和 SID 匹配的实际逻辑如何? Well, the SID bit is easy: just get the SID field off the ACE and compare:
ace.getSid().equals(sid)
.好吧,SID 位很简单:只需从 ACE 中取出 SID 字段并进行比较:
ace.getSid().equals(sid)
。 If the SIDs match, an overloaded isGranted
function is called, which just compares the masks:如果 SID 匹配,则调用重载的
isGranted
函数,该函数仅比较掩码:
protected boolean isGranted(AccessControlEntry ace, Permission p) {
return ace.getPermission().getMask() == p.getMask();
}
IMO, this method really should have been called something like isMatching
because it should return true for both allow (ie grant) AND deny type of permissions. IMO,这个方法真的应该被称为
isMatching
类的isMatching
因为它应该为允许(即授予)和拒绝类型的权限返回 true。 It is just a matching function - the allow/deny behaviour is stored within the ace.isGranting()
field.它只是一个匹配函数 - 允许/拒绝行为存储在
ace.isGranting()
字段中。 Furthermore, the function name isGranted
is overloaded*, confusing matters even more.此外,函数名称
isGranted
被重载*,更令人困惑。
There is also some confusion around why this doesn't use bitwise logic, but don't worry, you can easily override the method if you like, as specified in the answers to the linked question.关于为什么不使用按位逻辑也存在一些混淆,但请不要担心,如果您愿意,可以轻松覆盖该方法,如链接问题的答案中所述。
To recap, the OP originally asked:回顾一下,OP 最初询问:
How to interpret hasPermission in spring security?
spring security中如何解释hasPermission?
This answer deep dives into the machinery of hasPermission
to give an understanding of how to interpret it.这个答案深入探讨了
hasPermission
的机制,以了解如何解释它。 In summary:总之:
hasPermission
SpEL expression links to one of the overloaded hasPermission
methods in the AclPermissionEvaluator
in Spring ACL, with the Authentication
object filled in automatically by Spring security. hasPermission
SpEL 表达式链接到 Spring ACL 中AclPermissionEvaluator
中重载的hasPermission
方法之一,其中Authentication
对象由 Spring 安全性自动填充。hasPermission
SpEL expression trickle down through the Spring ACL machinery. hasPermission
SpEL 表达式的参数通过 Spring ACL 机制向下传递。isGranted
function, which can be overridden eg if the developer wants to use bitwise logic.isGranted
函数,如果开发人员想要使用按位逻辑,则可以覆盖该函数。*There are two version of the isGranted
function. *
isGranted
函数有两个版本。 The public one does indeed check if some permission in the list is granted to some SID. public 确实会检查列表中的某些权限是否授予了某个 SID。 Wherease the protected one really should have been called something like
isMatching
as it checks for matching ACEs.而受保护的确实应该被称为
isMatching
类的isMatching
因为它会检查匹配的 ACE。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.