spring 2.5也发布了,Acegi 2.0也出来了,发现里面也多了很多新特性,不过好多都是英文的,所以就到处看看,记些东西,谓之笔记也,呵呵。废话不多说,配置文件当然要从web.xml开始啊。看代码。
使用安全框架第一步就是需要在web.xml文件中声明要使用的过滤器<filter></filter>
给你们看个最小应用的<http>设置
为了加一些用户,你可以在命名空间里面定义一些测试数据:
<authentication-provider>元素创建一个DaoAuthenticationProvider bean,<user-service>元素创建一个InMemoryDaoImpl.一个ProviderManageer bean 总是由命名空间处理系统所创建而且DaoAuthenticationProvider自动被ProviderManageer bean注册。
我们接着说测试用户,上面定义了2个用户,他们的密码和角色被应用于程序中的登录控制。它也可以从<user-service>的配置属性中载入来自标准的属性文件中的用户信息。使用<authentication-provider>元素意味着用户信息将被authentication manager用来处理认证请求。
接下来说下auto-config属性,就如上面的配置定义一样,像这样定义就可以了
好了我们接下来谈下表单和基本登录选项,你可能会奇怪当提示你要登录的时候登录表单是从哪儿来的,因为我们并没有提供任何HTML文件或者JSP文件。事实上,我们并没有为登录页面明确的设置URL。 Spring Security 会产生一个自动的、基于自动激活并使用专为URL提交登录的标准参数特性,对于默认目标URL,用户会自动发送相应信息。然而,命名空间提供足够丰富的支持允许你自定义这些选项。比如,你想提供自己的登录页面,你可以这么做:
现实中,你会需要更大型的用户信息源,而不是写在application context里的几个名字。 多数情况下,你会想把用户信息保存到数据库或者是LDAP服务器里。 LDAP命名控件会在LDAP章里详细讨论,所以我们这里不会讲它。 如果你自定义了一个Spring Security的UserDetailsService实现,在你的application context中名叫"myUserDetailsService",然后你可以使用下面的验证。
<authentication-provider user-service-ref='myUserDetailsService'/>
如果你想用数据库,可以使用下面的方式
<authentication-provider>
<jdbc-user-service data-source-ref="securityDataSource"/>
</authentication-provider>
这里的"securityDataSource"就是 DataSource bean在application context里的名字,它指向了包含着Spring Security用户信息的表。 另外,你可以配置一个Spring Security JdbcDaoImpl bean,使用user-service-ref属性指定。
2.2.3.1. 添加一个密码编码器
你的密码数据通常要使用一种散列算法进行编码。 使用<password-encoder>元素支持这个功能。 使用SHA加密密码,原始的认证供应器配置,看起来就像这样:
<authentication-provider>
<password-encoder hash="sha"/>
<user-service>
<user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
在使用散列密码时,用盐值防止字典攻击是个好主意,Spring Security也支持这个功能。 理想情况下,你可能想为每个用户随机生成一个盐值,不过,你可以使用从UserDetailsService读取出来的UserDetails对象中的属性。 比如,使用username属性,你可以这样用:
<password-encoder hash="sha">
<salt-source user-property="username"/>
</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属性:
<http>
<intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
<intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>
...
</http>
使用了这个配置以后,如果用户通过HTTP尝试访问"/secure/**"匹配的网址,他们会先被重定向到HTTPS网址下。 可用的选项有"http", "https" 或 "any"。 使用"any"意味着使用HTTP或HTTPS都可以。
如果你的程序使用的不是HTTP或HTTPS的标准端口,你可以用下面的方式指定端口对应关系:
<http>
...
<port-mappings>
<port-mapping http="9080" https="9443"/>
</port-mappings>
</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加入如下部分:
<http>
...
<concurrent-session-control max-sessions="1" />
</http>
这将防止一个用户重复登录好几次-第二次登录会让第一次登录失效。 通常我们更想防止第二次登录,这时候我们可以使用
<http>
...
<concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/>
</http>
第二次登录将被阻止。
2.3.4. OpenID登录
命名空间支持OpenID登录,替代普通的表单登录,或作为一种附加功能,只需要进行简单的修改:
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<openid-login />
</http>
你应该注册一个OpenID供应器(比如myopenid.com),然后把用户信息添加到你的内存<user-service>中:
<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. 标准过滤器假名和顺序
Alias Filter Class
CHANNEL_FILTER ChannelProcessingFilter
CONCURRENT_SESSION_FILTER ConcurrentSessionFilter
SESSION_CONTEXT_INTEGRATION_FILTER HttpSessionContextIntegrationFilter
LOGOUT_FILTER LogoutFilter
X509_FILTER X509PreAuthenticatedProcessigFilter
PRE_AUTH_FILTER Subclass of AstractPreAuthenticatedProcessingFilter
CAS_PROCESSING_FILTER CasProcessingFilter
AUTHENTICATION_PROCESSING_FILTER AuthenticationProcessingFilter
BASIC_PROCESSING_FILTER BasicProcessingFilter
SERVLET_API_SUPPORT_FILTER classname
REMEMBER_ME_FILTER RememberMeProcessingFilter
ANONYMOUS_FILTER AnonymousProcessingFilter
EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter
NTLM_FILTER NtlmProcessingFilter
FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor
SWITCH_USER_FILTER SwitchUserProcessingFilter
你可以把你自己的过滤器添加到队列中,使用custom-filter元素,使用这些名字中的一个,来指定你的过滤器应该出现的位置:
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
</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>元素。 下面的声明同时启用两种类型的注解:
<global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>
2.4.1.1. 使用protect-pointcut添加安全切点
protect-pointcut是非常强大的,它让你可以用简单的声明对多个bean的进行安全声明。 参考下面的例子:
<global-method-security>
<protect-pointcut expression="execution(* com.mycompany.*Service.*(..))" access="ROLE_USER"/>
</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:
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>
web安全安全的语法也是一样,但是放在http元素里:
<http access-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>
2.5.2. 验证管理器
我们大概知道命名空间配置会自动为我们注册一个验证管理器bean。 这是一个Spring Security的ProviderManager类,如果你以前使用过框架,应该对它很熟悉了。
你也许想为ProviderManager注册另外的AuthenticationProvider bean,你可以使用<custom-authentication-provider>元素实现。比如:
<bean id="casAuthenticationProvider"
class="org.springframework.security.providers.cas.CasAuthenticationProvider">
<security:custom-authentication-provider />
...
</bean>
另一个常见的需求是,上下文中的另一个bean可能需要引用AuthenticationManager。 这里有一个特殊的元素,可以让你为AuthenticationManager注册一个别名,然后你可以application context的其他地方使用这个名字。
<security:authentication-manager alias="authenticationManager"/>
<bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
<security:custom-filter position="CAS_PROCESSING_FILTER"/>
<property name="authenticationManager" ref="authenticationManager"/>
...
</bean>
第三章 简单的应用
这几个网络应用程序在项目中是可用的。为了避免大负荷的下载,仅仅“指南”和“连接”例子包含在发行文件中。你也可以自行生成其它例子的项目文件,或者你也可以通过MAVEN仓库获取war文件。正如入门文档所描述的那样,你可以获得源代码并很容易的利用maven编译和部署它。
3.1. 指南的例子
指南的例子是个不错的入门级应用。它将简单的命名空间进行配置贯彻始终.
关于运行环境就不多说了,JDK1.4以上版本包含1.4.
让我们深入的了解一下spring security里面的共享组组件吧
1.SecurityContextHolder,SecurityContext和Authentication对象,这里面最基本的对象是SecurityContextHolder. 这里将存储应用程序的安全细节包括当前应用程序使用的principal细节,默认的SecurityContextHolder是以本地线程进行存储细节的,这意味着在同一线程中安全上下文对方法总是可用的,即使安全上下文并不是明确的将请求分发给各个方法,使用本地线程对于principal的请求被处理时是非常安全的,当转移请求的时候。当然,spring security会自动帮你保管所以你也无需担心。有些应用程序却并不适合使用本地线程,因为它们以特殊的线程方式运行。比如一个SWING的客户端程序需要JAVA虚拟机所有线程共用一个安全上下文。在这种情况下你需要使用SecurityContextHolder.MODE_GLOBAL。其它程序也希望由相同的安全标识线程产生其它线程,可以用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现上述要求。你有两种方式来改变默认的SecurityContextHolder.MODE_THREADLOCAL 模式。第一种是设置系统属性,作为选择可在SecurityContextHolder中调用一个静态方法。绝大多数的应用程序不需要改变,如果你想改变请在JAVADOC中获取更多的内容。
在SecurityContextHolder内部我们存储的当前principal细节与应用程序相结合,Spring Security使用一个Authentication对象去描述这些信息,同时你也不需要自己在去创建一个Authentication对象了,Spring Securtity为每一个用户都提供Authentication对象查询,你可以使用以下的程序代码块----在你程序的任何地方。
5.2.2. The UserDetailsService
通过上面的代码片段你可以从Authentication对象中获得一个principal。principal也是一个对象,大多数情况你都能从UserDetails中得到它的映射。
在Spring Security中UserDetails是一个重要的接口,它描述一个principal,在可扩展的且特殊的应用方式。你可以将UserDetails想象成一个适配器在你的数据库与Spring Security内部需要的 SecurityContextHolder之间。你完全可以在UserDetails中找到你应用程序中提供的原始对象的映射,因此你可以调用诸如getEmail(), getEmployeeNumber()等等的业务逻辑方法。现在你可能感到奇怪,我在何时提供了一个UserDetails对象?我是怎么做到的?谁提供的?最简短的解释就是有一个叫UserDetailsService的接口,它仅仅只有一个方法接受String类型的username参数并返回一个UserDetails。大部分的认证模块都会委托给UserDetailsService成为认证处理的一部分。UserDetailsService被用来构建存储在SecurityContextHolder里的Authentication对象。有个好消息是我们提供了许多UserDetailsService的实现,包括用于内存处理和JDBC处理的实现。大多数用户趋于实现他们自己的实现,尽管,在DAO中仅仅是一些简单的实现来描述雇佣者,客户信息或者其它企业级应用。
5.2.3. GrantedAuthority
除了principal以外,由Authentication 提供的另外一个非常重要的方法就是getAuthorities( ),这个方法提供一个GrantedAuthority对象数组,一个GrantedAuthority是由principal准许的一个授权,通常这些授权是一些"角色",比如 ROLE_ADMINISTRATOR或者ROLE_HR_SUPERVISOR,这些角色稍后将配置于web授权、方法授权和业务逻辑对象授权。Spring Security的其它部分能够解释这些授权,并将他们显示出来。权限类通常由UserDetailsService加载!通常GrantedAuthority在大部分应用程序中是允许的。但是他们并不是单独的给特定的业务逻辑对象,因此,你不用给你的雇员业务逻辑对象单独提供角色,假如这里有数以千计的授权,你内存会很快溢出的,或者说为你一个用户授权要花费很长的一段时间。
当然Spring Security为这些公共的需求特别设计了句柄,不过你最好使用你自己项目的对象安全容器去替代掉。有时,你需要在HTTP请求间对SecurityContext进行存储,其它时间principal会对每个请求重新认证,尽管大多数情况下它是会被存储的。HttpSessionContextIntegrationFilter将负责在HTTP请求间对一个SecurityContext进行存储。根据类型的名字,HttpSession 会存储这些信息,你完全没有必要使用HttpSession 存储安全信息,通常都用SecurityContextHolder来代替。
5.2.4. Summary
Spring Security主要组件如下:
1.SecurityContextHolder, 提供各种各样的类型登录SecurityContext
2.SecurityContext,控制安全信息的请求认证
3.HttpSessionContextIntegrationFilter,在web请求间将SecurityContext存储在HttpSession中
4.Authentication,描述一个具有spring security风格的principal
5.GrantedAuthority,映射程序范围内许可的principal
6.UserDetails,从你的应用程序的DAOS中获取有用的信息来构建一个Authentication对象
7.UserDetailsService,当传进一个string类型的username时创建一个UserDetails.
5.3. Authentication
一个典型的网络应用认证处理
1.你访问一个主页,然后点击一个链接。
2.服务器收到一个请求,确定你访问了一个被保护的请求。
3.如果你没有被认证,服务器端会返回一个指示请求要求你认证。请求可以是一个HTTP请求代码,也可以是一个重定向的页面。
4.依赖认证机制,你的浏览器也会重导向到特定页面,所以你可以填写表单,或者浏览器以各种方式找回你的唯一标识。
5.浏览器回回传一个请求给服务器,可能是个POST请求包含你填写的表单内容,或者一个HTTP头包含你的认证细节。
6.服务器端将会判断发送过来的认证信息是否有效,如果有效,下一步将会启动。如果无效,你的浏览器将会再次询问(回到第三步)
7.如果你有访问权限将能够访问相应的资源,否则将会返回一个403错误。
5.3.1. ExceptionTranslationFilter
ExceptionTranslationFilter是一个spring security 过滤器,它负责检测所有spring secutrity所抛出的异常,这些异常通常由主要为认证服务的AbstractSecurityInterceptor抛出,我们将在下一部分讨论AbstractSecurityInterceptor。但是现在我们需要知道它产生JAVA错误,并不知道HTTP的内容和如何去验证一个principal,代替ExceptionTranslationFilter提供这些服务,并负责返回403的错误代码(如果principal已经被认证而且缺乏足够的登录按照上述步骤7)或者登录一个AuthenticationEntryPoint(如果principal没有被认证因此我们需要返回第三步)。
5.3.2. AuthenticationEntryPoint
AuthenticationEntryPoint 负责上述步骤的第三步,你可以想象,每个网络应用程序都将会有个默认的认证策略,每个主要的认证系统都将有它自己的AuthenticationEntryPoint实现,他们用来描述步骤3中的动作,在你的浏览器决定提交你的认证证书后,这需要服务器端需要一些类似于收集这些认证的细节的东西。现在我们已经到了第六步了。在Spring Security中有一个专用名为从用户参数收集认证细节的函数,那个名字就是authentication mechanism。从用户参数中获得相应的认证细节以后,一个认证请求对象被构建,然后将会指向一个AuthenticationProvider。
5.3.3. AuthenticationProvider
欢迎到http://www.tutu6.com来看看
使用安全框架第一步就是需要在web.xml文件中声明要使用的过滤器<filter></filter>
<!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
以上代码在Spring Securtity网络构建下定义了一个钩子,接着你就可以开始编辑你的应用程序配置文件(这个就不是Web.xml了)。网页安全服务使用<http>元素配置。<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
给你们看个最小应用的<http>设置
<!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
以上代码说明希望我们应用程序的所有URLS都被保护起来,并要求由角色为ROLE_USER登入(注意:你可以使用若干的<intercept-url>元素为不同的URLS设置不同的用户角色登录请求,但是它们会在合适的排列中进行评估,而且第一个匹配的将被使用。所以你应该将最细节的配置部分放在最前面!)<intercept-url pattern="/**" access="ROLE_USER" />
</http>
为了加一些用户,你可以在命名空间里面定义一些测试数据:
<!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><authentication-provider>
<user-service>
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
注意:<http>元素是为创建一个FilterChainProxy和它要使用的若干filter beans,因为filter顺序不正确产生的问题,不会再出现了,现在这些过滤器的位置都是预定义好的。 <user-service>
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
<authentication-provider>元素创建一个DaoAuthenticationProvider bean,<user-service>元素创建一个InMemoryDaoImpl.一个ProviderManageer bean 总是由命名空间处理系统所创建而且DaoAuthenticationProvider自动被ProviderManageer bean注册。
我们接着说测试用户,上面定义了2个用户,他们的密码和角色被应用于程序中的登录控制。它也可以从<user-service>的配置属性中载入来自标准的属性文件中的用户信息。使用<authentication-provider>元素意味着用户信息将被authentication manager用来处理认证请求。
接下来说下auto-config属性,就如上面的配置定义一样,像这样定义就可以了
<http>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login />
<anonymous />
<http-basic />
<logout />
<remember-me />
</http>
以上这些元素负责启用用户登录、匿名认证、基础认证、注销和remember-me服务,他们都有各自的属性去改变他们的行为。auto-config需要一个UserDetailsService.在使用没有UserDetailsService的auto-config时在你的配置文件中会发成一个错误(比如你正在使用LDAP认证)。这是因为remember-me在auto-config="true"的时候会自动激活而且它还要求一个使用UserDetailsService运行的认证机制,那么如果你有一个因缺少UserDetailsService而产生的错误,请尝试移除auto-config的设置。<intercept-url pattern="/**" access="ROLE_USER" />
<form-login />
<anonymous />
<http-basic />
<logout />
<remember-me />
</http>
好了我们接下来谈下表单和基本登录选项,你可能会奇怪当提示你要登录的时候登录表单是从哪儿来的,因为我们并没有提供任何HTML文件或者JSP文件。事实上,我们并没有为登录页面明确的设置URL。 Spring Security 会产生一个自动的、基于自动激活并使用专为URL提交登录的标准参数特性,对于默认目标URL,用户会自动发送相应信息。然而,命名空间提供足够丰富的支持允许你自定义这些选项。比如,你想提供自己的登录页面,你可以这么做:
<http auto-config='true'>
<intercept-url pattern="/login.jsp*" filters="none"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
</http>
你依然能用auto-config,form-login元素仅仅是重写了默认的设置,同样的我们已经添加了额外的intercept-url元素告诉登录页面所有的请求都应该被安全过滤器所处理,另外,请求也应该被模式/**进行匹配并且它不能转向它自己的登录页面,如果你想使用basic authentication来代替form login,那么请修改配置文件如下:<intercept-url pattern="/login.jsp*" filters="none"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/>
</http>
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
</http>
Basic authentication将会获得优先并将用于登录提示当一个用户试图访问被保护的资源。如果你希望使用它的话Form login依然可以在这份配置文件中使用。 <intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
</http>
现实中,你会需要更大型的用户信息源,而不是写在application context里的几个名字。 多数情况下,你会想把用户信息保存到数据库或者是LDAP服务器里。 LDAP命名控件会在LDAP章里详细讨论,所以我们这里不会讲它。 如果你自定义了一个Spring Security的UserDetailsService实现,在你的application context中名叫"myUserDetailsService",然后你可以使用下面的验证。
<authentication-provider user-service-ref='myUserDetailsService'/>
如果你想用数据库,可以使用下面的方式
<authentication-provider>
<jdbc-user-service data-source-ref="securityDataSource"/>
</authentication-provider>
这里的"securityDataSource"就是 DataSource bean在application context里的名字,它指向了包含着Spring Security用户信息的表。 另外,你可以配置一个Spring Security JdbcDaoImpl bean,使用user-service-ref属性指定。
2.2.3.1. 添加一个密码编码器
你的密码数据通常要使用一种散列算法进行编码。 使用<password-encoder>元素支持这个功能。 使用SHA加密密码,原始的认证供应器配置,看起来就像这样:
<authentication-provider>
<password-encoder hash="sha"/>
<user-service>
<user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
在使用散列密码时,用盐值防止字典攻击是个好主意,Spring Security也支持这个功能。 理想情况下,你可能想为每个用户随机生成一个盐值,不过,你可以使用从UserDetailsService读取出来的UserDetails对象中的属性。 比如,使用username属性,你可以这样用:
<password-encoder hash="sha">
<salt-source user-property="username"/>
</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属性:
<http>
<intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
<intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>
...
</http>
使用了这个配置以后,如果用户通过HTTP尝试访问"/secure/**"匹配的网址,他们会先被重定向到HTTPS网址下。 可用的选项有"http", "https" 或 "any"。 使用"any"意味着使用HTTP或HTTPS都可以。
如果你的程序使用的不是HTTP或HTTPS的标准端口,你可以用下面的方式指定端口对应关系:
<http>
...
<port-mappings>
<port-mapping http="9080" https="9443"/>
</port-mappings>
</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加入如下部分:
<http>
...
<concurrent-session-control max-sessions="1" />
</http>
这将防止一个用户重复登录好几次-第二次登录会让第一次登录失效。 通常我们更想防止第二次登录,这时候我们可以使用
<http>
...
<concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/>
</http>
第二次登录将被阻止。
2.3.4. OpenID登录
命名空间支持OpenID登录,替代普通的表单登录,或作为一种附加功能,只需要进行简单的修改:
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<openid-login />
</http>
你应该注册一个OpenID供应器(比如myopenid.com),然后把用户信息添加到你的内存<user-service>中:
<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. 标准过滤器假名和顺序
Alias Filter Class
CHANNEL_FILTER ChannelProcessingFilter
CONCURRENT_SESSION_FILTER ConcurrentSessionFilter
SESSION_CONTEXT_INTEGRATION_FILTER HttpSessionContextIntegrationFilter
LOGOUT_FILTER LogoutFilter
X509_FILTER X509PreAuthenticatedProcessigFilter
PRE_AUTH_FILTER Subclass of AstractPreAuthenticatedProcessingFilter
CAS_PROCESSING_FILTER CasProcessingFilter
AUTHENTICATION_PROCESSING_FILTER AuthenticationProcessingFilter
BASIC_PROCESSING_FILTER BasicProcessingFilter
SERVLET_API_SUPPORT_FILTER classname
REMEMBER_ME_FILTER RememberMeProcessingFilter
ANONYMOUS_FILTER AnonymousProcessingFilter
EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter
NTLM_FILTER NtlmProcessingFilter
FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor
SWITCH_USER_FILTER SwitchUserProcessingFilter
你可以把你自己的过滤器添加到队列中,使用custom-filter元素,使用这些名字中的一个,来指定你的过滤器应该出现的位置:
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
</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>元素。 下面的声明同时启用两种类型的注解:
<global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>
2.4.1.1. 使用protect-pointcut添加安全切点
protect-pointcut是非常强大的,它让你可以用简单的声明对多个bean的进行安全声明。 参考下面的例子:
<global-method-security>
<protect-pointcut expression="execution(* com.mycompany.*Service.*(..))" access="ROLE_USER"/>
</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:
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>
web安全安全的语法也是一样,但是放在http元素里:
<http access-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>
2.5.2. 验证管理器
我们大概知道命名空间配置会自动为我们注册一个验证管理器bean。 这是一个Spring Security的ProviderManager类,如果你以前使用过框架,应该对它很熟悉了。
你也许想为ProviderManager注册另外的AuthenticationProvider bean,你可以使用<custom-authentication-provider>元素实现。比如:
<bean id="casAuthenticationProvider"
class="org.springframework.security.providers.cas.CasAuthenticationProvider">
<security:custom-authentication-provider />
...
</bean>
另一个常见的需求是,上下文中的另一个bean可能需要引用AuthenticationManager。 这里有一个特殊的元素,可以让你为AuthenticationManager注册一个别名,然后你可以application context的其他地方使用这个名字。
<security:authentication-manager alias="authenticationManager"/>
<bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
<security:custom-filter position="CAS_PROCESSING_FILTER"/>
<property name="authenticationManager" ref="authenticationManager"/>
...
</bean>
第三章 简单的应用
这几个网络应用程序在项目中是可用的。为了避免大负荷的下载,仅仅“指南”和“连接”例子包含在发行文件中。你也可以自行生成其它例子的项目文件,或者你也可以通过MAVEN仓库获取war文件。正如入门文档所描述的那样,你可以获得源代码并很容易的利用maven编译和部署它。
3.1. 指南的例子
指南的例子是个不错的入门级应用。它将简单的命名空间进行配置贯彻始终.
关于运行环境就不多说了,JDK1.4以上版本包含1.4.
让我们深入的了解一下spring security里面的共享组组件吧
1.SecurityContextHolder,SecurityContext和Authentication对象,这里面最基本的对象是SecurityContextHolder. 这里将存储应用程序的安全细节包括当前应用程序使用的principal细节,默认的SecurityContextHolder是以本地线程进行存储细节的,这意味着在同一线程中安全上下文对方法总是可用的,即使安全上下文并不是明确的将请求分发给各个方法,使用本地线程对于principal的请求被处理时是非常安全的,当转移请求的时候。当然,spring security会自动帮你保管所以你也无需担心。有些应用程序却并不适合使用本地线程,因为它们以特殊的线程方式运行。比如一个SWING的客户端程序需要JAVA虚拟机所有线程共用一个安全上下文。在这种情况下你需要使用SecurityContextHolder.MODE_GLOBAL。其它程序也希望由相同的安全标识线程产生其它线程,可以用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现上述要求。你有两种方式来改变默认的SecurityContextHolder.MODE_THREADLOCAL 模式。第一种是设置系统属性,作为选择可在SecurityContextHolder中调用一个静态方法。绝大多数的应用程序不需要改变,如果你想改变请在JAVADOC中获取更多的内容。
在SecurityContextHolder内部我们存储的当前principal细节与应用程序相结合,Spring Security使用一个Authentication对象去描述这些信息,同时你也不需要自己在去创建一个Authentication对象了,Spring Securtity为每一个用户都提供Authentication对象查询,你可以使用以下的程序代码块----在你程序的任何地方。
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (obj instanceof UserDetails) {
String username = ((UserDetails)obj).getUsername();
} else {
String username = obj.toString();
}
上面的代码介绍了许多有趣的关系和键值对象,首先你将注意到在SecurityContextHolder 和Authentication之间有一个中间媒介,SecurityContextHolder.getContext()方法事实上返回一个SecurityContext。if (obj instanceof UserDetails) {
String username = ((UserDetails)obj).getUsername();
} else {
String username = obj.toString();
}
5.2.2. The UserDetailsService
通过上面的代码片段你可以从Authentication对象中获得一个principal。principal也是一个对象,大多数情况你都能从UserDetails中得到它的映射。
在Spring Security中UserDetails是一个重要的接口,它描述一个principal,在可扩展的且特殊的应用方式。你可以将UserDetails想象成一个适配器在你的数据库与Spring Security内部需要的 SecurityContextHolder之间。你完全可以在UserDetails中找到你应用程序中提供的原始对象的映射,因此你可以调用诸如getEmail(), getEmployeeNumber()等等的业务逻辑方法。现在你可能感到奇怪,我在何时提供了一个UserDetails对象?我是怎么做到的?谁提供的?最简短的解释就是有一个叫UserDetailsService的接口,它仅仅只有一个方法接受String类型的username参数并返回一个UserDetails。大部分的认证模块都会委托给UserDetailsService成为认证处理的一部分。UserDetailsService被用来构建存储在SecurityContextHolder里的Authentication对象。有个好消息是我们提供了许多UserDetailsService的实现,包括用于内存处理和JDBC处理的实现。大多数用户趋于实现他们自己的实现,尽管,在DAO中仅仅是一些简单的实现来描述雇佣者,客户信息或者其它企业级应用。
5.2.3. GrantedAuthority
除了principal以外,由Authentication 提供的另外一个非常重要的方法就是getAuthorities( ),这个方法提供一个GrantedAuthority对象数组,一个GrantedAuthority是由principal准许的一个授权,通常这些授权是一些"角色",比如 ROLE_ADMINISTRATOR或者ROLE_HR_SUPERVISOR,这些角色稍后将配置于web授权、方法授权和业务逻辑对象授权。Spring Security的其它部分能够解释这些授权,并将他们显示出来。权限类通常由UserDetailsService加载!通常GrantedAuthority在大部分应用程序中是允许的。但是他们并不是单独的给特定的业务逻辑对象,因此,你不用给你的雇员业务逻辑对象单独提供角色,假如这里有数以千计的授权,你内存会很快溢出的,或者说为你一个用户授权要花费很长的一段时间。
当然Spring Security为这些公共的需求特别设计了句柄,不过你最好使用你自己项目的对象安全容器去替代掉。有时,你需要在HTTP请求间对SecurityContext进行存储,其它时间principal会对每个请求重新认证,尽管大多数情况下它是会被存储的。HttpSessionContextIntegrationFilter将负责在HTTP请求间对一个SecurityContext进行存储。根据类型的名字,HttpSession 会存储这些信息,你完全没有必要使用HttpSession 存储安全信息,通常都用SecurityContextHolder来代替。
5.2.4. Summary
Spring Security主要组件如下:
1.SecurityContextHolder, 提供各种各样的类型登录SecurityContext
2.SecurityContext,控制安全信息的请求认证
3.HttpSessionContextIntegrationFilter,在web请求间将SecurityContext存储在HttpSession中
4.Authentication,描述一个具有spring security风格的principal
5.GrantedAuthority,映射程序范围内许可的principal
6.UserDetails,从你的应用程序的DAOS中获取有用的信息来构建一个Authentication对象
7.UserDetailsService,当传进一个string类型的username时创建一个UserDetails.
5.3. Authentication
一个典型的网络应用认证处理
1.你访问一个主页,然后点击一个链接。
2.服务器收到一个请求,确定你访问了一个被保护的请求。
3.如果你没有被认证,服务器端会返回一个指示请求要求你认证。请求可以是一个HTTP请求代码,也可以是一个重定向的页面。
4.依赖认证机制,你的浏览器也会重导向到特定页面,所以你可以填写表单,或者浏览器以各种方式找回你的唯一标识。
5.浏览器回回传一个请求给服务器,可能是个POST请求包含你填写的表单内容,或者一个HTTP头包含你的认证细节。
6.服务器端将会判断发送过来的认证信息是否有效,如果有效,下一步将会启动。如果无效,你的浏览器将会再次询问(回到第三步)
7.如果你有访问权限将能够访问相应的资源,否则将会返回一个403错误。
5.3.1. ExceptionTranslationFilter
ExceptionTranslationFilter是一个spring security 过滤器,它负责检测所有spring secutrity所抛出的异常,这些异常通常由主要为认证服务的AbstractSecurityInterceptor抛出,我们将在下一部分讨论AbstractSecurityInterceptor。但是现在我们需要知道它产生JAVA错误,并不知道HTTP的内容和如何去验证一个principal,代替ExceptionTranslationFilter提供这些服务,并负责返回403的错误代码(如果principal已经被认证而且缺乏足够的登录按照上述步骤7)或者登录一个AuthenticationEntryPoint(如果principal没有被认证因此我们需要返回第三步)。
5.3.2. AuthenticationEntryPoint
AuthenticationEntryPoint 负责上述步骤的第三步,你可以想象,每个网络应用程序都将会有个默认的认证策略,每个主要的认证系统都将有它自己的AuthenticationEntryPoint实现,他们用来描述步骤3中的动作,在你的浏览器决定提交你的认证证书后,这需要服务器端需要一些类似于收集这些认证的细节的东西。现在我们已经到了第六步了。在Spring Security中有一个专用名为从用户参数收集认证细节的函数,那个名字就是authentication mechanism。从用户参数中获得相应的认证细节以后,一个认证请求对象被构建,然后将会指向一个AuthenticationProvider。
5.3.3. AuthenticationProvider
欢迎到http://www.tutu6.com来看看
发表评论
-
如何把Https网站中的安全证书导入到java中的cacerts证书库?
2010-08-06 18:22 917其实很简单,方法如下: 每一步:进入某个https://www ... -
利用httpclient来模拟登陆操作
2010-03-02 13:15 837如果有一个网站的url是http://xxx.xxx.xxx ... -
Java引用多个jar包的写法
2010-02-21 11:35 1153假设有个程序的启动方法在test.java里运行Java程序是 ... -
关于在配置spring的时候我犯的错误总结
2008-04-15 13:22 552由于第一次弄spring , ... -
关于.hashcode()和equals()的问题
2008-04-17 23:22 375今天看代码的时候忽然发现有些类会重写 ... -
关于SSH的琐事
2008-04-21 23:00 395今天终于把一个最简化的框架跑起来了。然后小小的 ... -
今天非常郁闷
2008-04-29 23:33 501今天做了下小测试,传参数也传到 ... -
自身一对多关联模型的JSON转换
2008-06-20 22:59 2109最近又遇到了一个问题, 这里有一个一对多自身关联 ... -
[转载]Spring AOP编程笔记
2008-06-24 17:09 482AOP正在成为软件开发的下一个圣杯。使用AOP,你可以将处理a ... -
Flex Json文档
2008-06-25 10:46 857<style> <!-- /* Font D ... -
IP转向技术------自动识别IP,并跳转到来访问者所在的城市
2009-02-02 17:10 1762先去下一个最新的纯真IP数据库,然后按如下操作: 1.运行纯真 ... -
关于解析CSV文件
2010-01-19 17:06 1281做开发的时候需要对一个CSV文件进行解析,并把解 ... -
[转载]quartz的配置
2010-01-22 10:58 552首先我们来写一个被调度的类: package com.kay. ...
相关推荐
Spring Security OAuth2.0学习笔记 什么是认证、授权、会话。 Java Servlet为支持http会话做了哪些事儿。 基于session认证机制的运作流程。 基于token认证机制的运作流程。 理解Spring Security的工作原理,Spring ...
Spring-Security2.0 和3.0 的中文使用文档。
Spring Security 2.0.x完全中文参考文档 学习Asegi Security 的不可或缺的东西! 完全版文档,绝无仅有!
spring security2.0.x chm 包含 1. spring security2.0.x api 2. spring security2.0.x 参考手册 3. spring security2.0.x 安全权限管理手册
Spring Security 2.0 中文参考文档 喜欢的请下载
Spring Security 2.0 参考手册.pdf
Spring Security 2.0.x Sample Code
终于实现了spring security 2.0 基于数据库的配置,可以连接数据库了,呵呵,由于加入了spring,jar包有些大,没有上传jar,或者新建一个工程,加入spring,然后将jar考过来或者从其他模块中考入jar, 这个工程下的...
三更springsecurity学习笔记
Spring boot+Spring Security Oauth2.0,Sprint cloud+Spring Security Oauth2集成。四种认证方式。附带有代码,和案例,案例,还有视频链接。我保证看完就回,如果视频链接失效,评论回复我,我单独再给你一份。
spring cloud2.0 eureka server spring security配置,与spring cloud1.X还是有很大区别
Spring Security2.0.x中文参考手册.CHM
转载关于spring2.0的学习笔记,希望对大家有所帮助
通过此PPT可进行Acegi的配置,最新Spring security2.0的安全构建JAVA系统
博文链接:https://snz.iteye.com/blog/221280
NULL 博文链接:https://ReturnOfKing.iteye.com/blog/255089
spring security oauth2.0 需要的基础 sql 文件
spring security + oauth 2.0 实现单点登录、认证授权,直接贴代码
spring security 基于oauth 2.0 实现 sso 单点登录Demo 使用 spring security 基于oauth 2.0 实现 sso 单点登录Demo spring boot + spring security + spring security oauth
Java与Java EE平台规范标准化了各种具体Java技术,比如,JDBC,JMX,Servlet,Annotation和JNDI API等,这些具体技术正是Spring2.0统一一体中的具体侧面。Spring2.0是Java和Java EE架构级框架,其依托的正是这些具体...