重要
[编辑 12/27/2012:我在下面引用的教程现在抛出 404。在 github 上有本教程的略微更新版本。我已经浏览了看起来很糟糕的链接。现在我将这个原样留给后代,因为现在缺少的教程是提问者引用的教程。 据我所知,这里包含的信息仍然有用,所以也许有一天我有时间会根据新教程重写它。]
此答案假定“OAuth 服务器在另一个网站中实现,该网站处理登录和访问令牌的创建。”您的意思是您正在使用不属于您自己的单独网站上的服务。
背景
我当然可以理解您的文档问题。 Spring Security 可以说是所有 Spring 项目中学习曲线最陡峭的,并且 OAuth 支持是相当新的,并且与 Spring Security 分开维护。 Spring Security OAuth 文档是稀疏的。
如果您对 OAuth 不满意,那就去买一个吧!您要求您的用户相信您的网站实施此标准的安全性。因此,您对这个主题的理解不能有任何歧义!显而易见的起点是 huniverse 的 OAuth.net 和 OAuth Beginner's Guide。
如果/一旦您对 OAuth 的工作原理有了很好的了解,我强烈建议您通读 Spring Security “Getting Started” 和 “Articles and Tutorials” 文档列表,以更好地了解 Spring Security 的实现方式一般。
一旦你对 Spring Security 和 OAuth 有相当的了解,官方的 Spring Security OAuth user guide 就会开始变得有意义。您需要特别注意您正在使用的 OAuth 版本的消费者/客户端部分(1.0 或 2.0)。
同一个站点也有一个不错的 tutorial 用于 OAuth 1.0 和 OAuth 2.0,它基于上面提到的服务的第二部分 OAuth Beginner's Guide。
访问受保护的 Restful 资源
对于您的问题,我们将重点关注上述 tutorial 的 Tonr 照片打印服务的实施。此服务打印照片,这些照片是由外部站点托管的 OAuth 保护资源。 Tonr 遵从这些站点对这些资源的访问控制。这将包括在必要时重定向用户以进行用户身份验证和身份验证确认。
Spring-MVC REST 服务/控制器本身就是外部 OAuth 受保护资源的消费者,它们通过使用请求过滤器来实现这种“延迟授权”(我的术语)行为。根据 1.0 user guide:
有两个请求过滤器
适用于 OAuth 消费者
逻辑。第一个过滤器,
OAuthConsumerContextFilter,是
负责建立一个
OAuth 特定的安全上下文,非常
类似于 Spring Security 的
SecurityContext。安全上下文
只包含一组访问令牌
已获得的
当前用户。这个安全上下文是
在提出请求时利用
受保护的资源。
还有另一个请求过滤器,
OAuthConsumerProcessingFilter,那个
可以应用于特定的 URL 或 URL
需要访问的模式
远程受保护资源。推杆
Spring Security 中的这个过滤器
过滤器链将确保任何
指定所需的访问令牌
URL模式将在之前获得
允许访问资源。
如您所见,对于 OAuth 1.0,过滤带有有效 OAuthConsumerProcessingFilter 的请求将处理获取有效访问令牌的所有事情,并在访问被拒绝时通知用户。同样有对应的OAuth2ClientContextFilter 和OAuth2ClientProcessingFilter 类。
最后,一旦完成所有设置,您就可以使用OAuthRestTemplate 或OAuth2RestTemplate 访问控制器中受OAuth 保护的资源,就像使用普通RestTemplate(信息here)访问未受保护的资源一样。但是,它们必须通过 ProtectedResourceDetails 或 OAuth2ProtectedResourceDetails 的实例注入到您的服务或控制器中。
如果这听起来很复杂,我有个好消息。所有这些废话通常都被 OAuth 和 OAuth2 XML 命名空间抽象出来并为您处理
oauth 命名空间在 Tonr 教程的 XML 配置文件中演示,这些文件位于各自的 src/webapp/WEB-INF 目录中。下面的例子是直接从那里缩写的。
如果您想了解提供方如何在不使用 OAuth 命名空间的情况下工作,我建议您查看this SpringSource forum post,并关注问题SECOAUTH-53 以获取更新。
OAuth 1.0 示例
Tonr 在此处使用来自 Sparklr 和 Google 的 OAuth 保护服务,因此它使用 oauth:resource-details-service 标签设置了一个名为 resourceDetails 的 ProtectedResourceDetailsService。然后它使用oauth:consumer 标记设置OAuthConsumerContextFilter 和OAuthConsumerProcessingFilter 并引用resourceDetails。这些过滤器是使用oauth:resource 标记为每个受保护的资源提供者创建的ProtectedResourceDetails 实例。
来自 tonr 的 applicationContext.xml:
<oauth:consumer resource-details-service-ref="resourceDetails" oauth-failure-page="/oauth_error.jsp">
<oauth:url pattern="/sparklr/**" resources="sparklrPhotos"/>
<oauth:url pattern="/google/**" resources="google"/>
</oauth:consumer>
<oauth:resource-details-service id="resourceDetails">
<oauth:resource id="sparklrPhotos"
key="tonr-consumer-key"
secret="SHHHHH!!!!!!!!!!"
request-token-url="http://localhost:8080/sparklr/oauth/request_token"
user-authorization-url="http://localhost:8080/sparklr/oauth/confirm_access"
access-token-url="http://localhost:8080/sparklr/oauth/access_token"/>
<!--see http://code.google.com/apis/accounts/docs/OAuth_ref.html-->
<oauth:resource id="google" key="anonymous" secret="anonymous"
request-token-url="https://www.google.com/accounts/OAuthGetRequestToken"
user-authorization-url="https://www.google.com/accounts/OAuthAuthorizeToken"
access-token-url="https://www.google.com/accounts/OAuthGetAccessToken"
request-token-method="GET"
access-token-method="GET">
<oauth:addtionalParameter name="scope" value="https://picasaweb.google.com/data/"/>
<oauth:addtionalParameter name="xoauth_displayname" value="Tonr Example Application"/>
</oauth:resource>
</oauth:resource-details-service>
接下来创建sparklrService 和googleService bean,每个bean 都有自己的内部OAuthRestTemplate bean,每个都通过constructor-arg 提供对各自ProtectedResourceDetails 的引用,这些ProtectedResourceDetails 先前创建并注入@987654363 @豆。
来自 tonr 的 spring-servlet.xml:
<bean id="sparklrService" class="org.springframework.security.oauth.examples.tonr.impl.SparklrServiceImpl">
<property name="sparklrPhotoListURL" value="${sparklrPhotoListURL}"/>
<property name="sparklrPhotoURLPattern" value="${sparklrPhotoURLPattern}"/>
<property name="sparklrRestTemplate">
<bean class="org.springframework.security.oauth.consumer.OAuthRestTemplate">
<constructor-arg ref="sparklrPhotos"/>
</bean>
</property>
</bean>
<bean id="googleService" class="org.springframework.security.oauth.examples.tonr.impl.GoogleServiceImpl">
<property name="googleRestTemplate">
<bean class="org.springframework.security.oauth.consumer.OAuthRestTemplate">
<constructor-arg ref="google"/>
</bean>
</property>
</bean>
OAuth 2.0 示例
我的理解有点弱。部分原因是 OAuth2 命名空间似乎抽象了很多。此外,看起来 Tonr 2 示例还没有像原来的 Tonr 示例那样充实。如有必要,我会尽力而为。
首先创建一个oauth:client 标记并给出对InMemoryOAuth2ClientTokenServices bean 的引用。这似乎设置了适当的过滤器。然后使用 oauth:resource 为 sparklr 和 Facebook 创建 OAuth2ProtectedResourceDetails bean。
来自 tonr 2 的 applicationContext.xml:
<!--apply the oauth client context-->
<oauth:client token-services-ref="oauth2TokenServices"/>
<beans:bean id="oauth2TokenServices" class="org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices"/>
<!--define an oauth 2 resource for sparklr-->
<oauth:resource id="sparklr" type="authorization_code" clientId="tonr"
accessTokenUri="http://localhost:8080/sparklr/oauth/authorize"
userAuthorizationUri="http://localhost:8080/sparklr/oauth/user/authorize"/>
<!--define an oauth 2 resource for facebook. according to the facebook docs, the 'clientId' is the App ID, and the 'clientSecret' is the App Secret -->
<oauth:resource id="facebook" type="authorization_code" clientId="162646850439461" clientSecret="560ad91d992d60298ae6c7f717c8fc93"
bearerTokenMethod="query" accessTokenUri="https://graph.facebook.com/oauth/access_token"
userAuthorizationUri="https://www.facebook.com/dialog/oauth"/>
接下来,就像在前面的示例中一样,每个需要访问受保护资源的控制器或服务 bean 都是使用内部 OAuth2RestTemplate bean 创建的。通过constructor-arg 为这个内部bean 提供了对正确OAuth2ProtectedResourceDetails bean 的引用。
来自 tonr 2 的 spring-servlet.xml:
<bean id="facebookController" class="org.springframework.security.oauth.examples.tonr.mvc.FacebookController">
<!-- snipped irrelevant properties -->
<property name="facebookRestTemplate">
<bean class="org.springframework.security.oauth2.consumer.OAuth2RestTemplate">
<constructor-arg ref="facebook"/>
</bean>
</property>
<property name="tokenServices" ref="oauth2TokenServices"/>
</bean>
<bean id="sparklrService" class="org.springframework.security.oauth.examples.tonr.impl.SparklrServiceImpl">
<!-- snipped irrelevant properties -->
<property name="sparklrRestTemplate">
<bean class="org.springframework.security.oauth2.consumer.OAuth2RestTemplate">
<constructor-arg ref="sparklr"/>
</bean>
</property>
<property name="tokenServices" ref="oauth2TokenServices"/>
</bean>