Spring源代码解析(九):Spring Acegi框架鉴权的实现

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

Java代码 复制代码
  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. }  
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        //这里检验是不是符合ServletRequest/SevletResponse的要求
        if (!(request instanceof HttpServletRequest)) {
            throw new ServletException("Can only process HttpServletRequest");
        }

        if (!(response instanceof HttpServletResponse)) {
            throw new ServletException("Can only process HttpServletResponse");
        }

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        //根据HttpServletRequest和HttpServletResponse来进行验证
        if (requiresAuthentication(httpRequest, httpResponse)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Request is to process authentication");
            }
            //这里定义Acegi中的Authentication对象来持有相关的用户验证信息
            Authentication authResult;

            try {
                onPreAuthentication(httpRequest, httpResponse);
                //这里的具体验证过程委托给子类完成,比如AuthenticationProcessingFilter来完成基于Web页面的用户验证
                authResult = attemptAuthentication(httpRequest);
            } catch (AuthenticationException failed) {
                // Authentication failed
                unsuccessfulAuthentication(httpRequest, httpResponse, failed);

                return;
            }

            // Authentication success
            if (continueChainBeforeSuccessfulAuthentication) {
                chain.doFilter(request, response);
            }
            //完成验证后的后续工作,比如跳转到相应的页面
            successfulAuthentication(httpRequest, httpResponse, authResult);

            return;
        }

        chain.doFilter(request, response);
    }


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

Java代码 复制代码
  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. }  
    public Authentication attemptAuthentication(HttpServletRequest request)
        throws AuthenticationException {
        //这里从HttpServletRequest中得到用户验证的用户名和密码
        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }
        //这里根据得到的用户名和密码去构造一个Authentication对象提供给AuthenticationManager进行验证,里面包含了用户的用户名和密码信息
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Place the last username attempted into HttpSession for views
        request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        //这里启动AuthenticationManager进行验证过程
        return this.getAuthenticationManager().authenticate(authRequest);
    }


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

Java代码 复制代码
  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.     }  
//如果验证失败,那么在验证过程中会直接抛出异常
    public final Authentication authenticate(Authentication authRequest)
        throws AuthenticationException {
        try {//这里是实际的验证处理,我们下面使用ProviderManager来说明具体的验证过程,传入的参数authRequest里面已经包含了从HttpServletRequest中得到的用户输入的用户名和密码
            Authentication authResult = doAuthentication(authRequest);
            copyDetails(authRequest, authResult);

            return authResult;
        } catch (AuthenticationException e) {
            e.setAuthentication(authRequest);
            throw e;
        }
    }


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

Java代码 复制代码
  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. }  
    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代码 复制代码
  1. public  Authentication authenticate(Authentication authentication)   
  2.      throws  AuthenticationException {   
  3.     Assert.isInstanceOf(UsernamePasswordAuthenticationToken. class , authentication,   
  4.         messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports" ,   
  5.              "Only UsernamePasswordAuthenticationToken is supported" ));   
  6.   
  7.      // 这里取得用户输入的用户名   
  8.     String username = (authentication.getPrincipal() ==  null ) ?  "NONE_PROVIDED"  : authentication.getName();   
  9.      // 如果配置了缓存,从缓存中去取以前存入的用户验证信 息 - 这里是UserDetail,是服务器端存在数据库里的用户信息,这样就不用每次都去数据库中取了   
  10.      boolean  cacheWasUsed =  true ;   
  11.     UserDetails user =  this .userCache.getUserFromCache(username);   
  12.      //没有取到,设置标志位,下面会把这次取到的服务器端用户信 息存入缓存中去   
  13.      if  (user ==  null ) {   
  14.         cacheWasUsed =  false ;   
  15.   
  16.          try  { //这里是调用UserDetailService去取用户数据库里信息的地方   
  17.             user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);   
  18.         }  catch  (UsernameNotFoundException notFound) {   
  19.              if  (hideUserNotFoundExceptions) {   
  20.                  throw   new  BadCredentialsException(messages.getMessage(   
  21.                          "AbstractUserDetailsAuthenticationProvider.badCredentials" "Bad credentials" ));   
  22.             }  else  {   
  23.                  throw  notFound;   
  24.             }   
  25.         }   
  26.   
  27.         Assert.notNull(user,  "retrieveUser returned null - a violation of the interface contract" );   
  28.     }   
  29.   
  30.      if  (!user.isAccountNonLocked()) {   
  31.          throw   new  LockedException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.locked" ,   
  32.                  "User account is locked" ));   
  33.     }   
  34.   
  35.      if  (!user.isEnabled()) {   
  36.          throw   new  DisabledException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.disabled" ,   
  37.                  "User is disabled" ));   
  38.     }   
  39.   
  40.      if  (!user.isAccountNonExpired()) {   
  41.          throw   new  AccountExpiredException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.expired" ,   
  42.                  "User account has expired" ));   
  43.     }   
  44.   
  45.      // This check must come here, as we don't want to tell users   
  46.      // about account status unless they presented the correct credentials   
  47.      try  { //这里是验证过程,在retrieveUser中从数据库中得到用户的信息,在 additionalAuthenticationChecks中进行对比用户输入和服务器端的用户信息   
  48.            //如果验证通过,那么构造一个 Authentication对象来让以后的授权使用,如果验证不通过,直接抛出异常结束鉴权过程   
  49.         additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);   
  50.     }  catch  (AuthenticationException exception) {   
  51.          if  (cacheWasUsed) {   
  52.              // There was a problem, so try again after checking   
  53.              // we're using latest data (ie not from the cache)   
  54.             cacheWasUsed =  false ;   
  55.             user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);   
  56.             additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);   
  57.         }  else  {   
  58.              throw  exception;   
  59.         }   
  60.     }   
  61.   
  62.      if  (!user.isCredentialsNonExpired()) {   
  63.          throw   new  CredentialsExpiredException(messages.getMessage(   
  64.                  "AbstractUserDetailsAuthenticationProvider.credentialsExpired" "User credentials have expired" ));   
  65.     }   
  66.      //根据前面的缓存结果决定是不是要把当前的用户信息存入缓存 以供下次验证使用   
  67.      if  (!cacheWasUsed) {   
  68.          this .userCache.putUserInCache(user);   
  69.     }   
  70.   
  71.     Object principalToReturn = user;   
  72.   
  73.      if  (forcePrincipalAsString) {   
  74.         principalToReturn = user.getUsername();   
  75.     }   
  76.      //最后返回Authentication记录了验证结果供以 后的授权使用   
  77.      return  createSuccessAuthentication(principalToReturn, authentication, user);   
  78. }   
  79. //这是是调用UserDetailService去加载服务器端用户信息的地方,从什么地方加载 要看设置,这里我们假设由JdbcDaoImp来从数据中进行加载   
  80. protected   final  UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)   
  81.      throws  AuthenticationException {   
  82.     UserDetails loadedUser;   
  83.      //这里调用UserDetailService去从数据库中 加载用户验证信息,同时返回从数据库中返回的信息,这些信息放到了UserDetails对象中去了   
  84.      try  {   
  85.         loadedUser =  this .getUserDetailsService().loadUserByUsername(username);   
  86.     }  catch  (DataAccessException repositoryProblem) {   
  87.          throw   new  AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);   
  88.     }   
  89.   
  90.      if  (loadedUser ==  null ) {   
  91.          throw   new  AuthenticationServiceException(   
  92.              "UserDetailsService returned null, which is an interface contract violation" );   
  93.     }   
  94.      return  loadedUser;   
  95. }  
    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;
    }


下面我们重点分析一下JdbcDaoImp这个类来看看具体是怎样从数据库中得到用户信息的:

Java代码 复制代码
  1. public   class  JdbcDaoImpl  extends  JdbcDaoSupport  implements  UserDetailsService {   
  2.      //~ Static fields/initializers =====================================================================================   
  3.      //这里是预定义好的对查询语句,对应于默认的数据库表结构, 也可以自己定义查询语句对应特定的用户数据库验证表的设计   
  4.      public   static   final  String DEF_USERS_BY_USERNAME_QUERY =   
  5.              "SELECT username,password,enabled FROM users WHERE username = ?" ;   
  6.      public   static   final  String DEF_AUTHORITIES_BY_USERNAME_QUERY =   
  7.              "SELECT username,authority FROM authorities WHERE username = ?" ;   
  8.   
  9.      //~ Instance fields ================================================================================================   
  10.      //这里使用Spring JDBC来进行数据库操作   
  11.      protected  MappingSqlQuery authoritiesByUsernameMapping;   
  12.      protected  MappingSqlQuery usersByUsernameMapping;   
  13.      private  String authoritiesByUsernameQuery;   
  14.      private  String rolePrefix =  "" ;   
  15.      private  String usersByUsernameQuery;   
  16.      private   boolean  usernameBasedPrimaryKey =  true ;   
  17.   
  18.      //~ Constructors ===================================================================================================   
  19.      //在初始化函数中把查询语句设置为预定义的SQL语句   
  20.      public  JdbcDaoImpl() {   
  21.         usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;   
  22.         authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;   
  23.     }   
  24.   
  25.      //~ Methods ========================================================================================================   
  26.   
  27.      protected   void  addCustomAuthorities(String username, List authorities) {}   
  28.   
  29.      public  String getAuthoritiesByUsernameQuery() {   
  30.          return  authoritiesByUsernameQuery;   
  31.     }   
  32.   
  33.      public  String getRolePrefix() {   
  34.          return  rolePrefix;   
  35.     }   
  36.   
  37.      public  String getUsersByUsernameQuery() {   
  38.          return  usersByUsernameQuery;   
  39.     }   
  40.   
  41.      protected   void  initDao()  throws  ApplicationContextException {   
  42.         initMappingSqlQueries();   
  43.     }   
  44.   
  45.      /**  
  46.      * Extension point to allow other MappingSqlQuery objects to be substituted in a subclass  
  47.      */   
  48.      protected   void  initMappingSqlQueries() {   
  49.          this .usersByUsernameMapping =  new  UsersByUsernameMapping(getDataSource());   
  50.          this .authoritiesByUsernameMapping =  new  AuthoritiesByUsernameMapping(getDataSource());   
  51.     }   
  52.   
  53.      public   boolean  isUsernameBasedPrimaryKey() {   
  54.          return  usernameBasedPrimaryKey;   
  55.     }   
  56.      //这里是取得数据库用户信息的具体过程   
  57.      public  UserDetails loadUserByUsername(String username)   
  58.          throws  UsernameNotFoundException, DataAccessException {   
  59.          //根据用户名在用户表中得到用户信息,包括用户名, 密码和用户是否有效的信息   
  60.         List users = usersByUsernameMapping.execute(username);   
  61.   
  62.          if  (users.size() ==  0 ) {   
  63.              throw   new  UsernameNotFoundException( "User not found" );   
  64.         }   
  65.          //取集合中的第一个作为有效的用户对象   
  66.         UserDetails user = (UserDetails) users.get( 0 );  // contains no GrantedAuthority[]   
  67.          //这里在权限表中去取得用户的权限信息,同样的返回 一个权限集合对应于这个用户   
  68.         List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());   
  69.   
  70.         addCustomAuthorities(user.getUsername(), dbAuths);   
  71.   
  72.          if  (dbAuths.size() ==  0 ) {   
  73.              throw   new  UsernameNotFoundException( "User has no GrantedAuthority" );   
  74.         }   
  75.          //这里根据得到的权限集合来配置返回的User对象 供以后使用   
  76.         GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray( new  GrantedAuthority[dbAuths.size()]);   
  77.   
  78.         String returnUsername = user.getUsername();   
  79.   
  80.          if  (!usernameBasedPrimaryKey) {   
  81.             returnUsername = username;   
  82.         }   
  83.   
  84.          return   new  User(returnUsername, user.getPassword(), user.isEnabled(),  true true true , arrayAuths);   
  85.     }   
  86.   
  87.      public   void  setAuthoritiesByUsernameQuery(String queryString) {   
  88.         authoritiesByUsernameQuery = queryString;   
  89.     }   
  90.   
  91.      public   void  setRolePrefix(String rolePrefix) {   
  92.          this .rolePrefix = rolePrefix;   
  93.     }   
  94.   
  95.      public   void  setUsernameBasedPrimaryKey( boolean  usernameBasedPrimaryKey) {   
  96.          this .usernameBasedPrimaryKey = usernameBasedPrimaryKey;   
  97.     }   
  98.   
  99.      public   void  setUsersByUsernameQuery(String usersByUsernameQueryString) {   
  100.          this .usersByUsernameQuery = usersByUsernameQueryString;   
  101.     }   
  102.   
  103.      //~ Inner Classes ==================================================================================================   
  104.   
  105.      /**  
  106.      * 这里是调用Spring JDBC的数据库操作,具体可以参考对 JDBC的分析,这个类的作用是把数据库查询得到的记录集合转换为对象集合 - 一个很简单的O/R实现  
  107.      */   
  108.      protected   class  AuthoritiesByUsernameMapping  extends  MappingSqlQuery {   
  109.          protected  AuthoritiesByUsernameMapping(DataSource ds) {   
  110.              super (ds, authoritiesByUsernameQuery);   
  111.             declareParameter( new  SqlParameter(Types.VARCHAR));   
  112.             compile();   
  113.         }   
  114.   
  115.          protected  Object mapRow(ResultSet rs,  int  rownum)   
  116.              throws  SQLException {   
  117.             String roleName = rolePrefix + rs.getString( 2 );   
  118.             GrantedAuthorityImpl authority =  new  GrantedAuthorityImpl(roleName);   
  119.   
  120.              return  authority;   
  121.         }   
  122.     }   
  123.   
  124.      /**  
  125.      * Query object to look up a user.  
  126.      */   
  127.      protected   class  UsersByUsernameMapping  extends  MappingSqlQuery {   
  128.          protected  UsersByUsernameMapping(DataSource ds) {   
  129.              super (ds, usersByUsernameQuery);   
  130.             declareParameter( new  SqlParameter(Types.VARCHAR));   
  131.             compile();   
  132.         }   
  133.   
  134.          protected  Object mapRow(ResultSet rs,  int  rownum)   
  135.              throws  SQLException {   
  136.             String username = rs.getString( 1 );   
  137.             String password = rs.getString( 2 );   
  138.              boolean  enabled = rs.getBoolean( 3 );   
  139.             UserDetails user =  new  User(username, password, enabled,  true true true ,   
  140.                      new  GrantedAuthority[] { new  GrantedAuthorityImpl( "HOLDER" )});   
  141.   
  142.              return  user;   
  143.         }   
  144.     }   
  145. }  
public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
    //~ Static fields/initializers =====================================================================================
    //这里是预定义好的对查询语句,对应于默认的数据库表结构,也可以自己定义查询语句对应特定的用户数据库验证表的设计
    public static final String DEF_USERS_BY_USERNAME_QUERY =
            "SELECT username,password,enabled FROM users WHERE username = ?";
    public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
            "SELECT username,authority FROM authorities WHERE username = ?";

    //~ Instance fields ================================================================================================
    //这里使用Spring JDBC来进行数据库操作
    protected MappingSqlQuery authoritiesByUsernameMapping;
    protected MappingSqlQuery usersByUsernameMapping;
    private String authoritiesByUsernameQuery;
    private String rolePrefix = "";
    private String usersByUsernameQuery;
    private boolean usernameBasedPrimaryKey = true;

    //~ Constructors ===================================================================================================
    //在初始化函数中把查询语句设置为预定义的
  

你可能感兴趣的:(spring,数据结构,框架,配置管理,Acegi)