學(xué)習acegi-security
這幾天對acegi研究了一下,現對這幾天的研究做個(gè)總結。
網(wǎng)上資料對于acegi在web上的應用非常多,所以特意不從web入手,而從一般的java方法入手。
acegi的基本原理就是利用攔截器,對請求進(jìn)行攔截,在攔截前和攔截后做些安全驗證,以達到安全目的。所謂安全,一般包括兩個(gè)部分,一為認證(authentication),二為授權(authorization)。
1,攔截器體系
acegi中攔截器為分為三類(lèi):
(1)filter攔截器(FilterSecurityInterceptor),主要對web應用進(jìn)行攔截,即利用servlet的filter進(jìn)行攔截。
(2)method攔截器,分為spring的method攔截器(MethodSecurityInterceptor)和aspectj的method攔截器(AspectJSecurityInterceptor)。
- public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware,
- MessageSourceAware {
- private AccessDecisionManager accessDecisionManager;
- private AfterInvocationManager afterInvocationManager;
- private ApplicationEventPublisher eventPublisher;
- private AuthenticationManager authenticationManager;
- protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
- private RunAsManager runAsManager = new NullRunAsManager();
- private boolean alwaysReauthenticate = false;
- private boolean rejectPublicInvocations = false;
- private boolean validateConfigAttributes = true;
- .........
-
- protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
- ............
- SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
- ............
- if (afterInvocationManager != null) {
- returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),token.getAttr(), returnedObject);
- }
- ............
- }
-
- protected InterceptorStatusToken beforeInvocation(Object object) {
- ............
- authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext().getAuthentication());
- ............
- this.accessDecisionManager.decide(authenticated, object, attr);
- ............
- Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);
- if (runAs == null) {
- ............
- return new InterceptorStatusToken(authenticated, false, attr, object);
- } else {
- ............
- SecurityContextHolder.getContext().setAuthentication(runAs);
- return new InterceptorStatusToken(authenticated, true, attr, object);
- }
- ............
- }
- }
-
- public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
- public void invoke(FilterInvocation fi) throws IOException, ServletException {
- if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
- && observeOncePerRequest) {
-
- fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
- } else {
-
- if (fi.getRequest() != null) {
- fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
- }
-
- InterceptorStatusToken token = super.beforeInvocation(fi);
-
- try {
- fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
- } finally {
- super.afterInvocation(token, null);
- }
- }
- }
- ............
- }
-
- public class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {
- public Object invoke(MethodInvocation mi) throws Throwable {
- Object result = null;
- InterceptorStatusToken token = super.beforeInvocation(mi);
-
- try {
- result = mi.proceed();
- } finally {
- result = super.afterInvocation(token, result);
- }
-
- return result;
- }
- ............
- }
-
- public class AspectJSecurityInterceptor extends AbstractSecurityInterceptor {
- public Object invoke(JoinPoint jp, AspectJCallback advisorProceed) {
- Object result = null;
- InterceptorStatusToken token = super.beforeInvocation(jp);
-
- try {
- result = advisorProceed.proceedWithObject();
- } finally {
- result = super.afterInvocation(token, result);
- }
-
- return result;
- }
- ............
- }
上面的代碼片斷已經(jīng)顯示,所有的真正參與驗證的代碼都在父類(lèi)AbstractSecurityInterceptor.beforeInvocation()之中進(jìn)行,而對于攔截器都只是做些委托罷了。這樣可以把具體的驗證代碼同攔截器分開(kāi),也有利于擴展,用其他的aop技術(shù)或攔截器進(jìn)行擴展,可以很輕松。
認證體系由AuthenticationManager負責,授權體系由AccessDecisionManager負責,RunAsManager是作為用戶(hù)身份轉換的手段。AfterInvocationManager留下了一個(gè)接口,可以擴展默認的授權體系,可以做一些其他額外的工作。
在A(yíng)bstractSecurityInterceptor.beforeInvocation()中,
首先進(jìn)行認證,authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext().getAuthentication());
其次進(jìn)行授權,this.accessDecisionManager.decide(authenticated, object, attr);
AbstractSecurityInterceptor.afterInvocation()中,
做其他擴展,returnedObject =afterInvocationManager.decide(token.getAuthentication(),token.getSecureObject(),token.getAttr(), returnedObject);
2,認證體系
2.1認證管理器
認證體系的核心為AuthenticationManager,他的方法authenticate(Authentication authentication)負責所有的認證。在acegi中,由具體類(lèi)ProviderManager來(lái)進(jìn)行認證過(guò)程。
- public interface AuthenticationManager {
- public Authentication authenticate(Authentication authentication)
- throws AuthenticationException;
- }
-
- public abstract class AbstractAuthenticationManager
- implements AuthenticationManager{
- public final Authentication authenticate(Authentication authRequest)
- throws AuthenticationException {
- try {
- Authentication authResult = doAuthentication(authRequest);
- copyDetails(authRequest, authResult);
-
- return authResult;
- } catch (AuthenticationException e) {
- e.setAuthentication(authRequest);
- throw e;
- }
- }
-
- private void copyDetails(Authentication source, Authentication dest) {
- if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
- AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
-
- token.setDetails(source.getDetails());
- }
- }
-
- protected abstract Authentication doAuthentication(Authentication authentication)
- throws AuthenticationException;
-
- .........
- }
-
- public class ProviderManager extends AbstractAuthenticationManager implements InitializingBean,
- ApplicationEventPublisherAware, MessageSourceAware {
- private List providers;
- ............
- public Authentication doAuthentication(Authentication authentication)
- throws AuthenticationException {
- .........
- Iterator iter = providers.iterator();
- ............
- while (iter.hasNext()) {
- .............
- AuthenticationProvider provider = (AuthenticationProvider) iter.next();
- .........
- result = provider.authenticate(authentication);
- ............
- }
- ............
- }
- .........
- }
從上面代碼片斷可以看出,真正的認證過(guò)程是在ProviderManager.doAuthentication()中進(jìn)行的。而ProviderManager并不是具體的認證者,他只是個(gè)管理器,它要將具體的認證過(guò)程委托給具體的認證器提供者AuthenticationProvider去做。
2.2認證提供者
認證提供者就有很多了,可以提供各種各樣的認證。如dao,ldap,anonymous,authbyadapter,cas,jaas,remeberme,remote,runnasimpl,testing,x509等。
- public interface AuthenticationProvider {
- public Authentication authenticate(Authentication authentication) throws AuthenticationException;
- public boolean supports(Class authentication);
- }
具體的認證提供者類(lèi)就不詳細分析了,只提個(gè)名字:DaoAuthenticationProvider,LdapAuthenticationProvider,AnonymousAuthenticationProvider,AuthByAdapterProvider,CasAuthenticationProvider,JaasAuthenticationProvider,RememberMeAuthenticationProvider,RemoteAuthenticationProvider,RunAsImplAuthenticationProvider,TestingAuthenticationProvider,X509AuthenticationProvider。
3,授權體系
3.1授權管理器
授權體系的核心為授權管理器(AccessDecisionManager),它的方法decide(Authenticationauthentication, Object object, ConfigAttributeDefinitionconfig)進(jìn)行具體的授權動(dòng)作。
- public interface AccessDecisionManager {
-
- public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
- throws AccessDeniedException, InsufficientAuthenticationException;
- public boolean supports(ConfigAttribute attribute);
- public boolean supports(Class clazz);
- }
-
- public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean,
- MessageSourceAware {
- private List decisionVoters;
- protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
- private boolean allowIfAllAbstainDecisions = false;
-
- public boolean supports(ConfigAttribute attribute) {
- Iterator iter = this.decisionVoters.iterator();
-
- while (iter.hasNext()) {
- AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
-
- if (voter.supports(attribute)) {
- return true;
- }
- }
-
- return false;
- }
-
- public boolean supports(Class clazz) {
- Iterator iter = this.decisionVoters.iterator();
-
- while (iter.hasNext()) {
- AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
-
- if (!voter.supports(clazz)) {
- return false;
- }
- }
-
- return true;
- }
- ..............
- }
-
- public class AffirmativeBased extends AbstractAccessDecisionManager {
- public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
- throws AccessDeniedException {
- Iterator iter = this.getDecisionVoters().iterator();
- int deny = 0;
-
- while (iter.hasNext()) {
- AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
- int result = voter.vote(authentication, object, config);
-
- switch (result) {
- case AccessDecisionVoter.ACCESS_GRANTED:
- return;
-
- case AccessDecisionVoter.ACCESS_DENIED:
- deny++;
-
- break;
-
- default:
- break;
- }
- }
-
- if (deny > 0) {
- throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
- "Access is denied"));
- }
-
-
- checkAllowIfAllAbstainDecisions();
- }
- ..............
- }
-
- public class ConsensusBased extends AbstractAccessDecisionManager {
- public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
- throws AccessDeniedException {
- Iterator iter = this.getDecisionVoters().iterator();
- int grant = 0;
- int deny = 0;
- int abstain = 0;
-
- while (iter.hasNext()) {
- AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
- int result = voter.vote(authentication, object, config);
-
- switch (result) {
- case AccessDecisionVoter.ACCESS_GRANTED:
- grant++;
-
- break;
-
- case AccessDecisionVoter.ACCESS_DENIED:
- deny++;
-
- break;
-
- default:
- abstain++;
-
- break;
- }
- }
-
- if (grant > deny) {
- return;
- }
-
- if (deny > grant) {
- throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
- "Access is denied"));
- }
-
- if ((grant == deny) && (grant != 0)) {
- if (this.allowIfEqualGrantedDeniedDecisions) {
- return;
- } else {
- throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
- "Access is denied"));
- }
- }
-
-
- checkAllowIfAllAbstainDecisions();
- }
- ..............
- }
-
- public class UnanimousBased extends AbstractAccessDecisionManager {
- public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
- throws AccessDeniedException {
- int grant = 0;
- int abstain = 0;
-
- Iterator configIter = config.getConfigAttributes();
-
- while (configIter.hasNext()) {
- ConfigAttributeDefinition thisDef = new ConfigAttributeDefinition();
- thisDef.addConfigAttribute((ConfigAttribute) configIter.next());
-
- Iterator voters = this.getDecisionVoters().iterator();
-
- while (voters.hasNext()) {
- AccessDecisionVoter voter = (AccessDecisionVoter) voters.next();
- int result = voter.vote(authentication, object, thisDef);
-
- switch (result) {
- case AccessDecisionVoter.ACCESS_GRANTED:
- grant++;
-
- break;
-
- case AccessDecisionVoter.ACCESS_DENIED:
- throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
- "Access is denied"));
-
- default:
- abstain++;
-
- break;
- }
- }
- }
-
-
- if (grant > 0) {
- return;
- }
-
-
- checkAllowIfAllAbstainDecisions();
- }
- ..............
- }
授權管理器AccessDecisionManager默認有三個(gè)實(shí)現,具體為AffirmativeBased,ConsensusBased,UnanimousBased。三個(gè)具體實(shí)現都大同小異,主要在具有角色是否應該授權上。
而具體能否單個(gè)角色是否授權,是委派給AccessDecisionVoter去做的。
3.2授權投票者
授權投票責的核心是接口A(yíng)ccessDecisionVoter。他有幾個(gè)具體實(shí)現類(lèi):BasicAclEntryVoter,AuthenticatedVoter,RoleVoter。
- public interface AccessDecisionVoter {
-
- public static final int ACCESS_GRANTED = 1;
- public static final int ACCESS_ABSTAIN = 0;
- public static final int ACCESS_DENIED = -1;
-
- public boolean supports(ConfigAttribute attribute);
- public boolean supports(Class clazz);
- public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config);
- }
-
- public abstract class AbstractAclVoter implements AccessDecisionVoter {
- public boolean supports(Class clazz) {
- if (MethodInvocation.class.isAssignableFrom(clazz)) {
- return true;
- } else if (JoinPoint.class.isAssignableFrom(clazz)) {
- return true;
- } else {
- return false;
- }
- }
- ............
- }
-
- public class BasicAclEntryVoter extends AbstractAclVoter implements InitializingBean {
- private AclManager aclManager;
- private String internalMethod;
- private String processConfigAttribute;
- private int[] requirePermission;
-
- public boolean supports(ConfigAttribute attribute) {
- if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getProcessConfigAttribute())) {
- return true;
- } else {
- return false;
- }
- }
-
- public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
- Iterator iter = config.getConfigAttributes();
-
- while (iter.hasNext()) {
- ConfigAttribute attr = (ConfigAttribute) iter.next();
-
- if (this.supports(attr)) {
-
-
- Object domainObject = getDomainObjectInstance(object);
-
-
- if (domainObject == null) {
- if (logger.isDebugEnabled()) {
- logger.debug("Voting to abstain - domainObject is null");
- }
-
- return AccessDecisionVoter.ACCESS_ABSTAIN;
- }
-
-
- if ((internalMethod != null) && !"".equals(internalMethod)) {
- try {
- Class clazz = domainObject.getClass();
- Method method = clazz.getMethod(internalMethod, new Class[] {});
- domainObject = method.invoke(domainObject, new Object[] {});
- } catch (NoSuchMethodException nsme) {
- throw new AuthorizationServiceException("Object of class '" + domainObject.getClass()
- + "' does not provide the requested internalMethod: " + internalMethod);
- } catch (IllegalAccessException iae) {
- if (logger.isDebugEnabled()) {
- logger.debug("IllegalAccessException", iae);
-
- if (iae.getCause() != null) {
- logger.debug("Cause: " + iae.getCause().getMessage(), iae.getCause());
- }
- }
-
- throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
- + " for object: " + domainObject);
- } catch (InvocationTargetException ite) {
- if (logger.isDebugEnabled()) {
- logger.debug("InvocationTargetException", ite);
-
- if (ite.getCause() != null) {
- logger.debug("Cause: " + ite.getCause().getMessage(), ite.getCause());
- }
- }
-
- throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
- + " for object: " + domainObject);
- }
- }
-
-
- AclEntry[] acls = aclManager.getAcls(domainObject, authentication);
-
-
- if ((acls == null) || (acls.length == 0)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Voting to deny access - no ACLs returned for this principal");
- }
-
- return AccessDecisionVoter.ACCESS_DENIED;
- }
-
-
- for (int i = 0; i < acls.length; i++) {
-
- if (acls[i] instanceof BasicAclEntry) {
- BasicAclEntry processableAcl = (BasicAclEntry) acls[i];
-
-
- for (int y = 0; y < requirePermission.length; y++) {
- if (processableAcl.isPermitted(requirePermission[y])) {
- if (logger.isDebugEnabled()) {
- logger.debug("Voting to grant access");
- }
-
- return AccessDecisionVoter.ACCESS_GRANTED;
- }
- }
- }
- }
-
-
- if (logger.isDebugEnabled()) {
- logger.debug(
- "Voting to deny access - ACLs returned, but insufficient permissions for this principal");
- }
-
- return AccessDecisionVoter.ACCESS_DENIED;
- }
- }
-
- return AccessDecisionVoter.ACCESS_ABSTAIN;
- }
- ...............
- }
-
-
- public class AuthenticatedVoter implements AccessDecisionVoter {
-
- public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY";
- public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED";
- public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY";
-
- private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
-
- private boolean isFullyAuthenticated(Authentication authentication) {
- return (!authenticationTrustResolver.isAnonymous(authentication)
- && !authenticationTrustResolver.isRememberMe(authentication));
- }
-
- public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
- Assert.notNull(authenticationTrustResolver, "AuthenticationTrustResolver cannot be set to null");
- this.authenticationTrustResolver = authenticationTrustResolver;
- }
-
- public boolean supports(ConfigAttribute attribute) {
- if ((attribute.getAttribute() != null)
- && (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())
- || IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())
- || IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute()))) {
- return true;
- } else {
- return false;
- }
- }
-
-
- public boolean supports(Class clazz) {
- return true;
- }
-
- public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
- int result = ACCESS_ABSTAIN;
- Iterator iter = config.getConfigAttributes();
-
- while (iter.hasNext()) {
- ConfigAttribute attribute = (ConfigAttribute) iter.next();
-
- if (this.supports(attribute)) {
- result = ACCESS_DENIED;
-
- if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {
- if (isFullyAuthenticated(authentication)) {
- return ACCESS_GRANTED;
- }
- }
-
- if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {
- if (authenticationTrustResolver.isRememberMe(authentication)
- || isFullyAuthenticated(authentication)) {
- return ACCESS_GRANTED;
- }
- }
-
- if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {
- if (authenticationTrustResolver.isAnonymous(authentication) || isFullyAuthenticated(authentication)
- || authenticationTrustResolver.isRememberMe(authentication)) {
- return ACCESS_GRANTED;
- }
- }
- }
- }
-
- return result;
- }
- }
-
- public class RoleVoter implements AccessDecisionVoter {
- private String rolePrefix = "ROLE_";
-
- public boolean supports(ConfigAttribute attribute) {
- if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
- return true;
- } else {
- return false;
- }
- }
-
-
- public boolean supports(Class clazz) {
- return true;
- }
-
- public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
- int result = ACCESS_ABSTAIN;
- Iterator iter = config.getConfigAttributes();
-
- while (iter.hasNext()) {
- ConfigAttribute attribute = (ConfigAttribute) iter.next();
-
- if (this.supports(attribute)) {
- result = ACCESS_DENIED;
-
- for (int i = 0; i < authentication.getAuthorities().length; i++) {
- if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {
- return ACCESS_GRANTED;
- }
- }
- }
- }
- return result;
- }
- }
這三個(gè)授權投票實(shí)現類(lèi)中 acl 又最復雜。他會(huì )委托給acl管理器(AclManager)來(lái)做具體的授權工作。
3.3acl授權體系
AclManager只有一個(gè)實(shí)現類(lèi)AclProviderManager ,負責提供acl授權實(shí)體。
- public interface AclManager {
-
- public AclEntry[] getAcls(Object domainInstance);
- public AclEntry[] getAcls(Object domainInstance, Authentication authentication);
- }
-
- public class AclProviderManager implements AclManager, InitializingBean {
- public AclEntry[] getAcls(Object domainInstance) {
- Assert.notNull(domainInstance, "domainInstance is null - violating interface contract");
-
- Iterator iter = providers.iterator();
-
- while (iter.hasNext()) {
- AclProvider provider = (AclProvider) iter.next();
-
- if (provider.supports(domainInstance)) {
- if (logger.isDebugEnabled()) {
- logger.debug("ACL lookup using " + provider.getClass().getName());
- }
-
- return provider.getAcls(domainInstance);
- }
- }
-
- if (logger.isDebugEnabled()) {
- logger.debug("No AclProvider found for " + domainInstance.toString());
- }
-
- return null;
- }
-
- public AclEntry[] getAcls(Object domainInstance, Authentication authentication) {
- Assert.notNull(domainInstance, "domainInstance is null - violating interface contract");
- Assert.notNull(authentication, "authentication is null - violating interface contract");
-
- Iterator iter = providers.iterator();
-
- while (iter.hasNext()) {
- AclProvider provider = (AclProvider) iter.next();
-
- if (provider.supports(domainInstance)) {
- if (logger.isDebugEnabled()) {
- logger.debug("ACL lookup using " + provider.getClass().getName());
- }
-
- return provider.getAcls(domainInstance, authentication);
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("Provider " + provider.toString() + " does not support " + domainInstance);
- }
- }
- }
-
- if (logger.isDebugEnabled()) {
- logger.debug("No AclProvider found for " + domainInstance.toString());
- }
-
- return null;
- }
- .........
- }