现在你可能会意识到我们 remember me 功能的实现,能够在应用重启前很好的使用,但在应用重启时用户的 session 会被丢失。这对用户来说会不太便利,他们不应该关心 JBCP Pets 的维护信息。
幸运的是, Spring Security 提供了将 rememberme token 持久化到任何存储的接口 o.s.s.web.authentication.rememberme.PersistentTokenRepository ,并提供了这个接口的 JDBC 实现。
在这里,修改 remember me 的配置以持久化到数据库是非常简单的。 Spring Security 配置的解析器能够识别出 <remember-me> 声明的 data-source-ref 新属性并为 RememberMeServices 切换实现类。让我们了解完成这个功能所需要的步骤。
我们需要将包含期望 schema 定义的 SQL 文件放在 classpath 下( WEB-INF/classes 中),它会与我们在前面使用的其它启动 SQL 脚本放在一起。我们将这个 SQL 脚本命名为 remember-me-schema.sql :
接下来,在 dogstore-security.xml 文件的 <embedded-database> 声明中添加对新 SQL 脚本的引用:
最后我们需要对 <remember-me> 声明做一些简单的配置修改使其指向我们使用的 data source :
这就是我们所有要做的。现在,如果你重启应用,将不会丢失之前合法用户设置的 remember me cookie 。
你可能会回忆起我们在第三章实现的 TokenBasedRememberMeServices ,它用 MD5 哈希算法将一系列与用户相关的数据编码成安全的 cookie ,这种方式很难(但并非不可能)篡改。 o.s.s.web.authentication.rememberme.PersistentTokenBasedRememberMeServices 类实现了持久化 tokens 以及对 token 安全处理,它通过一个校验方法以稍微不同的方式处理潜在的篡改。
PersistentTokenBasedRememberMeServices 为每个用户创建一个唯一的序列号, 用户在继续交互和认证时要使用序列号中唯一的 tokens 。序列号和 token 被存储在 cookie 中,在认证时要用来与存储的 token 进行对比。序列号和 token 都是基于配置的长度随机生成的,这使得恶意用户成功暴力破解的可能性很小了。
与 TokenBasedRememberMeServices 类似,持久化的 token 也可能被 cookie 窃取或其它的 man-in-the-middle 技术。在使用持久化 token 时,依旧建议用自定义的子类将 IP 地址合并到持久化 token 中,以及对站点的敏感区域强制使用用户名和密码认证。
在日常使用在线站点时,你很可能已经听说或使用过 SSL 。安全套接字层( SSL )协议,以及其后续的传输层安全( TLS ),被用来为网络上的 HTTP 事务提供传输层的安全——它们被称为安全的 HTTP 事务( HTTPS )。
简而言之, SSL 和 TLS 以一种对用户透明的方式保护原始的 HTTP 传输数据,这些数据在客户端浏览器和 web 服务器之间传输。但是作为开发人员,在设计安全站点时,规划使用 SSL 是很重要的。 Spring Security 提供了一系列的配置选项可以灵活的将 SSL 集成到 web 应用中。
【尽管 SSL 和 TLS 是不同的协议( TLS 是更成熟的协议),单数大多数人更熟悉 SSL 这个术语,所以在本书的剩余部分,我们使用这个术语来代指 SSL 和 TLS 两个协议。】
详细介绍 SSL 协议的机制已经超出了本书的范围,有一些很好的书籍和技术论文很详细地介绍了其规范和协议(你可以从 RFC : 5246 :传输安全协议( TLS ) Version1.2 开始,在以下地址 http://tools.ietf.org/html/rfc5246 )
首先且最重要的是,如果你计划执行如下 SSL 相关的例子,需要配置应用服务器以支持 SSL 连接。对于 Apache Tomcat ,这相对很容易。如果你在使用其它的应用服务器,请查看文档的相关部分。
我们需要使用 Java 的 keytool 命令来生成一个 key store 。打开一个命令提示窗口,并输入以下的命令:
按照提示进行如下的输入。输入密码 password 作为 key store 和个人密钥的密码。
这将会在当前目录下,生成一个名为 tomcat.keystore 的文件。这就是启用 Tomcat SSL 所使用的 key store 。
【注意的是要执行的是 genkeypair 命令(在早于 java 6 的释放版本中要使用 keytool 的 genkey 命令)】
为了下一步的操作,需要记住这个文件的地址。
在 Apache Tomcat 的 conf 目录下,用 XML 编辑器( Eclipse 或类似的都可以)打开 server.xml ,并取消注释或添加 SSL Connector 声明。应该如下所示:
确保在上一步中生成的 tomcat.keystore 文件被 copy 到了 Tomcat 安装路径的 conf 目录下。在配置后, Tomcat 服务器可以重启, JBCP Pets 应用能够在一个安全的端口 https://localhost:8443/JBCPPets/ 上进行访问。
取决于不同的浏览器,可能需要包含 https 而不是 http 。这样的问题可能会比较难发现,你可能会比较奇怪为什么不能看到 JBCP Pets 的主页。
我们假设你在对客户的数据进行 SSL 保护时遇到了麻烦,你想把应用的特定部分置于 SSL 的保护之下。幸运的是, Spring Security 让这一切变得很简单,只需要在 <intercept-url> 声明上添加一个配置属性。
requires-channel 属性能够添加到任何 <intercept-url> 声明中,以要求所有匹配的 URL 要以特定的协议( HTTP , HTTPS 或都可以)进行传递。如果按照这种形式来增强 JBCP Pets 站点,配置可能如下所示:
如果此时重启应用,你将会发现:
l 现在访问登录页和账号页需要 HTTPS ,浏览器将会为用户自动从不安全的( HTTP ) URL 重定向到安全的 URL 。例如,尝试访问 http://localhost:8080/JBCPPets/login.do 将会被定向到 https://localhost:8443/JBCPPets/login.do ;
l 如果用户被切换到了安全的 HTTPS URL ,如果他访问一个不必要使用 HTTPS 的 URL ,他能继续保留在 HTTPS 状态。
我们可以想象这种配置对于安全的好处——大多数的现代应用服务器使用一个 secure 标识 session 的 cookie ,所以强制要求登录页是安全的(如果这是应用的 session 被首次分配的地方)能够保证 session 的 cookie 能够被安全的传输,所以出现 session 劫持的可能性也更小。另外,直接将 SSL 加密配置在安全声明上的做法,能够很容易的保证应用中所有敏感的页面被适当和完整的保护。
为用户自动切换适当协议( HTTP 或 HTTPS )的功能,通过 Spring Security 过滤器链上的另外一个 servlet 过滤器来实现的(它的位置很靠前,在 SecurityContextPersistenceFilter 后面)。如果任何 URL 用 requires-channel 属性声明使用特定类型的协议, o.s.s.web.access.channel.ChannelProcessingFilter 将会自动添加到过滤器链上
ChannelProcessingFilter 在请求时的交互过程如下图所示:
如果你的应用需要超出内置功能的复杂逻辑, ChannelProcessingFilter 的设计可以进行扩展和增强。注意我们尽管只在图中说明了 SecureChannelProcessor 和 RetryWithHttpsEntryPoint 的实现,但是有类似的类去校验和处理声明为要求 HTTP 的 URL 。
注意, ChannelEntryPoint 使用了 HTTP 302 的 URL 重写,这就不能使用这种技术去重定向 POST 的 URL (尽管典型的 POST 请求不应该在安全协议和不安全协议间传递,因为大多数的应用都会对这种行为提出警告)。
在一些特定的环境中,可能不会使用标准的 HTTP 和 HTTPS 端口,其默认为 80/443 或 8080/8443 。在这种情况下,你必须配置你的应用包含明确的端口映射,这样 ChannelEntryPoint 的实现能够确定当重定向用户到安全或不安全的 URL 时,使用什么端口。
这仅需要增加额外的配置元素 <port-mappings> ,它能够指明除了默认的端口以外,额外的 HTTP 的 HTTPS 端口:
如果你的应用服务器在反向代理后的话,端口映射将会更加的重要。
在本章中,我们:
l 介绍了把安全数据存储在支持 JDBC 的数据库中是如何配置的;
l 配置 JBCP Pets 使用数据库来进行用户认证以及高安全性的密码存储,这里我们使用了密码加密和 salting 技术;
l 管理 JDBC 持久化到数据中的用户;
l 配置用户到安全组中。组被授予角色,而不是直接对用户进行角色的指定。这提高了站点和用户功能的可管理性;
l 介绍了 Spring Security 使用遗留的(非默认的)数据库 schema ;
l 讲解了 HTTPS 技术的配置及应用,它能够提高数据在访问应用敏感内容时的安全性。
在接下来的章节中,我们将会介绍 Spring Security 一些高级的授权功能,并引入 Spring Security 的 JSP 标签以实现良好的授权。