- 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) {
- publishEvent(event);
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("No event was found for the exception " + lastException.getClass().getName());
- }
- }
- // Throw the exception
- throw lastException;
- }
public Authentication doAuthentication(Authentication authentication) throws AuthenticationException { //这里取得配置好的provider链的迭代器,在配置的时候可以配置多个provider,这里我们配置的是DaoAuthenticationProvider来说明, 它使用数据库来保存用户的用户名和密码信息。 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()); //这个result包含了验证中得到的结果信息 Authentication result = null; try {//这里是provider进行验证处理的过程 result = provider.authenticate(authentication); sessionController.checkAuthenticationAllowed(result); } catch (AuthenticationException ae) { lastException = ae; result = null; } if (result != null) { sessionController.registerSuccessfulAuthentication(result); 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}")); } // 这里发布事件来通知上下文的监听器 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) { publishEvent(event); } else { if (logger.isDebugEnabled()) { logger.debug("No event was found for the exception " + lastException.getClass().getName()); } } // Throw the exception throw lastException; }
我们下面看看在DaoAuthenticationProvider是怎样从数据库中取出对应的验证信息进行用户验证的,在它的基类AbstractUserDetailsAuthenticationProvider定义了验证的处理模板:
Java代码
- public Authentication authenticate(Authentication authentication)
- throws AuthenticationException {
- Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
- messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
- "Only UsernamePasswordAuthenticationToken is supported"));
- // 这里取得用户输入的用户名
- String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
- // 如果配置了缓存,从缓存中去取以前存入的用户验证信息 - 这里是UserDetail,是服务器端存在数据库里的用户信息,这样就不用每次都去数据库中取了
- boolean cacheWasUsed = true;
- UserDetails user = this.userCache.getUserFromCache(username);
- //没有取到,设置标志位,下面会把这次取到的服务器端用户信息存入缓存中去
- if (user == null) {
- cacheWasUsed = false;
- try {//这里是调用UserDetailService去取用户数据库里信息的地方
- 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 {//这里是验证过程,在retrieveUser中从数据库中得到用户的信息,在additionalAuthenticationChecks中进行对比用户输入和服务器端的用户信息
- //如果验证通过,那么构造一个Authentication对象来让以后的授权使用,如果验证不通过,直接抛出异常结束鉴权过程
- additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
- } catch (AuthenticationException exception) {
- if (cacheWasUsed) {
- // There was a problem, so try again after checking
- // we're using latest data (ie not from the cache)
- cacheWasUsed = false;
- user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
- additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
- } else {
- throw exception;
- }
- }
- 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();
- }
- //最后返回Authentication记录了验证结果供以后的授权使用
- return createSuccessAuthentication(principalToReturn, authentication, user);
- }
- //这是是调用UserDetailService去加载服务器端用户信息的地方,从什么地方加载要看设置,这里我们假设由JdbcDaoImp来从数据中进行加载
- protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
- throws AuthenticationException {
- UserDetails loadedUser;
- //这里调用UserDetailService去从数据库中加载用户验证信息,同时返回从数据库中返回的信息,这些信息放到了UserDetails对象中去了
- try {
- loadedUser = this.getUserDetailsService().loadUserByUsername(username);
- } catch (DataAccessException repositoryProblem) {
- throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
- }
- if (loadedUser == null) {
- throw new AuthenticationServiceException(
- "UserDetailsService returned null, which is an interface contract violation");
- }
- return loadedUser;
- } public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported"));