在Acegi中是由认证管理器确定用户身份。一个认证管理器由借口AuthenticationManager实现。
		
				
				
				public
				 
				interface
				 AuthenticationManager 
				
						
				
				
						{
    
						//
						~Methods ================================================================== 
						
								
								
						
						   

   
						/** *
						
								/**
								
										
										
     * 尝试验证用户身份,如果验证成功,将返回一个含授权的完整的Authentication对象。
     * 一个AuthenticationManager必须按照下面的规则处理异常:
     * 如果账号是不被允许的必须抛出DisabledException,AuthenticationManager能检测到这个状态。
     * 如果账号已经被锁定必须抛出LockedException,AuthenticationManager能够检测被锁定的账号。
     * 如果取到的是非法的信任状(credential?)必须抛出BadCredentialsException。同时上述的2种异常也是可选
     * 的,AuthenticationManager必须总是检测信任状(credential)。
     * 异常必须被检测,如果有上述的异常出现须抛出异常。
     * 如果一个账号是不被允许的或是被锁定的,认证请求会立即被拒绝,信任状的检测不会发生。
     * 这防止了对不被允许账号和被锁定的账号的信任状检测。
     *
     * 
								@param
								 authentication the authentication request object
     *
     * 
								@return
								 a fully authenticated object including credentials
     *
     * 
								@throws
								 AuthenticationException if authentication fails
    
								*/
						
						
								
								
    
						public
						 Authentication authenticate(Authentication authentication)
        
						throws
						 AuthenticationException;
}
				
		 
		 Authentication 继承了java.security.Principal,Principal实现了简单的主体(Principal)定义。
		
				
				
				public
				 
				interface
				 Principal 
				
						
				
				
						{

    
						public
						 
						boolean
						 equals(Object another);

    
						public
						 String toString();

    
						public
						 
						int
						 hashCode();


   
						/** */
						
								/**
								
										
										
     * 返回主体的名字
     *
     * 
								@return
								 the name of this principal.
    
								*/
						
						
								
								
    
						public
						 String getName();
}
				
		 
		 
		
				
				
				public
				 
				interface
				 Authentication 
				extends
				 Principal, Serializable 
				
						
				
				
						{
    
						//
						 ~Methods ==================================================================
						
								
								
						
						
								
								
								
 
						/** */
						
								/**
								
										
										
     * 通过AuthenticationManager设置Principal的授权信息。
     * 注意:当Class状态为valid时,除非Value已经被可信任的AuthenticationManager设置t过,否则对Class
     * 不起作用。(此句存疑 Note that classes should not rely on this value as being valid unless it has 
     * been set by AuthenticationManager)
     * Authentication的实现类需要确保修改返回的数足不会影响Authentication Object的状态。(比如返回一个
     * 数组的copy)。
     * 
     * 
								@return
								 the authorities granted to the principal, or null if authentication has not been completed
    
								*/
						
						
								
								
    
						public
						 GrantedAuthority[] getAuthorities();


  
						/** */
						
								/**
								
										
										
     * 信任状证明Principal的身份是合法的。它经常是密码,但是也可以是任何与AuthenticationManager
     * 相关的东西。
     *
     * 
								@return
								 the credentials that prove the identity of the Principal
    
								*/
						
						
								
								
    
						public
						 Object getCredentials();


  
						/** */
						
								/**
								
										
										
     * 储存认证请求的额外信息。有可能是IP地址,证件号码等。
     *
     * 
								@return
								 additional details about the authentication request, or  null if not used
    
								*/
						
						
								
								
    
						public
						 Object getDetails();


  
						/** */
						
								/**
								
										
										
     * 返回一个已经通过验证的Principal(这通常是一个用户名)。
     *
     * 
								@return
								 the Principal being authenticated
    
								*/
						
						
								
								
    
						public
						 Object getPrincipal();


  
						/** */
						
								/**
								
										
     * AbstractSecurityInterceptor将认证令牌交给AuthenticationManager。如果认证成功AuthenticaionManager
     * 会返回一个不变的认证令牌(返回true)。
     * 返回"true"会改善性能,因为不再是每一个请求都需要调用AuthenticationManager。
     * 由于安全上的考虑,实现这个接口返回true的时候需要十分的小心。除非他们不再变化或者有什么途径
     * 确保属性在最初的初始化后不再变化。
     *
     * 
								@return
								 true if the token has been authenticated and the  AbstractSecurityInterceptor 
     *        does not need to represent the token for re-authentication to the AuthenticationManager
    
								*/
						
						
								
								
    
						public
						 
						boolean
						 isAuthenticated();


  
						/** */
						
								/**
								
								
										
     * 详情参阅isAuthenticate()方法
     * 参数为true的情况下,如果某个实现希望拒绝一个调用,那么将抛出一个IllegalArgumentException异常。
								
										
										
     *
     * 
								@param
								 isAuthenticated true if the token should be trusted (which may result in an exception) or
     *        false if the token should not be trusted
     *
     * 
								@throws
								 IllegalArgumentException if an attempt to make the authentication token trusted (by 
     *        passing true as the argument) is rejected due to the implementation being immutable or
     *        implementing its own alternative approach to isAuthenticated()}
								
										
										
    
								*/
						
						
								
								
    
						public
						 
						void
						 setAuthenticated(
						boolean
						 isAuthenticated)
        
						throws
						 IllegalArgumentException;
}
				
				
						
						
				
		 
		 Acegi提供了一个能适应大多数情况的ProviderManager,实现了AuthenticationManager。
  ProviderManager继承抽象类AbstractAuthenticationManager:
		
				
				
				public
				 
				abstract
				 
				class
				 AbstractAuthenticationManager 
				implements
				 AuthenticationManager 
				
						
				
				
						{
    
						
								//
								
								
								 ~Methods ================================================================== 
						
								
						
						
								
								
								
 
						/** */
						
								/**
								
										
										
     * 实现通过调用抽象方法doAuthentication()进行工作。
     * 如果doAuthentication()方法抛出AuthenticationException异常,验证失败。
     *
     * 
								@param
								 authRequest the authentication request object
     *
     * 
								@return
								 a fully authenticated object including credentials
     *
     * 
								@throws
								 AuthenticationException if authentication fails
    
								*/
						
						
								
								
    
						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;
        }
								
								
										
										
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 在目标Authentication的detail没有被设置的情况下从源Authentication复制detail信息。
     *
     * 
								@param
								 source source authentication
     * 
								@param
								 dest the destination authentication object
    
								*/
						
						
								
								
								
    
						private
						 
						void
						 copyDetails(Authentication source, Authentication dest) 
						
								
						
						
								{

        
								if
								 ((dest 
								instanceof
								 AbstractAuthenticationToken) 
								&&
								 (dest.getDetails() 
								==
								 
								null
								)) 
								
										
								
								
										{
            AbstractAuthenticationToken token 
										=
										 (AbstractAuthenticationToken) dest;

            token.setDetails(source.getDetails());
        }
								
								
										
										
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 具体的实现通过覆写此方法提供认证服务,此方法的约束详见AuthenticationManager的authenticate()方法
     *
     * 
								@param
								 authentication the authentication request object
     *
     * 
								@return
								 a fully authenticated object including credentials
     *
     * 
								@throws
								 AuthenticationException if authentication fails
    
								*/
						
						
								
								
    
						protected
						 
						abstract
						 Authentication doAuthentication(Authentication authentication)
        
						throws
						 AuthenticationException;
}
				
				
						
						
				
		 
		 
		
				
				
				/** *
				
						/**
						
								
								
     * 认证请求重复的通过一组AuthenticationProviders容器进行认证。
     * 可以通过配置ConcurrentSessionController来限制单个用户的session数目。
     * AuthenticationProvider试图得到一个非空的响应。非空的响应表明Provider已经对认证请求作出决议
     * 而不需要尝试其他的Provider。
     * 如果provider抛出一个AuthenticationException,这个异常将会被保留直到后续的providers都被尝试为止。
     * 如果认真请求在后续的Providers中被认证成功,早先的认证异常会被忽略而成功的认证信息将被使用。
     * 如果后续的Providers没有作出一个非空的响应或一个新的AuthenticationException,那么最后接收到的
     * AuthenticationException将会被使用。
     * 如果没有一个Provider返回一个非空的响应或是表明能运行一个Authentication,那么ProviderManager将会
     * 抛出一个ProviderNotFoundException异常。
     * 如果一个有效的Authentication被AuthenticationProvider返回,ProviderManager将会发布一个org
     * .acegisecurity.event.authentication.AuthenticationSuccessEvent。
     * 如果发现AuthenticationException,最后的AuthenticationException会发布一个适当的失败事件。
     * 默认的ProviderManager将异常和事件对应起来,我们可以通过定一个新的exceptionMappings Properties
     * 来调整对应关系。
     * 在properties中,key表现的是异常的类名的完整路径,value表现的是AbstractAuthenticationFailureEvent的
     * 子类,提供子类的构造。
     *
     * 
						@see
						 ConcurrentSessionController
     */
				
						
						
				
				public
				 
				class
				 ProviderManager 
				extends
				 AbstractAuthenticationManager 
				implements
				 InitializingBean,

    ApplicationEventPublisherAware, MessageSourceAware 
				
						
				
				
						{
    
						//
						~ Static fields/initializers ======================================================= 
						
								
								
						
						
								
								
    
						private
						 
						static
						 
						final
						 Log logger 
						=
						 LogFactory.getLog(ProviderManager.
						class
						);

    
						//
						~ Instance fields ============================================================= 
						
								
								
						
						
								
								
    
						private
						 ApplicationEventPublisher applicationEventPublisher;
    
						private
						 ConcurrentSessionController sessionController 
						=
						 
						new
						 NullConcurrentSessionController();
    
						private
						 List providers;
    
						protected
						 MessageSourceAccessor messages 
						=
						 AcegiMessageSource.getAccessor();
    
						private
						 Properties exceptionMappings;

    
						//
						~ Methods ================================================================== 
						
								
						
						
								
								
								
    
						public
						 
						void
						 afterPropertiesSet() 
						throws
						 Exception 
						
								
						
						
								{
        checkIfValidList(
								this
								.providers);
        Assert.notNull(
								this
								.messages, 
								"
								A message source must be set
								"
								);


        
								if
								 (exceptionMappings 
								==
								 
								null
								) 
								
										
								
								
										{
            exceptionMappings 
										=
										 
										new
										 Properties();
            exceptionMappings.put(AccountExpiredException.
										class
										.getName(),
                AuthenticationFailureExpiredEvent.
										class
										.getName());
            exceptionMappings.put(AuthenticationServiceException.
										class
										.getName(),
                AuthenticationFailureServiceExceptionEvent.
										class
										.getName());
            exceptionMappings.put(LockedException.
										class
										.getName(), AuthenticationFailureLockedEvent.
										class
										.getName());
            exceptionMappings.put(CredentialsExpiredException.
										class
										.getName(),
                AuthenticationFailureCredentialsExpiredEvent.
										class
										.getName());
            exceptionMappings.put(DisabledException.
										class
										.getName(), AuthenticationFailureDisabledEvent.
										class
										.getName());
            exceptionMappings.put(BadCredentialsException.
										class
										.getName(),
                AuthenticationFailureBadCredentialsEvent.
										class
										.getName());
            exceptionMappings.put(UsernameNotFoundException.
										class
										.getName(),
                AuthenticationFailureBadCredentialsEvent.
										class
										.getName());
            exceptionMappings.put(ConcurrentLoginException.
										class
										.getName(),
                AuthenticationFailureConcurrentLoginEvent.
										class
										.getName());
            exceptionMappings.put(ProviderNotFoundException.
										class
										.getName(),
                AuthenticationFailureProviderNotFoundEvent.
										class
										.getName());
            exceptionMappings.put(ProxyUntrustedException.
										class
										.getName(),
                AuthenticationFailureProxyUntrustedEvent.
										class
										.getName());
            doAddExtraDefaultExceptionMappings(exceptionMappings);
        }
								
								
										
										
    }
						
						
								
								
								
								
								
    
						private
						 
						void
						 checkIfValidList(List listToCheck) 
						
								
						
						
								{

        
								if
								 ((listToCheck 
								==
								 
								null
								) 
								||
								 (listToCheck.size() 
								==
								 
								0
								)) 
								
										
								
								
										{
            
										throw
										 
										new
										 IllegalArgumentException(
										"
										A list of AuthenticationManagers is required
										"
										);
        }
								
								
										
										
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 如果在启动期间没有exception被IoC容器注入,这个方法提供额外的异常对应。
     *
     * 
								@param
								 exceptionMappings the properties object, which already has entries in it
    
								*/
						
						
								
								
								
    
						protected
						 
						void
						 doAddExtraDefaultExceptionMappings(Properties exceptionMappings) 
						
								
						
						
								{}
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 尝试认证通过Authentication对象。
     * AuthenticationProviders组将会接连尝试认证对象,直到其中的一个通过这个Authentication对象。
     * 如果多个AuthenticationProvider通过了Authentication对象,那么只有第一个AuthenticationProvider
     * 产生结果,后续的AuthenticationProvider将不会被尝试。
     *
     * 
								@param
								 authentication the authentication request object.
     *
     * 
								@return
								 a fully authenticated object including credentials.
     *
     * 
								@throws
								 AuthenticationException if authentication fails.
    
								*/
						
						
								
								
    
						public
						 Authentication doAuthentication(Authentication authentication)

        
						throws
						 AuthenticationException 
						
								
						
						
								{
        Iterator iter 
								=
								 providers.iterator();

        Class toTest 
								=
								 authentication.getClass();

        AuthenticationException lastException 
								=
								 
								null
								;


        
								while
								 (iter.hasNext()) 
								
										
								
								
										{
            AuthenticationProvider provider 
										=
										 (AuthenticationProvider) iter.next();


            
										if
										 (provider.supports(toTest)) 
										
												
										
										
												{
                logger.debug(
												"
												Authentication attempt using 
												"
												 
												+
												 provider.getClass().getName());

                Authentication result 
												=
												 
												null
												;


                
												try
												 
												
														
												
												
														{
                    result 
														=
														 provider.authenticate(authentication);
                    sessionController.checkAuthenticationAllowed(result);

                }
												
												 
												catch
												 (AuthenticationException ae) 
												
														
												
												
														{
                    lastException 
														=
														 ae;
                    result 
														=
														 
														null
														;
                }
												
												
														
														
														
														
														
                
												if
												 (result 
												!=
												 
												null
												) 
												
														
												
												
														{
                    sessionController.registerSuccessfulAuthentication(result);
                    applicationEventPublisher.publishEvent(
														new
														 AuthenticationSuccessEvent(result));

                    
														return
														 result;
                }
												
												
														
														
            }
										
										
												
												
        }
								
								
										
										
										
										
										
        
								if
								 (lastException 
								==
								 
								null
								) 
								
										
								
								
										{
            lastException 
										=
										 
										new
										 ProviderNotFoundException(messages.getMessage(
										"
										ProviderManager.providerNotFound
										"
										,

                        
										new
										 Object[] 
										
												
										
										
												{toTest.getName()}
										
										, 
										"
										No AuthenticationProvider found for {0}
										"
										));
        }
								
								
										
										
										
										
        
								//
								 Publish the event
								
										
										
								
								        String className 
								=
								 exceptionMappings.getProperty(lastException.getClass().getName());
        AbstractAuthenticationEvent event 
								=
								 
								null
								;


        
								if
								 (className 
								!=
								 
								null
								) 
								
										
								
								
										{

            
										try
										 
										
												
										
										
												{
                Class clazz 
												=
												 getClass().getClassLoader().loadClass(className);

                Constructor constructor 
												=
												 clazz.getConstructor(
												new
												 Class[] 
												
														
												
												
														{
                            Authentication.
														class
														, AuthenticationException.
														class
														
																
																
                        }
												
												);

                Object obj 
												=
												 constructor.newInstance(
												new
												 Object[] 
												
														
												
												
														{authentication, lastException}
												
												);
                Assert.isInstanceOf(AbstractAuthenticationEvent.
												class
												, obj, 
												"
												Must be an AbstractAuthenticationEvent
												"
												);
                event 
												=
												 (AbstractAuthenticationEvent) obj;

            }
										
										 
										catch
										 (ClassNotFoundException ignored) 
										
												
										
										
												{}
										
										
												
												
												
            
										catch
										 (NoSuchMethodException ignored) 
										
												
										
										
												{}
										
										
												
												
												
            
										catch
										 (IllegalAccessException ignored) 
										
												
										
										
												{}
										
										
												
												
												
            
										catch
										 (InstantiationException ignored) 
										
												
										
										
												{}
										
										
												
												
												
            
										catch
										 (InvocationTargetException ignored) 
										
												
										
										
												{}
										
										
												
												
        }
								
								
										
										
										
										
										
        
								if
								 (event 
								!=
								 
								null
								) 
								
										
								
								
										{
            applicationEventPublisher.publishEvent(event);

        }
								
								 
								else
								 
								
										
								
								
										{

            
										if
										 (logger.isDebugEnabled()) 
										
												
										
										
												{
                logger.debug(
												"
												No event was found for the exception 
												"
												 
												+
												 lastException.getClass().getName());
            }
										
										
												
												
        }
								
								
										
										
										
										
        
								//
								 Throw the exception
								
										
										
								
								        
								throw
								 lastException;
    }
						
						
								
								
								
								
								
    
						public
						 List getProviders() 
						
								
						
						
								{
        
								return
								 
								this
								.providers;
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 返回设定的ConcurrentSessionController对象,如果对象没有被设置则返回一个
     * NullConcurrentSessionController(默认初始化的对象)
     *
     * 
								@return
								 ConcurrentSessionController instance
    
								*/
						
						
								
								
								
    
						public
						 ConcurrentSessionController getSessionController() 
						
								
						
						
								{
        
								return
								 sessionController;
    }
						
						
								
								
								
								
								
    
						public
						 
						void
						 setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) 
						
								
						
						
								{
        
								this
								.applicationEventPublisher 
								=
								 applicationEventPublisher;
    }
						
						
								
								
								
								
								
    
						public
						 
						void
						 setMessageSource(MessageSource messageSource) 
						
								
						
						
								{
        
								this
								.messages 
								=
								 
								new
								 MessageSourceAccessor(messageSource);
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 设置AuthenticationProvider对象
     * 
     * 
								@param
								 newList
     *
     * 
								@throws
								 IllegalArgumentException DOCUMENT ME!
    
								*/
						
						
								
								
								
    
						public
						 
						void
						 setProviders(List newList) 
						
								
						
						
								{
        checkIfValidList(newList);

        Iterator iter 
								=
								 newList.iterator();


        
								while
								 (iter.hasNext()) 
								
										
								
								
										{
            Object currentObject 
										=
										 
										null
										;


            
										try
										 
										
												
										
										
												{
                currentObject 
												=
												 iter.next();

                AuthenticationProvider attemptToCast 
												=
												 (AuthenticationProvider) currentObject;

            }
										
										 
										catch
										 (ClassCastException cce) 
										
												
										
										
												{
                
												throw
												 
												new
												 IllegalArgumentException(
												"
												AuthenticationProvider 
												"
												 
												+
												 currentObject.getClass().getName()
                    
												+
												 
												"
												 must implement AuthenticationProvider
												"
												);
            }
										
										
												
												
        }
								
								
										
										
										
										
        
								this
								.providers 
								=
								 newList;
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 设置ConcurrentSessionController来限制用户的session数量。
     * 默认设置为NullConcurrentSessionController
     *
     * 
								@param
								 sessionController ConcurrentSessionController
    
								*/
						
						
								
								
								
    
						public
						 
						void
						 setSessionController(ConcurrentSessionController sessionController) 
						
								
						
						
								{
        
								this
								.sessionController 
								=
								 sessionController;
    }
						
						
								
								
}
				
				
						
						
						
						
				
		 
		
				
  AuthenticationManager不依靠自己实现身份验证,而是通过Iterator逐个遍历AuthenticationProvider的子类集合(如果使用Spring的话子类类型由配置文件注入),直到某个Provider成功验证Authentication。
  Acegi提供的Provider实现包括:
  AuthByAdapterProvider、CasAuthenticationProvider、DaoAuthenticationProvider、JaasAuthenticationProvider、PasswordDaoAuthenticationProvider、RemoteAuthenticationProvider、RunAsImplAuthenticationProvider、TestingAuthenticationProvider。
  下面只分析DaoAuthenticationProvider的相关类,按照AuthenticationProvider-->AbstractUserDetailsAuthenticationProvider-->DaoAuthenticationProvider的顺序展开。
		
				
				
				/** */
				
						/**
						
								
								
     * 这个类处理一个Authenticaon类的具体实现
     */
				
						
						
						
				
				public
				 
				interface
				 AuthenticationProvider 
				
						
				
				
						{
    
						//
						~ Methods ==================================================================
						
								
								
						
						
								
								
								
 
						/** */
						
								/**
								
										
										
     * 和AuthenticationManager的同名方法实现同样的功能,详见AuthenticationManager
     *
     * 
								@param
								 authentication the authentication request object.
     *
     * 
								@return
								 a fully authenticated object including credentials. May return null if the
     *         AuthenticationProvider is unable to support authentication of the passed
     *         Authentication object. In such a case, the next AuthenticationProvider that
     *         supports the presented Authentication class will be tried.
     *
     * 
								@throws
								 AuthenticationException if authentication fails.
    
								*/
						
						
								
								
    
						public
						 Authentication authenticate(Authentication authentication)
        
						throws
						 AuthenticationException;


  
						/** */
						
								/**
								
										
										
     * 如果这个AuthenticationProvider支持通过Authentication对象,则返回True。
     * 返回True并不意味着AuthenticationProvider能认证当前的Authenctication。
     * 他只是简单的声明支持通过认证。AuthenticationProvider仍旧能够通过authenticate()方法返回null,
     * 使其它的Provider能够尝试认证这个Authentication。(存疑)
     * 选择哪一个AuthenticatonProvider履行鉴定是由ProviderManager在运行时管理的。
     *
     * 
								@param
								 authentication DOCUMENT ME!
     *
     * 
								@return
								 true if the implementation can more closely evaluate the Authentication class
     *         presented
    
								*/
						
						
								
								
    
						public
						 
						boolean
						 supports(Class authentication);
}
				
				
						
						
				
		 
		
		
				
				
				/** */
				
						/**
						
								
								
     * Authentication的基类,允许子类覆写他的方法以及使用UserDetails对象
     * 这个类被设计用来对UsernamePasswordAuthenticationToken的认证请求作出相应。
     * 在成功验证的基础上,UsernamePasswordAuthenticationToken会被构造同时返回给调用者。
     * 令牌可以是用户名的String形式也可以是由认证库中取得的UserDetails对象。
     * 如果适配器容器正在被使用,而又期望得到用户名的情况下,使用String类型是适当的。 
     * 如果需要访问额外的用户信息,例如电子邮件地址、用户导向的名字等等,那么使用UserDetail是恰
     * 当的。UserDetail提供了更灵活的访问,所以通常情况下我们返回一个UserDetail而并不推荐使用适配
     * 器容器。如果想要覆写默认的方式,将setForcePrincipalAsString置true即可。
     * UserDetails通过缓存放置在UserCache中。这确保了如果后续的请求在用户名相同的情况下可以不通过
     * UserDetailsService查询。需要指出的是:如果用户无意间输入了错误的密码,UserDetailService去查询
     * 出用户最后一次输入的密码并与之比较。
     */
				
						
						
				
				public
				 
				abstract
				 
				class
				 AbstractUserDetailsAuthenticationProvider 
				implements
				 AuthenticationProvider, InitializingBean,

    MessageSourceAware 
				
						
				
				
						{
    
						//
						~ Instance fields =============================================================
						
								
						
						
								
								
    
						protected
						 MessageSourceAccessor messages 
						=
						 AcegiMessageSource.getAccessor();
    
						private
						 UserCache userCache 
						=
						 
						new
						 NullUserCache();
    
						private
						 
						boolean
						 forcePrincipalAsString 
						=
						 
						false
						;
    
						protected
						 
						boolean
						 hideUserNotFoundExceptions 
						=
						 
						true
						;

    
						//
						~ Methods ==================================================================
						
								
								
						
						
								
								
								
 
						/** */
						
								/**
								
										
										
     * 允许子类对认证请求返回或缓存的UserDetails提供额外的校验。
     * 通常情况下子类至少会对Authentication的getCredentials()方法和UserDetails的getPassword()方法进行
     * 比照。
     * 如果定制的逻辑需要比照额外的UserDetail属性或者UsernamePasswordAuthenticationToken,也应该在
     * 这个方法中定制。
     *
     * 
								@param
								 userDetails as retrieved from the  #retrieveUser(String, 
     *        UsernamePasswordAuthenticationToken)} or UserCache
     * 
								@param
								 authentication the current request that needs to be authenticated
     *
     * 
								@throws
								 AuthenticationException AuthenticationException if the credentials could not be validated 
     *        (generally a  BadCredentialsException an AuthenticationServiceException)
    
								*/
						
						
								
								
    
						protected
						 
						abstract
						 
						void
						 additionalAuthenticationChecks(UserDetails userDetails,
        UsernamePasswordAuthenticationToken authentication)
        
						throws
						 AuthenticationException;


    
						public
						 
						final
						 
						void
						 afterPropertiesSet() 
						throws
						 Exception 
						
								
						
						
								{
        Assert.notNull(
								this
								.userCache, 
								"
								A user cache must be set
								"
								);
        Assert.notNull(
								this
								.messages, 
								"
								A message source must be set
								"
								);
        doAfterPropertiesSet();
    }
						
						
								
								
								
								
    
						public
						 Authentication authenticate(Authentication authentication)

        
						throws
						 AuthenticationException 
						
								
						
						
								{
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.
								class
								, authentication,
            messages.getMessage(
								"
								AbstractUserDetailsAuthenticationProvider.onlySupports
								"
								,
                
								"
								Only UsernamePasswordAuthenticationToken is supported
								"
								));

        
								//
								 Determine username
								
										
										
								
								        String username 
								=
								 (authentication.getPrincipal() 
								==
								 
								null
								) 
								?
								 
								"
								NONE_PROVIDED
								"
								 : authentication.getName();

        
								boolean
								 cacheWasUsed 
								=
								 
								true
								;
        UserDetails user 
								=
								 
								this
								.userCache.getUserFromCache(username);


        
								if
								 (user 
								==
								 
								null
								) 
								
										
								
								
										{
            cacheWasUsed 
										=
										 
										false
										;


            
										try
										 
										
												
										
										
												{
                user 
												=
												 retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);

            }
										
										 
										catch
										 (UsernameNotFoundException notFound) 
										
												
										
										
												{

                
												if
												 (hideUserNotFoundExceptions) 
												
														
												
												
														{
                    
														throw
														 
														new
														 BadCredentialsException(messages.getMessage(
                            
														"
														AbstractUserDetailsAuthenticationProvider.badCredentials
														"
														, 
														"
														Bad credentials
														"
														));

                }
												
												 
												else
												 
												
														
												
												
														{
                    
														throw
														 notFound;
                }
												
												
														
														
            }
										
										
												
												
												
												
            Assert.notNull(user, 
										"
										retrieveUser returned null - a violation of the interface contract
										"
										);
        }
								
								
										
										
										
										
										
        
								if
								 (
								!
								user.isAccountNonLocked()) 
								
										
								
								
										{
            
										throw
										 
										new
										 LockedException(messages.getMessage(
										"
										AbstractUserDetailsAuthenticationProvider.locked
										"
										,
                    
										"
										User account is locked
										"
										));
        }
								
								
										
										
										
										
										
        
								if
								 (
								!
								user.isEnabled()) 
								
										
								
								
										{
            
										throw
										 
										new
										 DisabledException(messages.getMessage(
										"
										AbstractUserDetailsAuthenticationProvider.disabled
										"
										,
                    
										"
										User is disabled
										"
										));
        }
								
								
										
										
										
										
										
        
								if
								 (
								!
								user.isAccountNonExpired()) 
								
										
								
								
										{
            
										throw
										 
										new
										 AccountExpiredException(messages.getMessage(
										"
										AbstractUserDetailsAuthenticationProvider.expired
										"
										,
                    
										"
										User account has expired
										"
										));
        }
								
								
										
										
										
										
        
								//
								 This check must come here, as we don't want to tell users
        
								//
								 about account status unless they presented the correct credentials
								
										
										
										
								
								        
								try
								 
								
										
								
								
										{
            additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);

        }
								
								 
								catch
								 (AuthenticationException exception) 
								
										
								
								
										{
            
										//
										 There was a problem, so try again after checking we're using latest data
										
												
												
										
										            cacheWasUsed 
										=
										 
										false
										;
            user 
										=
										 retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
            additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
        }
								
								
										
										
										
										
										
        
								if
								 (
								!
								user.isCredentialsNonExpired()) 
								
										
								
								
										{
            
										throw
										 
										new
										 CredentialsExpiredException(messages.getMessage(
                    
										"
										AbstractUserDetailsAuthenticationProvider.credentialsExpired
										"
										, 
										"
										User credentials have expired
										"
										));
        }
								
								
										
										
										
										
										
        
								if
								 (
								!
								cacheWasUsed) 
								
										
								
								
										{
            
										this
										.userCache.putUserInCache(user);
        }
								
								
										
										
										
										
        Object principalToReturn 
								=
								 user;


        
								if
								 (forcePrincipalAsString) 
								
										
								
								
										{
            principalToReturn 
										=
										 user.getUsername();
        }
								
								
										
										
										
										
        
								return
								 createSuccessAuthentication(principalToReturn, authentication, user);
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 构建一个成功的Authentication对象。使用的保护类型所以子类可以覆写本方法。
     * 子类往往会将用户提供的原始信任状(未经修改以及密码未被解密)储存在返回的Authentication
     * 对象中。
     *
     * 
								@param
								 principal that should be the principal in the returned object (defined by the 
     *        #isForcePrincipalAsString() method)
     * 
								@param
								 authentication that was presented to the provider for validation
     * 
								@param
								 user that was loaded by the implementation
     *
     * 
								@return
								 the successful authentication token
    
								*/
						
						
								
								
    
						protected
						 Authentication createSuccessAuthentication(Object principal, Authentication authentication,

        UserDetails user) 
						
								
						
						
								{
        
								//
								 Ensure we return the original credentials the user supplied,
        
								//
								 so subsequent attempts are successful even with encoded passwords.
        
								//
								 Also ensure we return the original getDetails(), so that future
        
								//
								 authentication events after cache expiry contain the details
								
										
										
								
								        UsernamePasswordAuthenticationToken result 
								=
								 
								new
								 UsernamePasswordAuthenticationToken(principal,
                authentication.getCredentials(), user.getAuthorities());
        result.setDetails(authentication.getDetails());

        
								return
								 result;
    }
						
						
								
								
								
								
								
    
						protected
						 
						void
						 doAfterPropertiesSet() 
						throws
						 Exception 
						
								
						
						
								{}
						
						
								
								
								
								
								
    
						public
						 UserCache getUserCache() 
						
								
						
						
								{
        
								return
								 userCache;
    }
						
						
								
								
								
								
								
    
						public
						 
						boolean
						 isForcePrincipalAsString() 
						
								
						
						
								{
        
								return
								 forcePrincipalAsString;
    }
						
						
								
								
								
								
								
    
						public
						 
						boolean
						 isHideUserNotFoundExceptions() 
						
								
						
						
								{
        
								return
								 hideUserNotFoundExceptions;
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 允许子类由定义的实现位置获取UserDetails,如果呈上的信任状是非法的可以有选择的抛出
     * AuthenticationExcepton异常(在有必要将用户与资源绑定来获取或产生UserDetail的情况下,这种处理
     * 方式是十分有效的)。
     * 子类没有必要去实现任何的缓存,因为AbstractUserDetailsAuthenticationProvider在默认的情况下
     * 将会缓存UserDetails。
     * UserDetails的缓存是十分复杂的,这意味着后续的认证请求即使是从缓存中得到回复也仍旧要进行
     * 信任状的校验,即使信任状的校验被基类在这个方法中用binding-based策略实现。
     * 因此在子类禁用缓存(如果想要保证这个方法是唯一能够进行认证的方法,没有UserDetails将会
     * 被缓存)或是确认子类实现additionalAuthenticationChecks()方法来进行被缓存的UserDetails
     * 对象的信任状和后续的认证请求的比照的情况下它是十分重要的。
     * 在大多数情况下子类不必要在这个方法中实现信任状的校验,取而代之的是在
     * additionalAuthenticationChecks()方法中实现。这样代码不用在2个方法中都实现信任状的校验。
     *
     * 
								@param
								 username The username to retrieve
     * 
								@param
								 authentication The authentication request, which subclasses may need to perform a binding-based
     *        retrieval of the UserDetails
     *
     * 
								@return
								 the user information (never null - instead an exception should the thrown)
     *
     * 
								@throws
								 AuthenticationException if the credentials could not be validated (generally a
     *         BadCredentialsExceptionan ,an AuthenticationServiceException or
     *         UsernameNotFoundException)
    
								*/
						
						
								
								
    
						protected
						 
						abstract
						 UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
        
						throws
						 AuthenticationException;


    
						public
						 
						void
						 setForcePrincipalAsString(
						boolean
						 forcePrincipalAsString) 
						
								
						
						
								{
        
								this
								.forcePrincipalAsString 
								=
								 forcePrincipalAsString;
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 在通常情况,如果用户名没有找到或是密码错误AbstractUserDetailsAuthenticationProvider将会
     * 抛出一个BadCredentialsException异常。设置这个属性为false的话,程序将会抛出
     * UsernameNotFoundException来取代BadCredentialsException异常。
     * 需要注意的是:我们认为这不如抛出BadCredentialsException来的可靠。
     *
     * 
								@param
								 hideUserNotFoundExceptions set to false if you wish UsernameNotFoundExceptions
     *        to be thrown instead of the non-specific BadCredentialsException (defaults to
     *        true)
    
								*/
						
						
								
								
								
    
						public
						 
						void
						 setHideUserNotFoundExceptions(
						boolean
						 hideUserNotFoundExceptions) 
						
								
						
						
								{
        
								this
								.hideUserNotFoundExceptions 
								=
								 hideUserNotFoundExceptions;
    }
						
						
								
								
								
								
								
    
						public
						 
						void
						 setMessageSource(MessageSource messageSource) 
						
								
						
						
								{
        
								this
								.messages 
								=
								 
								new
								 MessageSourceAccessor(messageSource);
    }
						
						
								
								
								
								
								
    
						public
						 
						void
						 setUserCache(UserCache userCache) 
						
								
						
						
								{
        
								this
								.userCache 
								=
								 userCache;
    }
						
						
								
								
								
								
								
    
						public
						 
						boolean
						 supports(Class authentication) 
						
								
						
						
								{
        
								return
								 (UsernamePasswordAuthenticationToken.
								class
								.isAssignableFrom(authentication));
    }
						
						
								
								
}
				
				
						
						
				
		 
		 
		
				
				
				/** */
				
						/**
						
								
								
     * AuthenticationProvider的具体实现,由UserDetailsService获取用户的详细信息。
     *
     */
				
						
						
						
				
				public
				 
				class
				 DaoAuthenticationProvider 
				extends
				 AbstractUserDetailsAuthenticationProvider 
				
						
				
				
						{
    
						
								//
								
								
								~ Instance fields =============================================================
						
						
								
								
						
						
								
								
    
						private
						 PasswordEncoder passwordEncoder 
						=
						 
						new
						 PlaintextPasswordEncoder();
    
						private
						 SaltSource saltSource;
    
						private
						 UserDetailsService userDetailsService;

    
						
								//
								
								
								~ Methods ==================================================================
						
						
								
								
						
						
								
								
    
						protected
						 
						void
						 additionalAuthenticationChecks(UserDetails userDetails,
        UsernamePasswordAuthenticationToken authentication)

        
						throws
						 AuthenticationException 
						
								
						
						
								{
        Object salt 
								=
								 
								null
								;


        
								if
								 (
								this
								.saltSource 
								!=
								 
								null
								) 
								
										
								
								
										{
            salt 
										=
										 
										this
										.saltSource.getSalt(userDetails);
        }
								
								
										
										
										
										
										
        
								if
								 (
								!
								passwordEncoder.isPasswordValid(userDetails.getPassword(), authentication.getCredentials().toString(), salt)) 
								
										
								
								
										{
            
										throw
										 
										new
										 BadCredentialsException(messages.getMessage(
                    
										"
										AbstractUserDetailsAuthenticationProvider.badCredentials
										"
										, 
										"
										Bad credentials
										"
										), userDetails);
        }
								
								
										
										
    }
						
						
								
								
								
								
								
    
						protected
						 
						void
						 doAfterPropertiesSet() 
						throws
						 Exception 
						
								
						
						
								{
        Assert.notNull(
								this
								.userDetailsService, 
								"
								An Authentication DAO must be set
								"
								);
    }
						
						
								
								
								
								
								
    
						public
						 PasswordEncoder getPasswordEncoder() 
						
								
						
						
								{
        
								return
								 passwordEncoder;
    }
						
						
								
								
								
								
								
    
						public
						 SaltSource getSaltSource() 
						
								
						
						
								{
        
								return
								 saltSource;
    }
						
						
								
								
								
								
								
    
						public
						 UserDetailsService getUserDetailsService() 
						
								
						
						
								{
        
								return
								 userDetailsService;
    }
						
						
								
								
								
								
    
						protected
						 
						final
						 UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)

        
						throws
						 AuthenticationException 
						
								
						
						
								{
        UserDetails loadedUser;


        
								try
								 
								
										
								
								
										{
            loadedUser 
										=
										 
										this
										.getUserDetailsService().loadUserByUsername(username);

        }
								
								 
								catch
								 (DataAccessException repositoryProblem) 
								
										
								
								
										{
            
										throw
										 
										new
										 AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
        }
								
								
										
										
										
										
										
        
								if
								 (loadedUser 
								==
								 
								null
								) 
								
										
								
								
										{
            
										throw
										 
										new
										 AuthenticationServiceException(
                
										"
										AuthenticationDao returned null, which is an interface contract violation
										"
										);
        }
								
								
										
										
										
										
        
								return
								 loadedUser;
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * 设置密码解密实例来进行密码的解密校验。如果没有设置,将会使用PlaintextPosswordEncoder
     * 作为默认设置。
     *
     * 
								@param
								 passwordEncoder The passwordEncoder to use
    
								*/
						
						
								
								
								
    
						public
						 
						void
						 setPasswordEncoder(PasswordEncoder passwordEncoder) 
						
								
						
						
								{
        
								this
								.passwordEncoder 
								=
								 passwordEncoder;
    }
						
						
								
								
								
								
								
 
						/** */
						
								/**
								
										
										
     * (source of salts翻译不来)此方法解迷密码的时候使用。
     * null是一个合法得值,这表明DaoAuthenticationProvider会把null返回给PasswordEncoder。
     *
     * 
								@param
								 saltSource to use when attempting to decode passwords via the PasswordEncoder
    
								*/
						
						
								
								
								
    
						public
						 
						void
						 setSaltSource(SaltSource saltSource) 
						
								
						
						
								{
        
								this
								.saltSource 
								=
								 saltSource;
    }
						
						
								
								
								
								
								
    
						public
						 
						void
						 setUserDetailsService(UserDetailsService authenticationDao) 
						
								
						
						
								{
        
								this
								.userDetailsService 
								=
								 authenticationDao;
    }
						
						
								
								
}