这个例子使用Guice来演示如何通过AOP来实现权限管理,当然,这只是一个demo性质的东东,但基本原理还是一样的。

package app;

public class Person {

private final String name; 

private final String email; 

public Person(String name, String email) {

    this.name = name;

    this.email = email;

}

public String getName() {

    return name;

}

public String getEmail() {

    return email;

}

@Override

public String toString() {

    return new StringBuffer(name)

            .append(" <")

            .append(email)

            .append(">")

            .toString();

}

@Override

public boolean equals(Object o) {

    if (this == o) return true;

    if (o == null || getClass() != o.getClass()) return false;

    Person person = (Person) o;

    if (!email.equals(person.email)) return false;

    if (!name.equals(person.name)) return false;

    return true;

}

@Override

public int hashCode() {

    int result = name.hashCode();

    result = 31 * result + email.hashCode();

    return result;

}

}

然后是一个ContactManager接口,注意add和remove方法都有返回值,从而是可以chain的。

package app;

public interface ContactManager {

public ContactManager add(Person person);

public ContactManager remove(Person person);

public Person lookup(String name);

}

我们的目的是实现一个基本的权限控制机制,使得具有某种角色的用户才能访问特定的方法,当然,这种机制的实现是declarative的,因此,需要如下两个annotation:

package app.auth;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface WithUserProfileVerification {

}

以及:

package app.auth;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface RequiresProfile {

UserProfile value();

}

接下来用枚举类型来实现三种UserProfile:

package app.auth;

public enum UserProfile {

ANONYMOUS, USER, ADMIN

}

现在可以提供一个ContactManager的实现,并且使用声明式的权限控制了:

package demo;

import app.ContactManager;

import app.Person;

import app.auth.RequiresProfile;

import static app.auth.UserProfile.ADMIN;

import static app.auth.UserProfile.USER;

import app.auth.WithUserProfileVerification;

import java.util.HashSet;

import java.util.Set;

@WithUserProfileVerification

public class ContactManagerImpl implements ContactManager {

private final Set<person> contacts = new HashSet<person>();

@RequiresProfile(ADMIN)

public ContactManager add(Person person) {

    contacts.add(person);

    return this;

}

@RequiresProfile(ADMIN)

public ContactManager remove(Person person) {

    contacts.remove(person);

    return this;

}

@RequiresProfile(USER)

public Person lookup(String name) {

    for (Person person : contacts) {

        if (person.getName().equals(name)) {

            return person;

        }

    }

    return null;

}

}

那么,如何使这个机制运行起来呢?接下来我们还需要做如下工作:

首先提供一个检查UserProfile的接口:

package app.auth;

public interface UserProfileChecker {

public UserProfile getCurrentUserProfile(); 

public UserProfile login(String login, String password); 

public UserProfile logout(); 

}

及其实现类:

package demo;

import app.auth.UserProfile;

import static app.auth.UserProfile.*;

import app.auth.UserProfileChecker;

public class DumbUserProfileChecker implements UserProfileChecker {

private UserProfile userProfile = ANONYMOUS;

public UserProfile getCurrentUserProfile() {

    return userProfile;

}

public UserProfile login(String login, String password) {

    if (login.equals("Julien") && password.equals("secret")) {

        userProfile = ADMIN;

    } else if (login.equals("Jean-Jacques") && password.equals("1234")) {

        userProfile = USER;

    } else {

        userProfile = ANONYMOUS;

    }

    return getCurrentUserProfile();

}

public UserProfile logout() {

    userProfile = ANONYMOUS;

    return userProfile;

}

}

接下来就是实现一个aspect将ProfileChecker和ContactManager关联起来

package demo;

import app.auth.RequiresProfile;

import app.auth.UserProfile;

import static app.auth.UserProfile.ADMIN;

import static app.auth.UserProfile.USER;

import app.auth.UserProfileChecker;

import com.google.inject.Inject;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

public class UserProfileInterceptor implements MethodInterceptor

{

@Inject

private UserProfileChecker profileChecker;

public Object invoke(MethodInvocation methodInvocation) throws Throwable {

    UserProfile required = methodInvocation.getMethod()

        .getAnnotation(RequiresProfile.class).value();

    UserProfile current = profileChecker.getCurrentUserProfile();

    if (insufficientProfile(required, current)) {

        throw new RuntimeException(

          "The current user profile (" + current + ") is not sufficient: " 

        + required);

    } else {

        return methodInvocation.proceed();

    }

}

private boolean insufficientProfile(UserProfile required, UserProfile current) {

    return (required == ADMIN && current != ADMIN)

            || (required == USER && (current != USER && current != ADMIN));

}

}

最后,我们用Guice将所有的这些组装起来:

package demo;

import app.ContactManager;

import app.Person;

import app.auth.RequiresProfile;

import app.auth.UserProfileChecker;

import app.auth.WithUserProfileVerification;

import com.google.inject.*;

import static com.google.inject.matcher.Matchers.*;

public class Main {

public static void main(String[] args) {

    new Main().execute();

}

private Module guiceModule = new AbstractModule() {

    @Override

    protected void configure() {

        bind(ContactManager.class)

                .to(ContactManagerImpl.class)

                .in(Singleton.class);

        bind(UserProfileChecker.class)

                .to(DumbUserProfileChecker.class)

                .in(Singleton.class);

        UserProfileInterceptor userProfileInterceptor = new UserProfileInterceptor();

        requestInjection(userProfileInterceptor);

        bindInterceptor(

                annotatedWith(WithUserProfileVerification.class),

                annotatedWith(RequiresProfile.class),

                userProfileInterceptor);

    }

};

private Injector injector = Guice.createInjector(guiceModule);

private void execute() {

    ContactManager contacts = injector.getInstance(ContactManager.class);

    UserProfileChecker profileChecker = injector.getInstance(UserProfileChecker.class);

    profileChecker.login("Julien", "secret");

    contacts.add(new Person("Julien Ponge", "[email protected]"));

    contacts.add(new Person("Jean-Jacques", "[email protected]"));

    profileChecker.logout();

    profileChecker.login("Jean-Jacques", "1234");

   // profileChecker.login("Jean-Jacques", "balabala..");

    System.out.println(contacts.lookup("Julien Ponge"));

}

}

在这个例子中,如果Jean-Jacques用户登录密码错误,那么执行lookup方法时候,会抛出异常的。另外要注意的是requestInjection(userProfileInterceptor)这个方法的调用,一般的说来,aspects不是由DI容器管理的,我们必须手动的实例化aspects,因为aspects不是由容器实例化的,他的dependency不会自动注入,所以,需要使用requestInjection手动进行依赖注入。如果没有这一步,会抛出java.lang.NullPointerException异常,因为此时profileChecker为null。

原文:http://x-focus.appspot.com/blog/31001/

英文原文:http://jpz-log.info/archives/2009/11/04/guice-it-up-or-aop-can-be-made-simple-sometimes/

Comments
Write a Comment