2.2.3.1 添加一个密码编码器

    你的密码数据通常要使用一种散列算法进行编码。 使用<password-encoder>元素支持这个功能。 使用SHA加密密码,原始的认证供应器配置,看起来就像这样:

1 <authentication-provider>
2 <password-encoder hash="sha"/>
3 <user-service>
4 <user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" authorities="ROLE_USER, ROLE_ADMIN" />
5 <user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f" authorities="ROLE_USER" />
6 </user-service>
7 </authentication-provider>

    在使用散列密码时,用盐值防止字典攻击是个好主意,Spring Security也支持这个功能。 理想情况下,你可能想为每个用户随机生成一个盐值,不过,你可以使用从UserDetailsService读取出来的UserDetails对象中的属性。 比如,使用username属性,你可以这样用:

1 <password-encoder hash="sha">
2 <salt-source user-property="username"/>
3 </password-encoder>

    你可以通过password-encoder的ref属性,指定一个自定义的密码编码器bean。 这应该包含application context中一个bean的名字,它应该是Spring Security的PasswordEncoder接口的一个实例。

2.3 高级web特性

2.3.1 Remember-Me认证

    参考Remember-Me章获得remember-me命名空间配置的详细信息。

2.3.2 添加HTTP/HTTPS信道安全

    如果你的同时支持HTTP和HTTPS协议,然后你要求特定的URL只能使用HTTPS,这时可以直接使用<intercept-url>的requires-channel属性:

1 <http>
2 <intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
3 <intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>
4 spring命名空间讲解续_职场
5 </http>

    使用了这个配置以后,如果用户通过HTTP尝试访问"/secure/**"匹配的网址,他们会先被重定向到HTTPS网址下。 可用的选项有"http", "https" 或 "any"。 使用"any"意味着使用HTTP或HTTPS都可以。

    如果你的程序使用的不是HTTP或HTTPS的标准端口,你可以用下面的方式指定端口对应关系:

1 <http>
2 spring命名空间讲解续_职场
3 <port-mappings>
4 <port-mapping http="9080" https="9443"/>
5 </port-mappings>
6 </http>

    你可以在Chapter 7, Channel Security找到更详细的讨论。

2.3.3 同步Session控制

    如果你希望限制单个用户只能登录到你的程序一次,Spring Security通过添加下面简单的部分支持这个功能。 首先,你需要把下面的监听器添加到你的web.xml文件里,让Spring Security获得session生存周期事件:
<listener>
<listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
</listener>

    然后,在你的application context加入如下部分:
1 
2 <http>
3 spring命名空间讲解续_职场
4 <concurrent-session-control max-sessions="1" />
5 </http>

    这将防止一个用户重复登录好几次-第二次登录会让第一次登录失效。 通常我们更想防止第二次登录,这时候我们可以使用

1 <http>
2 spring命名空间讲解续_职场
3 <concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/>
4 </http>

第二次登录将被阻止。

2.3.4 OpenID登录

    命名空间支持OpenID登录,替代普通的表单登录,或作为一种附加功能,只需要进行简单的修改:

1 <http auto-config='true'>
2 <intercept-url pattern="/**" access="ROLE_USER" />
3 <openid-login />
4 </http>
 
    你应该注册一个OpenID供应器(比如myopenid.com),然后把用户信息添加到你的内存<user-service>中:

1 <user name="http://jimi.hendrix.myopenid.com/" password="notused" authorities="ROLE_USER" />

    你应该可以使用myopenid.com网站登录来进行验证了。

2.3.5 添加你自己的filter

    如果你以前使用过Spring Security,你应该知道这个框架里维护了一个过滤器链,来提供它的服务。 你也许想把你自己的过滤器添加到链条的特定位置,或者让已存在的过滤器,使用特定的版本。 你如何在命名空间配置里实现这些功能呢?过滤器链现在已经不能之间看到了。

    过滤器顺序在使用命名空间的时候是被严格执行的。 每个Spring Security过滤器都实现了Spring的Ordered接口,这些过滤器在初始化的时候先被排好序了。 标准的过滤器在命名空间里都有自己的假名:

Table 2.1 标准过滤器假名和顺序

spring命名空间讲解续_职场_05

    你可以把你自己的过滤器添加到队列中,使用custom-filter元素,使用这些名字中的一个,来指定你的过滤器应该出现的位置:

1 <beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter">
2 <custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
3 </beans:bean>

    你还可以使用after 或 before属性,如果你想把你的过滤器添加到队列中另一个过滤器的前面或后面。可以使用"FIRST" 或 "LAST"来指定你想让你的过滤器分别出现在队列元素的前面或后面。

2.3.6 防止Session固定攻击

    Session固定攻击是一个潜在危险,当一个恶意攻击者可以创建一个session访问一个网站的时候,然后说服另一个用户登录到同一个会话上(比如,发送给他们一个包含了session标识参数的链接)。 Spring Security通过在用户登录时,创建一个新session来防止这个问题。 如果你不需要保护,或者它与其他一些需求冲突,你可以通过使用<http>中的session-fixation-protection属性来配置它的行为,它有三个选项

*
migrateSession - 创建一个新session,把原来session中所有属性复制到新session中。这是默认值。
*
none - 什么也不做,继续使用原来的session。
*
newSession - 创建一个新的“干净的”session,不会复制session中的数据。

2.3.7 设置自定义AuthenticationEntryPoint

    如果你不使用命名空间里的表单登录,OpenID或基本身份验证,你也许想定义个验证过滤器和入口点,使用传统的bean语法,把他们链接到命名空间里。 你可以像Section 2.3.5, “添加你自己的filter”里解释的那样,添加过滤器。 对应的AuthenticationEntryPoint可以使用<http>中的entry-point-ref进行设置。

    CAS例子,是一个在命名空间里使用自定义bean的好例子,包括这个语法。如果你不熟悉验证入口点,可以看看技术纵览章节中的讨论。

2.4 保护方法

    Spring Security 2.0大幅改善了对你的服务层方法添加安全。 如果你使用Java 5或更高版本,还支持JSR-250的安全注解,同框架提供的@secured注解相似。 你可以为单个bean提供安全控制,通过使用intercept-methods元素装饰bean声明,或者你可以使用AspectJ方式的切点来控制实体服务层里的多个bean。

2.4.1 <global-method-security>元素

    这个元素用来在你的应用程序中启用基于安全的注解(通过在这个元素中设置响应的属性),也可以用来声明将要应用在你的实体application context中的安全切点组。 你应该只定义一个<global-method-security>元素。 下面的声明同时启用两种类型的注解:

1 <global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>

2.4.1.1 使用protect-pointcut添加安全切点

    protect-pointcut是非常强大的,它让你可以用简单的声明对多个bean的进行安全声明。 参考下面的例子:
1 <global-method-security>
2 <protect-pointcut expression="execution(* com.mycompany.*Service.*(..))" access="ROLE_USER"/>
3 </global-method-security>

    这样会保护application context中的符合条件的bean的所有方法,这些bean要在com.mycompany包下,类名以"Service"结尾。 ROLE_USER的角色才能调用这些方法。 就像URL匹配一样,指定的匹配要放在切点队列的最前面,第一个匹配的表达式才会被用到。

2.5. 默认的AccessDecisionManager

    这章假设你有一些Spring Security权限控制有关的架构知识。 如果没有,你可以跳过这段,以后再来看,因为这章只是为了自定义的用户设置的,需要在简单基于角色安全的基础上加一些客户化的东西。

    当你使用命名空间配置时,默认的AccessDecisionManager实例会自动注册,然后用来为方法调用和web URL访问做验证,这些都是基于你设置的intercept-url和protect-pointcut权限属性内容(和注解中的内容,如果你使用注解控制方法的权限)。

    默认的策略是使用一个AffirmativeBased AccessDecisionManager ,以及RoleVoter 和AuthenticatedVoter。

2.5.1 自定义AccessDecisionManager

    如果你需要使用一个更复杂的访问控制策略,把它设置给方法和web安全是很简单的。

    对于方法安全,你可以设置global-security里的access-decision-manager-ref属性,用对应 AccessDecisionManager bean在application context里的id:

1 <global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
2 spring命名空间讲解续_职场
3 </global-method-security>

    web安全安全的语法也是一样,但是放在http元素里:

1 <http access-decision-manager-ref="myAccessDecisionManagerBean">
2 spring命名空间讲解续_职场
3 </http>

2.5.2 验证管理器

    我们大概知道命名空间配置会自动为我们注册一个验证管理器bean。 这是一个Spring Security的ProviderManager类,如果你以前使用过框架,应该对它很熟悉了。

    你也许想为ProviderManager注册另外的AuthenticationProvider bean,你可以使用<custom-authentication-provider>元素实现。比如:

1 <bean id="casAuthenticationProvider"
2 class="org.springframework.security.providers.cas.CasAuthenticationProvider">
3 <security:custom-authentication-provider />
4 spring命名空间讲解续_职场
5 </bean>

    另一个常见的需求是,上下文中的另一个bean可能需要引用AuthenticationManager。 这里有一个特殊的元素,可以让你为AuthenticationManager注册一个别名,然后你可以application context的其他地方使用这个名字。

1 <security:authentication-manager alias="authenticationManager"/>
2 
3 <bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
4 <security:custom-filter position="CAS_PROCESSING_FILTER"/>
5 <property name="authenticationManager" ref="authenticationManager"/>
6 spring命名空间讲解续_职场
7 </bean>