【问题标题】:Can Spring evaluate all characters/expressions in SpEL expressions as they are injected from a property file?当从属性文件注入时,Spring 可以评估 SpEL 表达式中的所有字符/表达式吗?
【发布时间】:2013-11-19 02:59:42
【问题描述】:

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

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

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

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

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

  3. 如果我们尝试将属性文件中的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:既不是配置问题,也不是注入问题。我检查了这些值是否正确注入。

【问题讨论】:

    标签: java spring spring-security annotations spring-el


    【解决方案1】:

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

    • SpEL 中的字符串文字用单引号表示,例如 'Hello' 是 SpEL 字符串文字。
    • 资源包中的所有值都转换为字符串文字,这意味着:
      • 如果您在 Java 中从包中获取 HELLO,您将收到 "HELLO" 字符串文字。
      • 如果您从 SpEL 中的捆绑包中获取 HELLO,您将收到 'HELLO' 字符串文字。

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

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

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

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

    【讨论】:

    • 感谢您加强我的假设。注意:我的第一个和第二个假设也与我的第三个相同:)。所以我的假设没有错(检查我的结论部分)。那么,解决方案将是什么?如果我们想将属性文件中的值注入到spring注解中,我们该如何实现呢?例如,让我们考虑上面的例子。我想将表达式 hasRole('ROLE_ADMIN') 放在属性文件中并将其注入 @PreAuthorize(...) 注释。如何启用 SpEL 转义单引号 (')?
    • 如果我是你,我会创建一个自定义实用程序“SpEL 表达式评估类”并将其注册为单例 bean。然后我将实现一个 generic 方法,该方法将 String 变量作为参数(这将是 String 格式的 SpEL 表达式)和预期结果的类类型。在@PreAuthorize 中,我将调用泛型方法并期望它返回Boolean。如果您想详细说明,我可以聊天。
    • 再次感谢您。我正在使用我的自定义权限评估器类来评估表达式。但是我决定不使用自定义权限评估器,以避免使用自定义评估器可能带来的复杂性,特别是我所涉及的上下文对于使用我的自定义评估器并不是那么灵活。是的,如果我们能聊一会儿会很有帮助。
    猜你喜欢
    • 1970-01-01
    • 2020-12-03
    • 2016-03-18
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    • 2011-08-08
    • 2018-01-27
    • 1970-01-01
    相关资源
    最近更新 更多