我想知道为什么spring不会直接评估所有表达式,因为它们是从属性文件注入到@PreAuthorize(...)
注释中的。我认为spring不会评估某些字符,如'(',')','''等,或者它会在属性文件中注入的值之上添加特殊字符。为了澄清,让我们考虑以下示例。
@PreAuthorize("hasRole('ROLE_ADMIN')")
上面的表达是正常的,并且工作正常。假设属性文件的值如下。
role1=ROLE_ADMIN
role2='ROLE_ADMIN'
role3=hasRole('ROLE_ADMIN')
让我们从属性文件中注入role1
并将其传递给@PreAuthorize("hasRole(${role1})")
,它运行正常。在评估hasRole(...)
表达式的正常方式中,角色名称必须在单引号下,即'ROLE_ADMIN'
。但这里它适用于ROLE_ADMIN。令人惊讶!
如果我们从属性文件中将role2
注入@PreAuthorize("hasRole(${role2})")
,它将返回拒绝访问权限。这意味着它被评估但我们可以意识到传递给表达式的值不是“'ROLE_ADMIN'
”。因此,如果角色名称在单引号下,则拒绝访问。另一个惊喜!。
如果我们尝试将role3
从属性文件注入@PreAuthorize("${role3}")
,则不会对其进行评估。
例外:
java.lang.IllegalArgumentException: Failed to evaluate expression 'role3'
根本原因:org.springframework.expression.spel.SpelEvaluationException: EL1001E:(pos 0): Type conversion problem, cannot convert from java.lang.String to java.lang.Boolean
java.lang.IllegalArgumentException: Invalid boolean value 'hasRole('ROLE_ADMIN')'
我的结论:
从上面的(1)和(2),我们可以实现一件事。也就是说,在传递给@PreAuthorize(...)
注释时,似乎注入的值放在单引号('')下。如果该陈述不成立,则(1)和(2)将不起作用。这只是我的结论!
当我们来到(3)时,案例看起来类似于(1)和(2)。文件中的值为“hasRole('ROLE_ADMIN')
”。正确注入此值后,如果在传递到@PreAuthorize(...)
时添加了单引号,则它将类似于@PreAuthorize("'hasRole('ROLE_ADMIN')'")
。所以“'hasRole('ROLE_ADMIN')'
”是一个字符串而不是一个布尔值。这只是我的怀疑。
问题:
你认为我的结论是正确的吗?如果没有,如果有什么我错过了,你能指出我吗?或者,如果您有其他解决方案来通过从属性文件中注入值来实现@PreAuthorize(...)
,请提供给我。
提前谢谢!
Note
:它既不是配置问题也不是注入问题。我检查了值是否正确注入。
答案 0 :(得分:3)
答案的关键是如何在SpEL中表示字符串文字。以下是有效的:
'Hello'
是SpEL字符串文字。HELLO
,您将收到"HELLO"
字符串文字。HELLO
,您将收到'HELLO'
字符串字面值。现在让我们来看看你上面的例子。
在第一种情况下,资源包中有role1=ROLE_ADMIN
,这意味着在评估'ROLE_ADMIN'
时将创建${role1}
字符串文字。这将导致@PreAuthorize("hasRole('ROLE_ADMIN')")
注释完全有效(如果您定义了ROLE_ADMIN角色)。这就是它工作的原因,而且不令人惊讶。
在第二种情况下,资源包中有role2='ROLE_ADMIN'
,这意味着在评估'''ROLE_ADMIN'''
时将创建${role2}
字符串文字。请注意,'
符号会通过放置两个'
字符进行转义。您收到“拒绝访问”错误,原因只是因为您没有'ROLE_ADMIN'
个角色,而是ROLE_ADMIN
(不同)。
你的第三个猜测几乎是正确的。您唯一不正确的是注释在评估资源包中的#{role3}
值时的样子。正如我所提到的,'
通过放置两个'
字符在SpEL中进行转义。因此,注释看起来像@PreAuthorize("'hasRole('''ROLE_ADMIN''')'")
。您完全正确地认为这是String
,而不是Boolean
表达式,这就是IllegalArgumentException
被抛出的原因。