安全命名空间配置 - 高级网站功能
Remember-Me 验证
查看独立的 Remember-Me 章节来查看该功能的配置。
添加 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>
使用上面的配置,如果用户访问到匹配/secure/**
规则的URL,如果使用了HTTP,他们将首先被重定向到HTTPS的URL.
如果要了解通道处理器怎么实现的,请查看
ChannelProcessingFilter
和相关类的API文档.
该选项可选值为”http”, “https” 或者 “any”,使用”any”意思是使用HTTP或者HTTPS均可。
如果应用程序使用HTTP和/或HTTPS非标准的端口,您可以指定如下端口映射的列表::
<http>
...
<port-mappings>
<port-mapping http="9080" https="9443"/>
</port-mappings>
</http>
需要注意的是,为了实现真正的安全,应用程序不应该使用HTTP或在HTTP和HTTPS之间切换。它应该在由HTTPS开始(让用户进入一个HTTPS URL),并一直使用安全连接,以避免被进行中间人攻击的可能。
Session管理
检测超时
你可以配置Spring Security检测无效的Session ID提交并且将用户重定向到一个适当的URL,这通过session-management
元素来达到:
<http>
...
<session-management invalid-session-url="/invalidSession.htm" />
</http>
请注意,如果你使用这个机制来检测会话超时,如果用户注销,然后重新登录,但不关闭浏览器它可能会错误地抛出一个错误。这是因为当你让Session失效时,Cookie没有被清理干净,就算用户已注销还是会重新提交session的cookie而不会清除。您可以在注销时明确的删除JSESSIONID的cookie,例如,通过在注销处理程序使用以下语法:
<http>
<logout delete-cookies="JSESSIONID" />
</http>
不幸的是这个不保证在所有servlet容器都会正常工作,所以你需要在你的环境下进行测试。
如果你的应用程序运行在代理后面,你还可以配置代理服务器去删除session的cookie..例如,使用Apache httpd的mod_headers模块,下面的指令可以通过在注销请求的响应头删除JSESSIONID的cookie.(假设应用程序部署在 /tutorial 路径下):
<LocationMatch "/tutorial/logout">
Header always set Set-Cookie "JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 1970 00:00:00 GMT"
</LocationMatch>
并发Session控制
如果你想限制单个用户访问你的应用程序的能力。Spring Security通过后面简单的配置马上启用。首先你需要添加后面的监听器到你的web.xml
文件。让Spring Security获得session的生存事件:
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
然后添加后面的行刀你的应用程序上下文:
<http>
...
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
</http>
这将防止用户登录多次 - 第二登录将导致第一次变成无效。通常我们更想防止第二次登录,在这种情况下,这种情况你可以使用:
<http>
...
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
</http>
第二次登录将被拒绝,如果基于表单的验证被启用这个用户将会被发送到authentication-failure-url
. 如果第二次登录是通过其他非交互的机制,比如 “记住我” 功能,进行登录的。那么一个 ,”unauthorized” (401) 错误将会被发送给客户端。如果你想替换成一个错误页面,你可以为session-management
添加一个 session-authentication-error-url
属性。
如果您正在使用基于表单的登录定制验证过滤器,那么你必须明确地配置同步会话控制的支持。更多的细节可以在会话管理章节中找到。
Session完成(Session Fixation)攻击保护
Session完成攻击是一个潜在的威胁。攻击者访问网站生成一个Session,然后诱使其他用户用同一个会话登录(例如:通过发送包含会话标示符作为一个参数链接)。Spring Security通过在登录时创建新的Session或者修改Session ID来应对这种情况。如果你不需要这个保护或者与一些其他需求冲突你可以通过<session-management>
的session-fixation-protection
属性来控制这个行为。他有4个选项:
- none - 什么都不做. 原来的会话将会被保留
- newSession - 建立一个新的干净的Session,不会复制已经存在的Session数据,(Spring Security相关的属性将会被复制)
- migrateSession - 创建一个新的Session并且拷贝所以已经存在的Session属性到新的Session,这是Servlet 3.0 及之前的容器的默认设置。
- changeSessionId - 不创建新的Session,使用Servlet容器提供的Session完成攻击保护(
HttpServletRequest#changeSessionId()
). 这个选项只有在Servlet 3.1(Java EE7)和更新的容器下可用。在旧的容器设置这个选项会产生一个异常。在Servlet3.1和更新的容器这默认该选项。
当会话完成保护发生时,它会导致SessionFixationProtectionEvent发布到应用程序上下文,如果使用changeSessionId,这种保护也将导致任何javax.servlet.http.HttpSessionIdListener被通知,所以如果你的代码侦听这两个事件要特别小心。查看Session管理章节查看更多信息。
OpenID 支持
通过一个简单的改变,命名空间可用支持用OpenID替换或者添加到普通的基于表单的登录:
<http>
<intercept-url pattern="/**" access="ROLE_USER" />
<openid-login />
</http>
然后,您应该将自己注册为一个OpenID提供商(如myopenid.com),并添加用户信息到你的基于内存的<user-service>
:
<user name="http://jimi.hendrix.myopenid.com/" authorities="ROLE_USER" />
您应该能够使用myopenid.com网站登录来进行验证。另外,也可以通过设置openid-login
元素的 user-service-ref
属性来指定一个UserDetailsService
bean来使用OpenID.查看前面的验证供应商章节了解更多信息。请注意,我们省略从上述用户配置中的密码属性,由于该组的用户数据只被用于加载当前的用户。内部会产生一个随机密码,防止你意外的将这个用户数据用作验证源到你的配置中的其他地方。
属性交换
OpenID的属性交换支持,作为一个例子,下面的例子尝试接受从OpenId供应商接收邮件和全名,用于应用程序中:
<openid-login>
<attribute-exchange>
<openid-attribute name="email" type="http://axschema.org/contact/email" required="true"/>
<openid-attribute name="name" type="http://axschema.org/namePerson"/>
</attribute-exchange>
</openid-login>
每个OpenID属性的 “type” 是一个 URI, 有一种特定的模式来确定,这个例子中是http://axschema.org/
. 如果属性必须在成功认证后被接收,可以设置 required
属性.确切的模式和属性的支持将取决于您的OpenID提供商。该属性值返回作为认证过程的一部分,随后可以使用下面的代码访问:
OpenIDAuthenticationToken token =
(OpenIDAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();
List<OpenIDAttribute> attributes = token.getAttributes();
OpenIDAttribute
包含了属性类型和接收到的值(在有多个属性值的情况下包含多个值),通过查看Spring Security核心组件的技术预览章节我们可以了解更多的SecurityContextHolder
类的使用方法。如果你希望使用多个身份提供者,多重属性交换配置也被支持。你可以提供多个attribute-exchange
元素。在每个上面使用identifier-matcher
属性。他包含一个正则表达式,会匹配由用户提供的的OpenID标识符。查看代码库的OpenID示例应用的一个示例配置。对Google ,Yahoo和MyOpenID提供了不同的属性列表。
响应头
查看如何定制头元素的更多信息请查看 安全HTTP响应头章节.
添加自己的过滤器
如果你以前使用过Spring Security,你就会知道,这个框架维护一个过滤器链,以便应用它的服务。你可能想要添加自己的过滤器到过滤器堆栈的特定位置,或者使用一个Spring Security还没有一个命名空间配置的选项的过滤器(比如CAS).或者你想使用一个标准命名空间过滤器的定制化版本,比如UsernamePasswordAuthenticationFilter
他是由<form-login>
元素显式的使用Bean来获取一些额外的高级配置选项。在过滤器链不直接暴露的情况下,你怎么使用命名空间配置做到这一点?
使用命名空间时过滤器的顺序始终严格执行。当创建应用程序上下文,过滤器Bean被命名空间处理代码进行排序,标准的Spring Security过滤器都具有的命名空间和一个众所周知的位置的别名。
在以前的版本中,排序发生在过滤器实例创建之后,在应用程序上下文后处理中。语法在3.0有一些轻微的改变,这会影响到解析
<http>
元素时,你自己的过滤器如何被添加到整个过滤器列表中
过滤器,别名和创建的命名空间元素/属性在下表列出,按照过滤器在链中的出现顺序列出:
Table 4.1. 标准过滤器别名和顺序
Alias | Filter Class | Namespace Element or Attribute |
---|---|---|
CHANNEL_FILTER | ChannelProcessingFilter | http/intercept-url@requires-channel |
SECURITY_CONTEXT_FILTER | SecurityContextPersistenceFilter | http |
CONCURRENT_SESSION_FILTER | ConcurrentSessionFilter | session-management/concurrency-control |
HEADERS_FILTER | HeaderWriterFilter | http/headers |
CSRF_FILTER | CsrfFilter | http/csrf |
LOGOUT_FILTER | LogoutFilter | http/logout |
X509_FILTER | X509AuthenticationFilter | http/x509 |
PRE_AUTH_FILTER | AbstractPreAuthenticatedProcessingFilter Subclasses | N/A |
CAS_FILTER | CasAuthenticationFilter | N/A |
FORM_LOGIN_FILTER | UsernamePasswordAuthenticationFilter | http/form-login |
BASIC_AUTH_FILTER | BasicAuthenticationFilter | http/http-basic |
SERVLET_API_SUPPORT_FILTER | SecurityContextHolderAwareRequestFilter | http/@servlet-api-provision |
JAAS_API_SUPPORT_FILTER | JaasApiIntegrationFilter | http/@jaas-api-provision |
REMEMBER_ME_FILTER | RememberMeAuthenticationFilter | http/remember-me |
ANONYMOUS_FILTER | AnonymousAuthenticationFilter | http/anonymous |
SESSION_MANAGEMENT_FILTER | SessionManagementFilter | session-management |
EXCEPTION_TRANSLATION_FILTER | ExceptionTranslationFilter | http |
FILTER_SECURITY_INTERCEPTOR | FilterSecurityInterceptor | http |
SWITCH_USER_FILTER | SwitchUserFilter | N/A |
您可以添加自己的过滤器到列表中,使用自定义过滤元件和这些名字来指定你的过滤器应该出现在的位置之一:
<http>
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
</http>
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"/>
你也可以使用 after
和before
属性来让你的过滤器插入到列表中的其他过滤器的前面和后面。FIRST
和LAST
可以用在position
属性来设置你希望将你的过滤器插入到整个列表的前面或者后面。
如果你插入的定制的过滤器可能会占用由命名空间创建的标准过滤器相同的位置,很重要的一点是不要错误的包含了命名空间的版本。移除所有你想替换的功能的过滤器元素。
注意,你不能一出有<http>
元素本省创建的过滤器,SecurityContextPersistenceFilter
,ExceptionTranslationFilter
,FilterSecurityInterceptor
,一些其他的过滤器是默认被添加的,但是你可以禁止他们。除非你禁用Session完成攻击保护,一个AnonymousAuthenticationFilter
会默认被添加,一个SessionManagementFilter
也会被添加到过滤器链。
如果你替换的命名空间过滤器需要一个验证入口点(例如:在认证过程是通过尝试触发未认证用户访问以受保护资源)你也需要添加一个定制的入口点Bean.
创建定制的 AuthenticationEntryPoint
如果你没有使用表单登录,OpenID或基本验证,你可能想想我们之前看到的一样,使用传统的bean语法,定义一个验证过滤器和入口点链接到命名空间。相应的AuthenticationEntryPoint
可以使用<http>
元素的entry-point-ref
属性进行设置。
CAS示例应用是一个很好的示例,展示命名空间的定制bean的使用。如果你不熟悉认证的入口点,可以在技术预览章节看到相关讨论。