Roller 用户登陆 --- acegi验证

创建好用户,用户就可以登陆了。

 

在Roller的系统中,用户登陆其实是直接指向登陆成功的首页:

 连接为: /roller-ui/login-redirect.rol

 这个配置在struts.xml中

        <action name="login-redirect">
            <result>/roller-ui/login-redirect.jsp</result>
        </action>

 

在acegi中配置:

    <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="accessDecisionManager" ref="accessDecisionManager"/>
         <property name="objectDefinitionSource">
            <value>
                PATTERN_TYPE_APACHE_ANT
                /roller-ui/login-redirect**=admin,editor


                /roller-ui/profile**=admin,editor
                /roller-ui/createWeblog**=admin,editor
                /roller-ui/menu**=admin,editor
                /roller-ui/authoring/**=admin,editor
                /roller-ui/admin/**=admin
                /rewrite-status*=admin
            </value>
                <!-- Add this to above list for LDAP/SSO configuration -->
                <!-- /roller-ui/user.do*=register -->
        </property>
    </bean>

 连接到 /roller-ui/login-redirect** 需要 admin或者editor权限,

 

如果用户没有登陆,就跳转到用户登陆页面中。

这个由配置中的authenticationManager来进行处理

如果用户登陆了,需要验证权限,由配置中的accessDecisionManager来进行处理。

这个登陆完全是采用acegi的方式进行。

 

 

 

/roller/roller-ui/login.rol 进行登陆;

 

 

登陆是 : org.apache.roller.weblogger.ui.struts2.core.Login进行处理

 

这个servlet只是转向到tiles的.Login配置,具体页面为: jsps/core/Login.jsp

 

登陆的要求发送给:<c:url value="/roller_j_security_check"/>

在submit之前,先把用户名,保留在cookie中,key为username,过期时间:但前日期+30天

-------------------------------

 

这里要提到原来的一个未说明的 Listener了: RollerSession

RollerSession: 实现了: HttpSessionListener

                                  HttpSessionActivationListener

                                  (serializable)

这个就是在创建一个Session的时候,系统创建了一个RollerSession的实例放在session中

 

----------------------------

连接: roller_j_security_check 配置给了 acegi的登陆:

配置文件security.xml

    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationFailureUrl" value="/roller-ui/login.rol?error=true"/>
        <property name="defaultTargetUrl" value="/"/>
        <property name="filterProcessesUrl" value="/roller_j_security_check


"/>
        <property name="rememberMeServices" ref="rememberMeServices"/>
    </bean>
 

这个配置给了

filterProcessesUrl

具体的工作,由authenticationManager进行处理。

 

在Roller中,authenticationManager的配置为:

    <bean id="authenticationManager




" class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref local="daoAuthenticationProvider"/> 
                <!-- Uncomment this for LDAP/SSO configuration <ref local="ldapAuthProvider"/> -->
                <!-- Uncomment this for CAS/SSO configuration <ref local="casAuthenticationProvider"/> -->
                <ref local="anonymousAuthenticationProvider"/>                
                <!-- rememberMeAuthenticationProvider added programmatically -->
            </list>
        </property>
    </bean>
 

这个由:daoAuthenticationProvider来进行具体的实现:

daoAuthenticationProvider的配置:

    <bean id="daoAuthenticationProvider




" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
         <property name="userDetailsService" ref="jdbcAuthenticationDao"/>
         <property name="userCache" ref="userCache"/> 
    </bean>

    <!-- Read users from Roller API -->
    <bean id="jdbcAuthenticationDao" class="org.apache.roller.weblogger.ui.core.security.RollerUserDetailsService"/>

    <bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
        <property name="cache">
            <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
                <property name="cacheManager">
                    <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
                </property>
                <property name="cacheName" value="userCache"/>
            </bean>
        </property>
    </bean>

 

RollerUserDetailsService实现了接口: org.acegisecurity.userdetails.UserDetailsService

  实现了方法: loadUserByUsername

    通过webloggerFactory.getWeblooger().getUserManager().getUserByUserName, 获取一个User对象;

 

同时获取GrantedAuthority,

 

最后生成一个 org.acegisecurity.userdetails.User对象返回

 

-------------------------

来看一下Roller中使用的acegi的验证:

 

首先在web.xml中,定义采用acegi:

    <filter>
        <filter-name>securityFilter</filter-name>
        <filter-class>org.acegisecurity.util.FilterToBeanProxy


</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
            <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
        </init-param>
    </filter>

    <!-- Acegi Security filters - controls secure access to different parts of Roller -->
    <!--  对于所有的url都进行权限验证  -->
    <filter-mapping>
        <filter-name>securityFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping> 

FilterToBeanProxy是一个特殊的Servlet过滤器,它本身完成的工作并不多,而是将自己的工作委托给Spring应用程序上下文中的一个Bean来完成。被委托的Bean几乎和其他的Servlet过滤器一样,实现javax.servlet.Filter接口。但是它是在Spring的配置文件而不是在web.xml中配置的。

 

FilterToBeanProxy的参数target:表示将过滤工作委托给哪个Bean来完成。

当这个FilterToBeanProxy被初始化时,它会在Spring的上下文中寻找这个Bean,并且把自己的过滤工作委托给这个Bean完成。(这意味着这个Spring上下文必须使用Spring的ContextLoaderListener或者是contextLoaderServlet进行加载)

 

 

security.xml中的配置:

    <!-- ======================== FILTER CHAIN ======================= -->
    <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
        <property name="filterInvocationDefinitionSource">
            <value>
                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                PATTERN_TYPE_APACHE_ANT
                /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,rememberMeProcessingFilter,channelProcessingFilter,remoteUserFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
            </value>
            <!-- Replace "authenticationProcessingFilter,rememberMeProcessingFilter" with "casProcessingFilter" if you want to use Roller with CAS -->
        </property>
    </bean>

首先配置了acegi的FilterChainProxy,这个定义了在Http请求采用多少种方式的Filter进行处理;

 

FilterChainProxy:是javax.servlet.Filter的一个实现,它可以被配置来同时把几个过滤器链接在一起,

 它有一个参数:filterInvocationDefinitionSource:String类型参数,CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转为小写,

    它被解析FilterChainProxy将用来把过滤器接到一起的一种模式。

    接下来一行,在声明URL模式时,采用Apache Ant样式路径,

    最后,一个或多个”URL-到-过滤器-链“映像被提供。在这里, /**模式(在Ant中,这意味着所有URL都将匹配)被映像给后面的这些过滤器。

 

对于用户登陆,使用的是:authenticationProcessingFilter,这样就回到了本文的开头的配置说明。

 

在authenticationProcessingFilter的配置中:

  filterProcessesUrl:处理来源URL

  defaultTargetUrl: 处理成功的去向

  authenticationFailureUrl:验证失败URl

 

在Acegi框架规定必须使用j_username和j_password作为用户名和密码的HTTP参数名。这两个参数名是固定的,Acegi不允许对此进行配置,不过这种约定俗成而非事事配置的风格正渐渐成为流行的设计模式。

 

---------------

整体流程在复述一次:

  1. Http请求过来,通过Acegi的Filter:org.acegisecurity.util.FilterToBeanProxy,
  2. 这个其实是一个代理,通过配置参数中的 org.acegisecurity.util.FilterChainProxy 进行实际 filter链处理(简化web.xml中的配置,同时可以利用到Spring的IoC优势)
  3. 在这个处理链中负责处理登陆的是:authenticationProcessingFilter
  4. 在authenticationProcessingFilter中,是通过authenticationManager来进行具体操作的,
  5. authenticationManager中配置了多个ProviderManager,目前应该是daoAuthenticationProvider来进行的:
    daoAuthenticationProvider进行简单的基于数据库的身份验证。DaoAuthenticationProvider获取数据库中的账号密码并进行匹配,若成功则在通过用户身份的同时 返回一个包含授权信息的Authentication对象,否则身份验证失败,抛出一个AuthenticatiionException。
    对于daoAuthenticationProvider的配置,需要了解3个参数:
    userDetailsService
    
    :
        用于在数据中获取用户信息。
        acegi提供了用户及授权的表结构,但是您也可以自己来实现。
        通过usersByUsernameQuery这个SQL得到你的(用户ID,密码,状态信息);
        通过authoritiesByUsernameQuery这个SQL得到你的(用户ID,授权信息) 
    
    userCache
    
    :
        缓存用户和资源相对应的权限信息。每当请求一个受保护资源时,daoAuthenticationProvider就会被调用以获取用户授权信息。
        如果每次都从数据库获取的话,那代价很高,对于不常改变的用户和资源信息来说,最好是把相关授权信息缓存起来
        userCache提供了两种实现: NullUserCache 和 EhCacheBasedUserCache, 
            NullUserCache实际上就是不进行任何缓存,
            EhCacheBasedUserCache是使用Ehcache来实现缓功能。 
    
    passwordEncoder
    
    :
        使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器:
        PlaintextPasswordEncoder—默认,不加密,返回明文.
        ShaPasswordEncoder—哈希算法(SHA)加密
        Md5PasswordEncoder—消息摘要(MD5)加密 
    

  6. daoAuthenticationProvider中配置了jdbcAuthenticationDao进行获取用户对象,
  7. jdbcAuthenticationDao的具体实现是:org.apache.roller.weblogger.ui.core.security.RollerUserDetailsService

 

------------------------------------------------------------------------------------------------------------------------------

Acegi的认证管理器(authenticationManager)有:

Acegi提供了不同的AuthenticationProvider的实现,如:
        DaoAuthenticationProvider 从数据库中读取用户信息验证身份
        AnonymousAuthenticationProvider 匿名用户身份认证
        RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证
        AuthByAdapterProvider 使用容器的适配器验证身份
        CasAuthenticationProvider 根据Yale中心认证服务验证身份, 用于实现单点登陆
        JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证身份
        RemoteAuthenticationProvider 根据远程服务验证用户身份 中国网管联盟www.bitscn.com
        RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证
        X509AuthenticationProvider 从X509认证中获取用户信息验证身份
        TestingAuthenticationProvider 单元测试时使用

 

------------------------------------------------------------------------------------------------------------------------------

在FilterChainProxy中,配置的一些过滤器:

 ChannelProcessingFilter:通道处理过滤器,可选

     该过滤器负责检查当前请求的Channel,并判断是否已满足安全需 要。如果不满足,则由非安全的通道传输(Http)重定向于安全的通道传输(Https),以确保服务器与浏览器之间传输的数据加密。

 

 AuthenticationProcessingFilter:  认证处理过滤器

    该过滤器会判断该请求是否是一个认证请求(通常是 "/j_acegi_security_check")。如果是,则它会从请求中获取用户名和密码,并转交给AuthenticationManager 来认证用户身份。如果不是,则会直接转到下一个Filter。

 

HttpSessionContextIntegrationFilter : HttpSession安 全上下文信息集成过滤器

    该过滤器负责将Authentication对象保存在HttpSession中,使其在下一个请求到来时仍可被获取。故 Authentication能跨越多个请求。

 

FilterSecurityInterceptor : 过滤器安全拦截器

    该过滤器会首先调用 AuthenticationManager判断用户是否已登陆认证,如还没认证成功,则重定向到登陆界面。认证成功,则并从 Authentication中获取用户的权限。然后从objectDefinitionSource属性获取各种URL资源所对应的权限。最后调用 AccessDecisionManager来判断用户所拥有的权限与当前受保华的URL资源所对应的权限是否相匹配。如果匹配失败,则返回403错误 (禁止访问)给用户。匹配成功则用户可以访问受保护的URL资源。

 

#  保护方法调用
当去保护方法调用时,Acegi使用Spring AOP,将"切面"应用于所代理的对象,以确保用户只有在拥有恰当授权时才可以调用受保护的方法。


MethodSecurityInterceptor : 方法调用安全拦截器

    同样也是在调用方法之前,先调用AuthenticationManager 判断用户身份是否已验证,然后从objectDefinitionSource中获取方法所对应的权限,再调用 AccessDecisionManager来匹配用户权限和方法对应的权限。如果用户没有足够权限调用当前方法,则抛出 AccessDeniedException使方法不能被调用。

 

------------------------------------------------------------------------------------------------------------------------------如果已有表和Acegi的表名字不一样,如何验证

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(spring,bean,UI,SSO,Acegi)