这个例子使用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", "julien.ponge@gmail.com"));
contacts.add(new Person("Jean-Jacques", "jean.jacques@gmail.com"));
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/