Cyh的博客

Email:kissyan4916@163.com
posts - 26, comments - 19, trackbacks - 0, articles - 220

笔记之Spring Security

Posted on 2009-01-10 00:43 啥都写点 阅读(968) 评论(0)  编辑  收藏 所属分类: J2EE

 


  •  

  • 安全拦截器

    • 认证管理器:由AuthenticationManager(接口)定义
      • ProviderManager(AuthenticationManager的一个实现)
        • AuthByAdapterProvider(使用容器适配器验证身份)
        • AnonymousAuthenticationProvider(以匿名用户方式验证用户)
        • CasAuthenticationProvider(根据JA-SIG"中心认证服务"(CAS)验证身份)
        • DaoAuthenticationProvider(从数据库中获取用户信息,包括用户名和密码)
          • userDetailsService属性:用来指定将用户从数据库中检索用户信息的那个Bean(UserDetailsService接口)
            • InMemoryDaoImpl:一个从Spring配置文件中获取用户信息的实现
              • usermap属性:包括一组用户名、密码和权限
            • JdbcDaoImpl:简单而灵巧的认证Dao,从关系数据库中检索用户的信息
              • dataSource属性
          • 可装配编码器,看一下Spring Security的密码编码器
            • Md5PasswordEncoder:在密码上执行“信息摘要”(MD5)编码
            • PlaintextPasswordEncoder:在密码上不执行任何编码,照原样返回它
            • ShaPasswordEncoder:在密码上执行"安全散列算法"(SHA)编码
            • 使用LDAP SHA和salted-SHA(SSHA)编码技术编码密码
          • 设置种子源(salt salt):一个种子源为所用的编码技术提供种子或者称加密密匙
            • SystemWideSaltSource:对所有用户提供相同的种子
            • ReflectionSaltSource:利用用户的User对象中某个指定属性的反射来生成种子
          • 启用用户信息的缓存功能,提供一个UserCache接口的实现
            • NullUserCache
            • EhCacheB asedUserCache:基于EHcache实现的
        • LdapAuthenticationProvider(根据某一轻量级目录访问协议服务器验证身份)
          • ref = "authenticator"策略:根据LDAP仓库处理实际的身份验证
            • 方法1:利用一个LADP用户的用户名和密码绑定到LDAP服务器
            • 方法2:在LDAP中检索一个用户的数目,然后将提供的密码和检索到的LDAP记录中的密码属性相比较
            • spring自带两个实现
              • BindAuthenticator
                • property: userDnPatterns:此属性是用来告诉BindAuthenticator如何在LDAP中找到某个用户的。它的值是一个模式列表。
                • constructor-arg ref="initialDirContextFactory"
                  • 由DefaultInitialDirContextFactory实现:用来捕捉连接到一台LDAP服务器所需的全部信息,并生成一个JNDIDirContext对象
                    • constructor-arg  value="ldap://ldap.roadrantz.com:389/dc=roadrantz,dc=com"
              • LdapAuthenticator
              • (可选)PasswordComparisonAuthenticator:这是作为BindAuthenticator之外的一种可选方式。除类名称外,声明与BindAuthenticator完全相同。
                • 但是还有属性可以用来自定义。举例来说,如果默认的userPassword属性并不符合你的需求,则可以通过装配一个新的值到passwordAttributeName属性来覆盖它。  <property name="passwordAttributeName" value="userCredentials">(转配的地方与 BindAuthenticator下的第二个分支的地方一致)

                  与BindAuthentication不同,passwordComparisonAuthenticator并不利用用户的DN绑定到LDAP,虽然有些LDAP提供者允许匿名绑定,在这种情况下,虽然 initialDirContextFactory会正常工作,但出于安全考虑,因此我们需要为DefaultInitialDirContextFactory提供一个管理器DN和密码来绑定。   <property name="mamagerDn" value="cn=manager, dc=roadrantz,dc=com" />   <property name="managerPassword" value="letmein" />( 这两个属性与BindAuthenticator的第二分支下的地方一致)

          • ref = "populator"策略:负责从LDAP仓库检索用户获得的权限集
            • DefaultLdapAuthoritiesPopulator
              • <constructor-arg ref="initialDirContextFactory">

                <constructor-arg  value=" ou=groups ">:帮助DefaultLdapAuthoritiesPopulator在LDAP仓库中查找组。由于LDAP仓库实际上是分层的,安全组随处可见。这个构造函数参数指定一个基础DN,根据它来搜索组。这个基础DN与初始上下文相关。

                <property name="groupRoleAttribute" value="ou"> :指定将包含角色信息的特征名称。它的默认值为cn,这里设置成了ou。

                <property name="convertToUpperCase" value="fasle"> 关闭将组名称转换为大写字母的操作(就是前缀后面的字符)

                <property name="rolePrefix" value="GROUP_"> 改变前缀

                更改查找成员方式。如果你的LDAP仓库使用一个associate属性(不是member属性)跟踪成员资格。则需要添加<property name="groupSearchFilter" value="(associate={0})">  {0}表示用户的DN

        • JaasAuthenticationProvider(从JAAS登陆配置中获取用户信息)
        • RemmberMeAuthenticationProvider(验证某一之前验证过并且被记住的用户的身份)
        • RemoteAuthenticationProvider(根据远程服务验证用户身份)
        • TestingAuthenticationProvider(用于单元测试。)
        • X509AuthenticationProvider(使用X.509证书验证用户身份)
        • RunAsImplAuthenticationProvider(针对身份已经被运行身份管理器替换的用户进行认证)
    • 访问决策管理器
      • AccessDecisionManager
        • 实现1:AffirmativeBased:只要有一个投票者投票赞成授予访问权,就允许访问
          • <property name="decisionVoters"><list><ref bean="roleVoter" /></list></property>
            • Spring中提供有一个很实用的投票者实现类RoleVoter,它在受保护资源的配置属性代表一个角色时进行投票。具体来说,就是在受保护资源有一个名称以ROLE_打头的配置属性时,RoleVoter参与投票。可以在Spring配置文件中使用下列XML配置一个RoleVoter: <bean id="roleVoter"  class="org.acegisecurity.vote.RoleVoter" />  同时,若想修改前缀,可在其中插入<property name="rolePrefix"  value="GROUP_" />

          • <property name="allowIfAllAbstain" value="true" /> : 如果所有的投票者都放弃投票,则如同它们都投赞成票一样,访问将被授权。
        • 实现2:ConsensusBased:大多数的
        • 实现3:UnanimousBased:所有的
    • 运行身份管理器
    • 调用后台管理器

    保护Web应用程序:Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。在对一个Spring保护的Web应用程序提出请求时,他将经过至少下列四个过滤器

    • 集成过滤器:负责在一个请求的开始时检索先前存储的认证信息(通常存储在HTTP会话中。)
      • HttpSessionContextIntegrationFilter:一个请求必须经过的第一个Spring Security过滤器,也是FilterChainProxy中第一个配置的过滤器   ----->    <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">    </bean>

    • 认证处理过滤器:将决定该请求是否是一个认证请求,如果是(通常为用户名/密码)就会被从这个请求中提取出来,然后转交给认证管理器来确定用户的身份。
      • 这是在FilterChainProxy中接下来配置的一个过滤器。身份验证时通过由一个认证入口点和一个认证处理过滤器组成的"双人组",Spring Security带有五对匹配的认证入口点和认证处理过滤器,见配套PPT。

        • 基本身份验证(BasicProcessingFilterEntryPont)的配置方法: <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">      <property name="realmName"><value>Contacts Realm</value></property> </bean>输入确认按钮后,输入的用户名和密码就会被通过HTTP头提交回服务器端,BasicProcessFilter将会拾取并处理它:  <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">      <property name="authenticationManager"><ref local="authenticationManager"/></property>      <property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>   </bean>      有局限性,因为页面不够美观

          基于表单的身份验证:配置方法如下-->  <bean id="authenticationProcessingFilterEntryPoint"  class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">      <property name="loginFormUrl"><value>/login.htm</value></property>      <property name="forceHttps"><value>false</value></property>   </bean> <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">      <property name="authenticationManager"><ref bean="authenticationManager"/></property>         <property name="authenticationFailureUrl"><value>/acegilogin.jsp?login_error=1</value></property>      <property name="defaultTargetUrl"><value>/</value></property>      <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>      <property name="rememberMeServices"><ref local="rememberMeServices"/></property>   </bean>

    • 例外转换过滤器:如发现异常,则当前请求会被发送到一个身份验证入口点(譬如登陆页面)
      • ExceptionTranslationFilter的装配:  <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">      <property name="authenticationEntryPoint"   ref="authenticationEntryPoint"  />      <property name="accessDeniedHandler"   ref="accessDeniedHandler"  />  </bean>  <bean id="accessDeniedHandler"  class="org.acegisecurity.ui.AccessDeniedHandlerImpl">       <property  name="errorPage"  value="/error.htm"/>  </bean>      1、注入一个对身份验证入口点的引用,意味着当前用户还没有成功通过身份验证,重新登录   2、表明当前用户已经通过身份验证,但是尚未被授予足够访问所请求资源的权限,当AccessDeniedException被捕捉到时,把当前用户送往一个外观更友善的报错页面。

    • 安全拦截器
      • 每当用户请求Web应用程序中的一个页面时,那个页面可能是需要保护的,也可能是不需要保护的,在Spring Security中,一个过滤器安全拦截器负责拦截请求,判断某一请求是否安全,并且给予身份验证和访问决策管理器一个机会来验证相应用户的身份和权限。在Spring配置文件中,过滤器安全拦截器是按照以下方式声明的:   <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">      <property name="authenticationManager"  ref="authenticationManager"  />      <property name="accessDecisionManager"  ref="accessDecisionManager" />      <property name="objectDefinitionSource">           <value>                 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON                 PATTERN_TYPE_APACHE_ANT                 /editProfile.htm=ROLE_MOTORIST           </value>      </property>   </bean>FilterSecurityInterceptor充当Web应用程序的安全拦截器。当有一个请求进来想要访问某以资源时,FilterSecurityInceptor将执行几项检查: 1、当前用户已通过身份验证了吗? 2、所请求的资源受保护了吗? 3、当前用户已经被授予足够访问所请求资源的权限了吗?

        ChannelProcessingFilter(确保安全性请求在HTTPS上传送):是一种Spring Security过滤器,它拦截某一请求,查看它是否需要被保护,如果是,就通过将该请求重新定向至原始请求URL的HTTPS格式来让“s”起作用。(见PPT) 配置如下:   <bean id="channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter">      <property name="channelDecisionManager" ref="channelDecisionManager" />      <property name="filterInvocationDefinitionSource">         <value>             CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON            PATTERN_TYPE_APACHE_ANT             /login.htm=REQUIRES_SECURE_CHANNEL             /j_acegi_security_check*=REQUIRES_SECURE_CHANNEL             /**=REQUIRES_INSECURE_CHANNEL         </value>      </property>   </bean>这里的filterInvocationDefinitionSource属性被配置来告诉ChannelProcessingFilter哪些页面应该使用HTTPS保护,以及哪些不应该进行保护。ChannelDecisionManagerImple:权衡决定某一请求是否应该被重新定向。<bean id="channelDecisionManager" class="org.acegisecurity.securechannel.ChannelDecisionManagerImpl">      <property name="channelProcessors">         <list>            <bean class="org.acegisecurity.securechannel.SecureChannelProcessor" />            <bean class="org.acegisecurity.securechannel.InsecureChannelProcessor" />         </list>      </property>   </bean>

    代理Spring Security过滤器(FilterToBeanProxy):本身做的工作并不多,而是将自己的工作委托给Spring应用程序上下文中的来完成。 下面我来演示如何配置FilterToBeanProxy

    • ==========这里委托给了一个FooFilter  bean=========web.xml 中的配置:<filter>         <filter-name>Foo</filter-name>         <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>         <init-param>             <param-name>targetClass</param-name>             <param-value>com.roadrantz.FooFilter</param-value>         </init-param>     </filter>      <filter-mapping>       <filter-name>Foo</filter-name>       <url-pattern>/*</url-pattern>     </filter-mapping>                                      spring上下文中的配置: <bean id="fooFilter" class="com.roadrantz.FooFilter"> <property name="bar" ref="bar" /></bean>

      ==========这里委托给了FilterChainProxy  (Spring Security要求至少配置四个而且可能一打或者更多的过滤器)=========  web.xml 中的配置:<filter>        <filter-name>Acegi Filter Chain Proxy</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>    <filter-mapping>      <filter-name>Acegi Filter Chain Proxy</filter-name>      <url-pattern>/*</url-pattern>    </filter-mapping>                                                     spring上下文中的配置:                                                              <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">      <property name="filterInvocationDefinitionSource">         <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON   PATTERN_TYPE_APACHE_ANT    /**=filter1,filter2,filter3    </value>      </property>    </bean>

    视图层安全:Spring Security带有一个小型(但是功能强大)的JSP标签库。这个标签库只提供了三个标签:1、<authz:acl> 根据当前用户是否已被授予针对某一域对象的一组特定权限之一,有条件地渲染标签体。2、<authz:authentication> 提供有关当前用户的信息。3、<authz:authorize>根据当前用户是否已被授予某些权限,有条件地渲染标签体。通过<%@ taglib prefix="authz"  uri="http://acegisecurity.org/authz" %> 引入标签库。

    • <authz:authorize>:有3个属性  1、<authz:authorize ifAllGranted="ROLE_MOTORIST,ROLE_VIP">                                                                    welcome motorist<br/>                                                                    <a href="j_acegi_logout">logoff</a>                                                                </authz:authorize>2、同1,将ifAllGranted 替换成 ifAnyGranted3、同1,将ifAllGranted 替换成 ifNotGranted

      <authz:authentication>:显示用户身份验证信息 <authz:authentication>标签把 Authentication.getPrincipal()返回的那个对象的属性呈递给JSP输出。Authentication.getPrincipal()通常返回Spring Security 的org.acegisecurity.userdetails.UserDetails接口的一个实现,包括一个getUsername()方法。因此,要想显示UserDetails对象的username属性,添加如下: <authz:authorize ifAnyGranted="ROLE_MOTORIST,ROLE_VIP">                    welcome <arthz:authentication operation="username" />  </authz:authorize>

    保护方法调用

    • 创建一个安全切面:也许设置一个AOP代理的最简单的方式是使用Spring的BeanNameAutoProxyCreator,并且简单地列举出你想要保护的Bean.假设你希望保护courseService和billingService Bean:<bean id="autoProxyCheator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">   <property name="interceptorNames">      <list>            <value>securityInterceptor</value>      </list>   </property>  <property name="beanNames">      <list>          <value>courseService</value>          <value>billingService</value>      </list>  </property></bean>在这个示例中我们要求自动代理创建器通过一个名为 securityInterceptor的拦截器代理它的Bean,这个securityInterceptor Bean按照以下方式配置:               <bean id="securityInterceptor" class="org.acegisecurity.intercept.method.MethodSecurityInterceptor">      <property name="authenticationManager"  ref="authenticationManager"  />      <property name="accessDecisionManager"  ref="accessDecisionManager" />      <property name="objectDefinitionSource">           <value>              com.springinactionl.springtraining.service.CourseService.createCourse=ROLE_ADMIN           com.springinactionl.springtraining.service.CourseService.enroll*=ROLE_ADMIN,ROLE_REGISSTRAR           </value>      </property>   </bean>

      使用元数据保护方法:首先声明一个元数据实现以告诉Spring如何装在元数据  <bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes" />接下来,声明一个对象定义源 <bean id="objectDefinitionSource" class="org.acegisecurity.intercept.method.MethodDefinitionAttributes">      <property name="attributes"><ref bean="attributes" /> </property> </bean>配置好后,装配到MethodSecurityIntereceptor的objectDefinitionSource属性中   <bean id="securityInterceptor" class="org.acegisecurity.intercept.method.MethodSecurityInterceptor">      .........      <property name="objectDefinitionSource">           <value>                <ref bean="objectDefinitionSource">           </value>      </property>   </bean>现在可以在代码上加上安全属性标记,举例/** * @@org.acegisecurity.SecurityConfig("ROLE_ADMIN") * @@org.acegisecurity.SecurityConfig("ROLE_REGISTRAR") */public void enrollStudentInCourse(Course course,Student student) throws CourseException



  •                                                                                                        --    学海无涯