OAuth 集成
现在的很多 Web 服务都提供 API 接口,允许第三方应用使用其数据。当第三方应用需要访问用户私有数据的时候,需要进行认证。OAuth 是目前流行的一种认证方式,被很多 Web 服务采用,包括 Twitter、LinkedIn、Google Buzz 和新浪微博等。OAuth 的特点是第三方应用不能直接获取到用户的密码,而只是使用一个经过用户授权之后的令牌(token)来进行访问。用户可以对能够访问其数据的第三方应用进行管理,通过回收令牌的方式来终止第三方应用对其数据的访问。OAuth 的工作方式涉及到服务提供者、第三方应用和用户等 3 个主体。其基本的工作流程是:第三方应用向服务提供者发出访问用户数据的请求。服务提供者会询问用户是否同意此请求。如果用户同意的话,服务提供者会返回给第三方应用一个令牌。第三方应用只需要在请求数据的时候带上此令牌就可以成功获取。
第三方应用在使用 OAuth 认证方式的时候,其中所涉及的交互比较复杂。Spring Security 本身并没有提供 OAuth 的支持,通过另外一个开源库 OAuth for Spring Security 可以实现。OAuth for Spring Security 与 Spring Security 有着很好的集成,可以很容易在已有的使用 Spring Security 的应用中添加 OAuth 的支持。不过目前 OAuth for Spring Security 只对 Spring Security 2.0.x 版本提供比较好的支持。对 OAuth 的支持包括服务提供者和服务消费者两个部分:服务提供者是数据的提供者,服务消费者是使用这些数据的第三方应用。一般的应用都是服务消费者。OAuth for Spring Security 对服务提供者和消费者都提供了支持。下面通过获取 LinkedIn 上的状态更新的示例来说明其用法。
作为 OAuth 的服务消费者,需要向服务提供者申请表示其应用的密钥。服务提供者会提供 3 个 URL 来与服务消费者进行交互。代码清单 11中给出了使用 OAuth for Spring Security 的配置文件。
清单 11. 使用 OAuth for Spring Security 的配置文件
<oauth:consumer resource-details-service-ref="linkedInResourceDetails"
oauth-failure-page="/oauth_error.jsp">
<oauth:url pattern="/linkedin.do**" resources="linkedIn" />
</oauth:consumer>
<bean id="oauthConsumerSupport"
class="org.springframework.security.oauth.consumer.CoreOAuthConsumerSupport">
<property name="protectedResourceDetailsService" ref="linkedInResourceDetails" />
</bean>
<oauth:resource-details-service id="linkedInResourceDetails">
<oauth:resource id="linkedIn"
key="***" secret="***"
request-token-url="https://api.linkedin.com/uas/oauth/requestToken"
user-authorization-url="https://www.linkedin.com/uas/oauth/authorize"
access-token-url="https://api.linkedin.com/uas/oauth/accessToken" />
</oauth:resource-details-service>
如 代码清单 11所示,只需要通过对 元素进行简单的配置,就可以声明使用 LinkedIn 的服务。每个 元素对应一个 OAuth 服务资源。该元素的属性包含了与该服务资源相关的信息。OAuth for Spring Security 在 Spring Security 提供的过滤器的基础上,额外增加了处理 OAuth 认证的过滤器实现。通过 的子元素 可以定义过滤器起作用的 URL 模式和对应的 OAuth 服务资源。当用户访问指定的 URL 的时候,应用会转到服务提供者的页面,要求用户进行授权。当用户授权之后,应用就可以访问其数据。访问数据的时候,需要在 HTTP 请求中添加额外的 Authorization头。代码清单 12给出了访问数据时使用的代码。