`
huibin
  • 浏览: 750679 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论

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源代码解析(九):Spring_Acegi框架鉴权的实现.doc

    在Spring_Acegi框架鉴权的实现中,我们主要关注的是如何处理用户的登录验证以及在验证成功或失败后系统如何响应。 首先,Spring_Acegi通过实现Servlet的`Filter`接口来介入HTTP请求的生命周期。`...

    Spring源代码解析.rar

    Spring源代码解析1:IOC容器.doc Spring源代码解析2:IoC容器在Web容器中的启动.doc Spring源代码解析3:...Spring源代码解析9:Spring Acegi框架鉴权的实现.doc Spring源代码解析10:Spring Acegi框架授权的实现.doc

    Spring 源代码解析

    Spring源代码解析1:IOC容器;Spring源代码解析2:IoC容器在Web容器中的启动;Spring源代码解析3:Spring JDBC ;...Spring源代码解析9:Spring Acegi框架鉴权的实现 Spring源代码解析10:Spring Acegi框架授权的实现

    Spring源代码解析

    Spring源代码解析(一):IOC容器 Spring源代码解析(二):IoC容器在Web容器中的启动 Spring源代码解析(三)...Spring源代码解析(九):Spring Acegi框架鉴权的实现 Spring源代码解析(十):Spring Acegi框架授权的实现

    Spring源代码解析9:SpringAcegi框架鉴权的实现.pdf

    在Spring源代码解析9中,我们关注的是如何实现这个过程,特别是涉及用户账户状态检查和密码验证的部分。这部分代码展示了Spring Acegi如何与数据库交互来获取并验证用户信息。 首先,`Assert.notNull(user, ...

    Spring源代码解析(十):Spring_Acegi框架授权的实现.doc

    在Spring框架中,Acegi(现在已经并入Spring Security)是一个强大的安全管理组件,它提供了认证和授权功能。在本文中,我们将深入探讨Spring_Acegi框架如何实现授权机制,特别是通过`FilterSecurityInterceptor`来...

    Spring源码学习文档,绝对值得好好研究~~

    Spring源代码解析(一)Spring中的事务处理.doc Spring源代码解析(二):ioc容器在Web容器中的启动....Spring源代码解析(九):Spring Acegi框架鉴权的实现.doc Spring源代码解析(十):Spring Acegi框架授权的实现.doc

    spring源代码解析

    7. **Spring Acegi安全框架**:"spring源代码解析(九):Spring Acegi框架鉴权的实现.doc"和"Spring源代码解析(十):Spring Acegi框架授权的实现.doc"介绍了Spring的安全组件,如何实现用户身份验证和权限控制。...

    spring源代码解析十.pdf

    这篇源代码解析主要关注Spring Acegi中AuthenticationProcessingFilter的实现,它是Web页面验证的核心部分。AuthenticationProcessingFilter是Servlet Filter接口的实现,主要用于在请求到达目标资源之前进行身份...

    Acegi-spring安全框架

    Acegi框架的这种设计使安全控制变得简单且灵活,能够在不侵入业务代码的情况下实现安全策略。然而,尽管Acegi非常强大,它也有不足之处,如学习曲线较陡峭,配置复杂,对于初学者来说可能较为困难。此外,随着Spring...

    Spring技术内幕:深入解析Spring架构与设计原理

    《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和实现原理进行了深入剖析。你不仅能从本书中参透Spring框架的出色架构和设计思想,还能从...

    Spring技术内幕:深入解析 Spring架构与设计原理.pdf

    本书从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和实现原理进行了深入剖析。你不仅能从本书中参透Spring框架的优秀架构和设计思想,还能从Spring优雅的实现源码中一窥Java语言的精髓。本书在开篇...

    实战Acegi:使用Acegi作为基于Spring框架的WEB应

    Acegi Security基于Spring AOP(面向切面编程)原理,允许开发者通过声明式的方式定义安全策略,无需编写大量的安全代码。它主要由以下组件组成: 1. **AuthenticationManager**: 负责用户身份验证,包括用户名和...

    spring源码分析(1-10)

    Spring 源代码分析系列涵盖了多个关键模块,包括事务处理、IoC容器、JDBC、MVC、AOP以及与Hibernate和Acegi安全框架的集成。以下是对这些知识点的详细阐述: 1. **Spring 事务处理**:Spring 提供了声明式事务管理...

    Spring技术内幕:深入解析Spring架构与设计原理(第1部分)

     揭开Spring源代码的神秘面纱,展示系统阅读开源软件源代码的方法和秘诀。  如果你正在思考下面这些问题,也许《Spring技术内幕:深入解析Spring架构与设计原理》就是你想要的!  掌握Spring的架构原理与设计思想...

    实战Acegi:使用Acegi作为基于Spring框架的WEB应用的安全框架

    Acegi是一个专门为SpringFramework应用提供安全机制的开放源代码项目,全称为Acegi Security System for Spring,当前版本为 0.8.3。它使用了Spring的方式提供了安全和认证安全服务,包括使用Bean Context,拦截器和...

    Spring技术内幕:深入解析Spring架构与设计原理(第2部分)

     揭开Spring源代码的神秘面纱,展示系统阅读开源软件源代码的方法和秘诀。  如果你正在思考下面这些问题,也许《Spring技术内幕:深入解析Spring架构与设计原理》就是你想要的!  掌握Spring的架构原理与设计思想...

    Spring技术内幕:深入解析Spring架构与设计原理(第2版)

    Spring技术内幕:深入解析Spring架构与设计原理(第2版)》是国内唯一一本系统分析Spring源代码的著作,也是Spring领域的问鼎之作,由业界拥有10余年开发经验的资深Java专家亲自执笔,Java开发者社区和Spring开发者...

Global site tag (gtag.js) - Google Analytics