15

我可以找到在 Java 中调用方法的对象吗?我有一个与团体和个人的社交网络。如果一个人想离开一个组,只有那个可以将自己从组中删除,没有其他人可以删除那个人,调用该方法的人必须以某种方式证明它的身份。

4

3 回答 3

10

检查方法的调用者是缺乏经验的程序员的常见请求。我很惊讶它在 SO 上出现的频率并不高。但这是一个非常糟糕的主意(只需查看 Java 2(可能更早的)安全模型)。

在少数情况下实施此限制很重要。正如 Oli Charlesworth 所说,不要这样做。但是让我们假设它很重要,但我们不打算进行堆栈检查。

让我们首先假设我们信任该组。一种可行但荒谬的方法是向组传递一个代表只有该人才能创建的人的对象。(注意 Java 语言访问限制是基于类的。不同的实例可以创建这样的替代,但代码必须在Person类中。)

public final class Group { // Can't have a malicious subclass.
    public void deregisterPerson(Person.Standin standin) {
        Person person = standin.person();
        ...
    }
}
public class Person { // May be subclassed.
    public final class Standin { // Could be one per Person.
        private Standin() { // Hide constructor from other outer classes.
        }
        public Person person() {
            return Person.this;
        }
    }
    private void groupDeregister(Group group) {
        group.deregisterPerson(new Standin());
    }
 }

如果我们不信任该组,那么我们可以扩展替身以引用该组。这可以防止恶意组从其他组中注销某人。

public class Group { // Can have malicious subclasses.
    public void deregisterPerson(Person.GroupDeregister deregister) {
        if (deregister.group() != this) { // Not equals...
            throw new IllegalArgumentException();
        }
        Person person = deregister.person();
        ...
    }
}
public class Person { // May be subclassed.
    public final class GroupDeregister {
        private final Group group;
        private GroupDeregister(Group group) { // Hidden.
            this.group = group;
        }
        public Person person() {
            return Person.this;
        }
        public Group group() {
            return group;
        }
    }
    private void groupDeregister(Group group) {
        group.deregisterPerson(new GroupDeregister(group));
    }
 }

另一种方法是制作可以公开给其他人的“公共”版本的 Person。

public class Person { // "PrivatePerson"
    public PublicPerson publicPerson() {
         return new PublicPerson(this);
    }
    private void groupRegister(Group group) {
        group.registerPerson(this);
    }
    private void groupDeregister(Group group) {
        group.deregisterPerson(this);
    }
    ...
}
public class PublicPerson {
    private final Person person;
    public PublicPerson(Person person) {
        this.person = person;
    }
    @Override public final boolean equals(Object obj) {
        return obj instanceof Person && (Person)obj.person == person;
    }
    @Override public final int hashCode() {
        return person.hashCode();
    }
    ...methods, but no raw registration...
 }
 public class Group {
     private final Set<Person> members = new IdentityHashSet<>(); // No Object.equals.
     public void registerPerson(Person person) {
         members.add(person);
     }
     public void deregisterPerson(Person person) {
         members.remove(person);
     }
     public Set<PublicPerson> members() {
         // This will be more concise in Java SE 8.
         Set<PublicPerson> publics = new HashSet<>();
         for (Member member : members) {
             publics.add(member.publicPerson());
         }
         return unmodifiableSet(publics);
     }
}

Objects.requireNonNull为了“简洁”而省略。)

于 2013-03-11T03:41:57.700 回答
5

您可以通过分析堆栈跟踪来使用反射来做到这一点(如本问题所述:如何使用堆栈跟踪或反射找到方法的调用者?)。

然而,在大多数情况下,这将是对反射的滥用。您应该强烈考虑让您的方法采用一个额外的参数调用caller(调用者应该填充this)。

于 2013-03-11T00:44:57.753 回答
0

Oli 解决方案的风险在于恶意代码可能会调用“删除”方法并指定不同的调用者对象。

如果您正在使用诸如 Spring Security 之类的安全框架,您可以询问当前主体,并且只允许从组中删除该用户,或者如果该用户是管理员,则允许从组中删除任何用户。

于 2013-03-11T00:51:40.480 回答