4

Spring Security 中的 Spring Expression Language (SpEL) 比较对象使用 equals() 还是 ==?

例如(方法 equals () 没有被调用!):

class SecurityObject {      
   public boolean equals(Object obj) {
  //...
   }
 }

@PreAuthorize(" #secObject == #otherSecObject ") 
public void securityMethod(SecurityObject secObject, SecurityObject otherSecObject) {                        
   //...        
}

这个是正常的!?我需要在任何地方使用 @PreAuthorize(" #secObject.equals(#otherSecObject) ") 吗?

更新

为什么在第一种情况下 Spring Security 调用 .equals(),而第二种不调用?

      //TestObject 

      public class TestObject {

      private static final Logger log = LoggerFactory.getLogger(TestObject.class);

      private Long id;

      public TestObject(Long id) {
           this.id = id;
      }

       @Override
       public int hashCode() {
           int hash = 7;
           hash = 71 * hash + Objects.hashCode(this.id);
           return hash;
       }

       @Override
       public boolean equals(Object obj) {

       log.info("equals");

       if (obj == null) {
          return false;
       }

       if (getClass() != obj.getClass()) {
         return false;
       }
       final TestObject other = (TestObject) obj;
       if (!Objects.equals(this.id, other.id)) {
         return false;
       }
      return true;
  }               
}

//TestService

@PreAuthorize(" #one == #two ")
public String testEqualsInAnnotation(Long one, Long two) {        
    //...
}

@Override
@PreAuthorize(" #one == #two ")
public String testEqualsInAnnotation(TestObject one, TestObject two) {
    //...
}

//Test

    log.info("for Long");
    Long one = new Long(500);
    Long two = new Long(500);        

    log.info("one == two: {}", (one==two)? true : false); // print false
    log.info("one equals two: {}", (one.equals(two))? true : false); // print true

    testService.testEqualsInAnnotation(one, two); //OK

    log.info("for TestObject");

    TestObject oneObj = new TestObject(new Long(500));
    TestObject twoObj = new TestObject(new Long(500));              

    log.info("oneObj == twoObj: {}", (oneObj==twoObj)? true : false); // print false
    log.info("oneObj equals twoObj: {}", (oneObj.equals(twoObj))? true : false); // print true

    testService.testEqualsInAnnotation(oneObj, twoObj); // AccessDeniedException: Access is denied

更新 2

equals() 从来没有被调用过

           package org.springframework.expression.spel.ast;

           import org.springframework.expression.EvaluationException;
           import org.springframework.expression.spel.ExpressionState;
           import org.springframework.expression.spel.support.BooleanTypedValue;

/**
 * Implements equality operator.
 *
 * @author Andy Clement
 * @since 3.0
 */
public class OpEQ extends Operator {

    public OpEQ(int pos, SpelNodeImpl... operands) {
        super("==", pos, operands);
    }

    @Override
    public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
        Object left = getLeftOperand().getValueInternal(state).getValue();
        Object right = getRightOperand().getValueInternal(state).getValue();
        if (left instanceof Number && right instanceof Number) {
            Number op1 = (Number) left;
            Number op2 = (Number) right;
            if (op1 instanceof Double || op2 instanceof Double) {
                return BooleanTypedValue.forValue(op1.doubleValue() == op2.doubleValue());
            } else if (op1 instanceof Long || op2 instanceof Long) {
                return BooleanTypedValue.forValue(op1.longValue() == op2.longValue());
            } else {
                return BooleanTypedValue.forValue(op1.intValue() == op2.intValue());
            }
        }
        if (left!=null && (left instanceof Comparable)) {
            return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) == 0);
        } else {
            return BooleanTypedValue.forValue(left==right);
        }
    }

}
4

3 回答 3

3

根据 spEL 文档,您需要创建ExpressionParser实例,创建Expression实例并获取如下值

String name = "Nikola Tesla";
Expression exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(Boolean.class); 

结果评估为“真”。也就是说,当我们需要比较任何两个对象时,我们需要重写该equals()方法并将两个对象传递给 parser#parseExpression("obj1 == obj2") ,然后调用exp#getValue(Boolean.class)来评估。以类似的方式,Expression 实例也可以包含Obj1.equals(Obj2)用于检查相等性的表达式字符串。因此,使用 spEL 可以使用两种检查相等性的方法。

于 2013-10-18T18:44:00.297 回答
2

您可能已经发现了这一点,因为它在OpEq原始帖子的“更新 2”中的代码中,但是......

比较运算符lt < gt > le <= ge >= eq == ne !=基于 java 的Comparable接口。

因此,如果您有一个自定义类型,您希望能够使用==!=在 SpEL 表达式中进行比较,那么您可以编写它来实现Comparable. 当然,然后你必须找出一些理智的规则来决定当它们不相等时哪个对象在另一个之前。

也就是说,我在 Spring 的当前文档中找不到任何表明这一点的内容。

于 2016-06-07T16:34:40.543 回答
1

rdm,我认为您必须使用权限评估器来评估表达式。我认为您并没有真正为以下表达式中的对象注入/传递值。

@Override
@PreAuthorize(" #one == #two ")
public String testEqualsInAnnotation(TestObject one, TestObject two) {
    //...

我尝试做同样的事情,但我未能传递值,因此无法评估表达式。我的建议是为上述表达式实现您的自定义权限评估器,并从评估器注入/传递值。概括我的想法,我怀疑对象是空的,这就是为什么你无法评估它。如果您真的可以在这里传递对象的值,请告诉我们:@PreAuthorize(" #one == #two ")

Added:

我正在使用权限评估器来评估 @PreAuthorize(...) 注释下的表达式。因为我无法像上面解释的那样将值传递给参数。如果可以传递/注入值,则最好降低使用权限评估器可能带来的复杂性。

rdm 或其他人,如果可能的话,你能告诉我如何传递 @PreAuthorize(...) 下的参数值吗?

很抱歉在 rdm 的帖子上提出另一个问题,并提前感谢您的帮助!

于 2013-10-30T00:16:56.883 回答