Spring 可以评估 SpEL 表达式中的所有字符/表达式,因为它们是从属性文件注入的?

我想知道为什么 spring 不直接评估所有表达式,因为它们是从属性文件注入到 @PreAuthorize(...) 注释中的.我认为 spring 不会评估某些字符,例如 '('、')'、''' 等,或者它会在属性文件中的注入值之上添加特殊字符.为了澄清起见,让我们考虑以下示例.

I wonder why spring doesn't directly evaluate all expressions as they are injected from a property file into @PreAuthorize(...) annotation. I think spring doesn't evaluate some characters like '(', ')', ''', etc or it adds special characters on top of those injected values from property file. To clarify, let us consider the following example.

@PreAuthorize("hasRole('ROLE_ADMIN')")

上面的表达式是正常的,并且工作正常.假设属性文件的值如下.

The above expression is normal, and works fine. Suppose values of the property file are the following.

role1=ROLE_ADMIN
role2='ROLE_ADMIN'
role3=hasRole('ROLE_ADMIN')

  1. 让我们从属性文件中注入 role1 并将其传递给 @PreAuthorize("hasRole(${role1})"),它工作正常.在评估 hasRole(...) 表达式的正常方式中,角色名称必须用单引号括起来,即 'ROLE_ADMIN'.但在这里它适用于 ROLE_ADMIN.令人惊讶!.

  1. Let us inject role1 from property file and pass it to @PreAuthorize("hasRole(${role1})"), it works fine. In normal way of evaluating hasRole(...) expression, the role name must be under single quotes, i.e, 'ROLE_ADMIN'. But here it works with ROLE_ADMIN. Surprising!.

如果我们将属性文件中的 role2 注入 @PreAuthorize("hasRole(${role2})"),它会返回拒绝访问.这意味着它已被评估,但我们可以意识到传递给表达式的值不是'ROLE_ADMIN'".因此,如果角色名称在单引号下,则拒绝访问.另一个惊喜!

If we inject role2 from property file into @PreAuthorize("hasRole(${role2})"), it returns access denied. That means it is evaluated but we can realise that the value passed to the expression is something other than "'ROLE_ADMIN'". So, if the role name is under single quotes, access is denied. Another surprise!.

如果我们尝试将属性文件中的 role3 注入 @PreAuthorize("${role3}"),它也不会被评估.例外:

If we try to inject role3 from a property file into @PreAuthorize("${role3}"), it is not being evaluated as well. exceptions:

  • java.lang.IllegalArgumentException:无法评估表达式role3",根本原因:
  • org.springframework.expression.spel.SpelEvaluationException: EL1001E:(pos 0): 类型转换问题,无法从java.lang.String转换为java.lang.Boolean
  • java.lang.IllegalArgumentException: 无效的布尔值 'hasRole('ROLE_ADMIN')'
  • java.lang.IllegalArgumentException: Failed to evaluate expression 'role3' with root causes:
  • 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) 将不起作用.这只是我的结论!

From (1) and (2) above, we may realise one thing. That is, it seems that injected values are placed under single quotes (' ') when being passed to the @PreAuthorize(...) annotation. If this statement is not true, (1) and (2) wouldn't be working. Its just my conclusion!.

当我们来到 (3) 时,情况似乎类似于 (1) 和 (2).文件中的值为hasRole('ROLE_ADMIN')".正确注入此值后,如果在传递给 @PreAuthorize(...) 时添加单引号,它将类似于 @PreAuthorize("'hasRole('ROLE_ADMIN')'").所以'hasRole('ROLE_ADMIN')'"是一个字符串而不是一个布尔值.它只是我的嫌疑人.

When we come to (3), the case seems similar to (1) and (2). The value in the file is "hasRole('ROLE_ADMIN')". After injecting this value correctly, if single quotes are added while passing to the @PreAuthorize(...), it will be like @PreAuthorize("'hasRole('ROLE_ADMIN')'"). So "'hasRole('ROLE_ADMIN')'" is a string not a boolean value. Its just my suspect.

问题:

你认为我的结论是正确的吗?如果没有,你能指出我是否有遗漏的东西吗?或者,如果您有其他解决方案可以通过从属性文件中注入值来实现 @PreAuthorize(...),请提供给我.

Do you think that my conclusion is correct? if not, can you point out me if there is something that I've missed? Or provide me if you have alternative solutions to achieve @PreAuthorize(...) by injecting values from property file.

提前谢谢你!

注意:既不是配置问题,也不是注入问题.我检查了这些值是否正确注入.

Note: it is neither a problem of configuration nor injection. I checked that the values are correctly injected.

推荐答案

答案的关键是字符串字面量在 SpEL 中的表示方式.以下是有效的:

The key to the answer is the way of how the the String literals are represented in SpEL. The following are valid:

  • SpEL 中的字符串文字用单引号表示,例如 'Hello' 是 SpEL 字符串文字.
  • 资源包中的所有值都转换为字符串字面量,这意味着:
    • 如果您在 Java 中从包中获取 HELLO,您将收到 "HELLO" 字符串文字.
    • 如果您从 SpEL 中的捆绑包中获取 HELLO,您将收到 'HELLO' 字符串文字.
    • The String literals in SpEL are presented with single quotes, e.g 'Hello' is a SpEL String literal.
    • All the values from a resource bundle are converted to String literals, which means that:
      • If you fetch a HELLO from a bundle in Java, you will receive a "HELLO" String literal.
      • If you fetch a HELLO from a bundle in SpEL, you will receive a 'HELLO' String literal.

      现在让我们看看上面的例子.

      Now let's take a look on the example you have above.

      1. 在您的第一种情况下,资源包中有 role1=ROLE_ADMIN,这意味着在评估 'ROLE_ADMIN' 字符串文字时将创建 <代码>${role1}.这将导致 @PreAuthorize("hasRole('ROLE_ADMIN')") 注释完全有效(如果您定义了 ROLE_ADMIN 角色).这就是它起作用的原因,它并不令人惊讶.

      1. In your first case, you have role1=ROLE_ADMIN in the resource bundle, which means that a 'ROLE_ADMIN' String literal will be created when evaluating ${role1}. This will result in @PreAuthorize("hasRole('ROLE_ADMIN')") annotation which is perfectly valid (if you have a ROLE_ADMIN role defined). That's why it's working and it is not surprising.

      在第二种情况下,资源包中有 role2='ROLE_ADMIN',这意味着 '''ROLE_ADMIN''' 字符串文字将在评估 ${role2} 时创建.请注意,' 符号通过放置两个 ' 字符进行转义.您收到拒绝访问错误,仅仅是因为您没有 'ROLE_ADMIN' 角色,而是 ROLE_ADMIN(这是不同的).

      In the second case, you have role2='ROLE_ADMIN' in the resource bundle, which means that a '''ROLE_ADMIN''' String literal will be created when the ${role2} is evaluated. Note that the ' sign is escaped by putting two ' characters. You receive an Access Denied error, simply because you don't have a 'ROLE_ADMIN' role, but ROLE_ADMIN (which is different).

      你的第三个猜测几乎是正确的.您唯一不正确的是注释在评估资源包中的 #{role3} 值之后的样子.正如我所提到的,' 在 SpEL 中通过放置两个 ' 字符进行转义.因此,注释看起来像 @PreAuthorize("'hasRole('''ROLE_ADMIN''')'").您完全正确地假设这是一个 String,而不是 Boolean 表达式,这就是抛出 IllegalArgumentException 的原因.p>

    • Your third guess is almost correct. The only thing you're not correct about is how the annotation would look like afted evaluating the #{role3} value in the resource bundle. As I mentioned, the ' is escaped in SpEL by putting two ' characters. Therefore, the annotation would look like @PreAuthorize("'hasRole('''ROLE_ADMIN''')'"). You're completely correct in your assumption that this is a String, not a Boolean expression and this is why the IllegalArgumentException is thrown.

相关文章