尝试使用Spring MVC中的OAuth保护资源

18 java oauth spring-mvc spring-security

我们已经在Spring MVC上用Java编写了REST Web服务,我一直在努力保护它们.

OAuth服务器在另一个处理登录和创建访问令牌的网站中实现.因此,我需要在授予用户访问Web服务的权限之前验证访问令牌是否正确.

但是,使用OAuth的Spring Security文档似乎非常糟糕,示例代码实际上并没有解释它正在做什么!我甚至不确定我是否应该为此实现它,因为它应该是这么简单的检查.

保护这些Web服务的最佳方法是什么?什么是入门的最佳方式?

谢谢你的帮助.

Ben*_*rns 37

重要

[编辑12/27/2012:我在下面引用的教程现在抛出404.在github上有一个稍微更新的本教程版本.我已经看清了看起来很糟糕的链接.因为现在缺少的教程是提问者引用的教程,所以现在我将其留给后代. 据我所知,此处包含的信息仍然有用,所以也许有一天,当我有时间时,我会根据新教程重写它.

这个答案假设"OAuth服务器是在另一个处理登录和创建访问令牌的网站中实现的".您的意思是您在不属于您自己的单独网站上使用服务.


背景

我当然可以解决你的文档问题.Spring Security可以说是任何Spring项目中最陡峭的学习曲线,OAuth支持相当新,并且与Spring Security分开维护.春季安全的OAuth文档稀疏.

如果您对OAuth没有良好的感觉,那就去买一个吧! 您要求您的用户信任您网站实施此标准的安全性.因此,您无法理解您对该主题的理解!显而易见的起点是OAuth.netOAuth Beginner's Guide at huniverse.

如果/一旦您对OAuth的工作方式有了良好的了解,我强烈建议您阅读Spring Security" 入门 "和" 文章和教程 "文档列表,以便更好地了解Spring Security的实现方式.

一旦掌握了Spring Security的相关知识和对OAuth的正确了解,官方的Spring Security OAuth 用户指南就会开始变得有意义.您需要特别注意消费者/客户端部分,了解您正在使用的OAuth版本(1.02.0).

该网站还为OAuth 1.0和OAuth 2.0 提供了一个不错的教程,该教程基于上面提到的OAuth初学者指南的第二部分.

访问受保护的Restful资源

对于您的问题,我们将重点关注上述教程中Tonr照片打印服务的实施.此服务打印的照片是由外部网站托管的受OAuth保护的资源.Tonr遵循这些站点以访问这些资源.这将包括重定向用户以进行用户身份验证和必要时的身份验证确认.

Spring-MVC REST服务/控制器本身是外部OAuth保护资源的消费者,它通过使用请求过滤器来实现这种"延迟授权"(我的术语)行为.根据1.0 用户指南:

有两个适用于OAuth使用者逻辑的请求筛选器.第一个过滤器 OAuthConsumerContextFilter负责建立特定于OAuth的安全上下文,与Spring Security的SecurityContext非常相似 .安全上下文只包含一组为当前用户获取的访问令牌.在发出受保护资源请求时会利用此安全上下文.

还有另一个请求过滤器 OAuthConsumerProcessingFilter,可应用于需要访问远程受保护资源的特定URL或URL模式.将此过滤器放在Spring Security的过滤器链中将确保在允许访问资源之前获取指定URL模式所需的任何访问令牌.

正如您所看到的,对于OAuth 1.0,使用有效的过滤请求OAuthConsumerProcessingFilter将处理获取有效访问令牌的所有内容,以及在访问被拒绝时通知用户.同样,有相应的OAuth2ClientContextFilterOAuth2ClientProcessingFilter类.

最后,一旦完成设置,您就可以访问控制器中受OAuth保护的资源,OAuthRestTemplate或者OAuth2RestTemplate就像访问不受保护的资源一样RestTemplate(这里是信息).但是,必须使用ProtectedResourceDetailsOAuth2ProtectedResourceDetails实例将它们注入到服务或控制器中.

如果这听起来很复杂,我有个好消息.所有这些废话通常都是抽象的,并由OAuth和OAuth2 XML命名空间为您处理

oauth命名空间在位于各自src/webapp/WEB-INF目录中的Tonr教程的XML配置文件中进行了演示.以下示例直接从那里缩写.

如果您想在使用OAuth名称空间的情况下查看提供者方的工作方式,我建议您查看此SpringSource论坛帖子,并按照SECOAUTH-53的问题进行更新.

OAuth 1.0示例

Tonr在这里消费了来自Sparklr和Google的OAuth保护服务,因此它使用标签设置了一个ProtectedResourceDetailsService名为.然后通过使用标记设置和引用.使用标记为每个受保护资源提供程序创建这些过滤器的实例.resourceDetailsoauth:resource-details-serviceOAuthConsumerContextFilterOAuthConsumerProcessingFilterresourceDetailsoauth:consumerProtectedResourceDetailsoauth:resource


来自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>
Run Code Online (Sandbox Code Playgroud)


接下来,sparklrServicegoogleService创建豆,每个都有自己的内部OAuthRestTemplate豆,其每一个通过设置有参考constructor-arg到相应的ProtectedResourceDetails其中以前创建的并注入ProtectedResourceDetailsService豆.


来自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>
Run Code Online (Sandbox Code Playgroud)


OAuth 2.0示例

我的理解在这里有点弱. 造成这种情况的部分原因是OAuth2名称空间似乎抽象了很多.此外,看起来Tonr 2的例子还没有充实,以及最初的Tonr示例.我会尽我所能并在必要时进行编辑.

首先oauth:client创建一个标记并给出对InMemoryOAuth2ClientTokenServicesbean 的引用.看来这会设置适当的过滤器.然后用OAuth2ProtectedResourceDetailsspark创建了针对sparklr和Facebook的bean oauth:resource.


从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"/>
Run Code Online (Sandbox Code Playgroud)


接下来,就像在前面的示例中一样,需要访问受保护资源的每个控制器或服务bean都是使用内部OAuth2RestTemplatebean 创建的.这个内部bean被赋予对正确OAuth2ProtectedResourceDetailsbean 的引用constructor-arg.


从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>
Run Code Online (Sandbox Code Playgroud)