Spring Acegi框架鉴权的实现 (1)

简单分析一下Spring Acegi的源代码实现:
Servlet.Filter的实现AuthenticationProcessingFilter启动Web页面的验证过程 - 在AbstractProcessingFilter定义了整个验证过程的模板:

代码
  1. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)   
  2.     throws IOException, ServletException {   
  3.     //这里检验是不是符合ServletRequest/SevletResponse的要求   
  4.     if (!(request instanceof HttpServletRequest)) {   
  5.         throw new ServletException("Can only process HttpServletRequest");   
  6.      }   
  7.   
  8.     if (!(response instanceof HttpServletResponse)) {   
  9.         throw new ServletException("Can only process HttpServletResponse");   
  10.      }   
  11.   
  12.      HttpServletRequest httpRequest = (HttpServletRequest) request;   
  13.      HttpServletResponse httpResponse = (HttpServletResponse) response;   
  14.     //根据HttpServletRequest和HttpServletResponse来进行验证   
  15.     if (requiresAuthentication(httpRequest, httpResponse)) {   
  16.         if (logger.isDebugEnabled()) {   
  17.              logger.debug("Request is to process authentication");   
  18.          }   
  19.         //这里定义Acegi中的Authentication对象来持有相关的用户验证信息   
  20.          Authentication authResult;   
  21.   
  22.         try {   
  23.              onPreAuthentication(httpRequest, httpResponse);   
  24.             //这里的具体验证过程委托给子类完成,比如AuthenticationProcessingFilter来完成基于Web页面的用户验证   
  25.              authResult = attemptAuthentication(httpRequest);   
  26.          } catch (AuthenticationException failed) {   
  27.             // Authentication failed   
  28.              unsuccessfulAuthentication(httpRequest, httpResponse, failed);   
  29.   
  30.             return;   
  31.          }   
  32.   
  33.         // Authentication success   
  34.         if (continueChainBeforeSuccessfulAuthentication) {   
  35.              chain.doFilter(request, response);   
  36.          }   
  37.         //完成验证后的后续工作,比如跳转到相应的页面   
  38.          successfulAuthentication(httpRequest, httpResponse, authResult);   
  39.   
  40.         return;   
  41.      }   
  42.   
  43.      chain.doFilter(request, response);   
  44. }   


在AuthenticationProcessingFilter中的具体验证过程是这样的:

代码
  1. public Authentication attemptAuthentication(HttpServletRequest request)   
  2.     throws AuthenticationException {   
  3.     //这里从HttpServletRequest中得到用户验证的用户名和密码   
  4.      String username = obtainUsername(request);   
  5.      String password = obtainPassword(request);   
  6.   
  7.     if (username == null) {   
  8.          username = "";   
  9.      }   
  10.   
  11.     if (password == null) {   
  12.          password = "";   
  13.      }   
  14.     //这里根据得到的用户名和密码去构造一个Authentication对象提供给AuthenticationManager进行验证,里面包含了用户的用户名和密码信息   
  15.      UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);   
  16.   
  17.     // Place the last username attempted into HttpSession for views   
  18.      request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username);   
  19.   
  20.     // Allow subclasses to set the "details" property   
  21.      setDetails(request, authRequest);   
  22.     //这里启动AuthenticationManager进行验证过程   
  23.     return this.getAuthenticationManager().authenticate(authRequest);   
  24. }   


在Acegi框架中,进行验证管理的主要类是AuthenticationManager,我们看看它是怎样进行验证管理的 - 验证的调用入口是authenticate在AbstractAuthenticationManager的实现中:
//这是进行验证的函数,返回一个Authentication对象来记录验证的结果,其中包含了用户的验证信息,权限配置等,同时这个Authentication会以后被授权模块使用

代码
  1. //如果验证失败,那么在验证过程中会直接抛出异常   
  2.     public final Authentication authenticate(Authentication authRequest)   
  3.         throws AuthenticationException {   
  4.         try {//这里是实际的验证处理,我们下面使用ProviderManager来说明具体的验证过程,传入的参数authRequest里面已经包含了从HttpServletRequest中得到的用户输入的用户名和密码   
  5.              Authentication authResult = doAuthentication(authRequest);   
  6.              copyDetails(authRequest, authResult);   
  7.   
  8.             return authResult;   
  9.          } catch (AuthenticationException e) {   
  10.              e.setAuthentication(authRequest);   
  11.             throw e;   
  12.          }   
  13.      }   


在ProviderManager中进行实际的验证工作,假设这里使用数据库来存取用户信息:

代码
  1. public Authentication doAuthentication(Authentication authentication)   
  2.     throws AuthenticationException {   
  3.     //这里取得配置好的provider链的迭代器,在配置的时候可以配置多个provider,这里我们配置的是DaoAuthenticationProvider来说明, 它使用数据库来保存用户的用户名和密码信息。   
  4.      Iterator iter = providers.iterator();   
  5.   
  6.      Class toTest = authentication.getClass();   
  7.   
  8.      AuthenticationException lastException = null;   
  9.   
  10.     while (iter.hasNext()) {   
  11.          AuthenticationProvider provider = (AuthenticationProvider) iter.next();   
  12.   
  13.         if (provider.supports(toTest)) {   
  14.              logger.debug("Authentication attempt using " + provider.getClass().getName());   
  15.             //这个result包含了验证中得到的结果信息   
  16.              Authentication result = null;   
  17.   
  18.             try {//这里是provider进行验证处理的过程   
  19.                  result = provider.authenticate(authentication);   
  20.                  sessionController.checkAuthenticationAllowed(result);   
  21.              } catch (AuthenticationException ae) {   
  22.                  lastException = ae;   
  23.                  result = null;   
  24.              }   
  25.   
  26.             if (result != null) {   
  27.                  sessionController.registerSuccessfulAuthentication(result);   
  28.                  publishEvent(new AuthenticationSuccessEvent(result));   
  29.   
  30.                 return result;   
  31.              }   
  32.          }   
  33.      }   
  34.   
  35.     if (lastException == null) {   
  36.          lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",   
  37.                     new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));   
  38.      }   
  39.   
  40.     // 这里发布事件来通知上下文的监听器   
  41.      String className = exceptionMappings.getProperty(lastException.getClass().getName());   
  42.      AbstractAuthenticationEvent event = null;   
  43.   
  44.     if (className != null) {   
  45.         try {   
  46.              Class clazz = getClass().getClassLoader().loadClass(className);   
  47.              Constructor constructor = clazz.getConstructor(new Class[] {   
  48.                          Authentication.class, AuthenticationException.class  
  49.                      });   
  50.              Object obj = constructor.newInstance(new Object[] {authentication, lastException});   
  51.              Assert.isInstanceOf(AbstractAuthenticationEvent.class, obj, "Must be an AbstractAuthenticationEvent");   
  52.              event = (AbstractAuthenticationEvent) obj;   
  53.          } catch (ClassNotFoundException ignored) {}   
  54.         catch (NoSuchMethodException ignored) {}   
  55.         catch (IllegalAccessException ignored) {}   
  56.         catch (InstantiationException ignored) {}   
  57.         catch (InvocationTargetException ignored) {}   
  58.      }   
  59.   
  60.     if (event != null) {   
  61.          publishEvent(event);   
  62.      } else {   
  63.         if (logger.isDebugEnabled()) {   
  64.              logger.debug("No event was found for the exception " + lastException.getClass().getName());   
  65.          }   
  66.      }   
  67.   
  68.     // Throw the exception   
  69.     throw lastException;   
  70. }   


我们下面看看在DaoAuthenticationProvider是怎样从数据库中取出对应的验证信息进行用户验证的,在它的基类AbstractUserDetailsAuthenticationProvider定义了验证的处理模板:

你可能感兴趣的:(spring,框架,Web,配置管理,Acegi)