尝试在春季 MVC 中使用 OAuth 保护资源重要背景访问受保护的宁静资源

2022-09-02 20:15:05

我们已经在Spring MVC上用Java编写了REST Web服务,我一直在尝试保护它们。

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

然而,使用OAuth的Spring Security的文档似乎真的很差,示例代码实际上并没有解释它在做什么!我甚至不确定我是否应该为此实现它,因为它应该是一个如此简单的检查。

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

感谢您的帮助。


答案 1

重要

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

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


背景

我当然可以理解你的文档问题。Spring Security可以说是所有Spring项目中最陡峭的学习曲线,OAuth支持是相当新的,并且与Spring Security分开维护。Spring Security OAuth文档很少

如果您对OAuth没有良好的感觉,请去买一个!您要求用户信任您网站实施此标准的安全性。因此,您在理解该主题时不能有任何歧义!显而易见的起点是 OAuth.net 和huniverse的OAuth初学者指南

如果/一旦你对OAuth的工作原理有了很好的了解,我强烈建议你阅读Spring Security的“入门”和“文章和教程”文档列表,以便对Spring Security的一般实施方式有一个很好的了解。

一旦您对Spring Security有了体面的了解和对OAuth的体面了解,官方的Spring Security OAuth用户指南将开始变得有意义。您需要特别注意您正在使用的OAuth版本(1.0或2.0)的消费者/客户端部分。

同一站点还为OAuth 1.0和OAuth 2.0提供了一个不错的教程,该教程基于上述服务OAuth初学者指南的第二部分。

访问受保护的宁静资源

对于您的问题,我们将重点介绍上述教程中Tonr照片打印服务的实现。此服务打印照片是由外部站点托管的受 OAuth 保护的资源。Tonr 遵从这些站点以访问控制这些资源。这将包括重定向用户以进行用户身份验证和身份验证确认(如有必要)。

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

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

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

因此,如您所见,对于 OAuth 1.0,筛选具有有效访问权限的请求将处理与获取有效访问令牌相关的所有操作,并在访问被拒绝时通知用户。同样,还有相应的 OAuth2ClientContextFilterOAuth2ClientProcessingFilter 类。OAuthConsumerProcessingFilter

最后,完成所有设置后,您可以使用或就像使用正常方式访问未受保护的资源一样访问控制器中的OAuth受保护资源(此处的信息)。但是,它们必须使用 ProtectedResourceDetailsOAuth2ProtectedResourceDetails 实例注入到您的服务或控制器中。OAuthRestTemplateOAuth2RestTemplateRestTemplate

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

oauth 命名空间在 Tonr 教程的 XML 配置文件中演示,这些文件位于各自的 src/webapp/WEB-INF 目录中。下面的示例是直接从此处缩写的。

如果你想看看提供程序端在不使用OAuth命名空间的情况下是如何工作的,我建议你查看这个SpringSource论坛帖子,并关注SECOAUTH-53问题以获取更新。

OAuth 1.0 示例

Tonr正在这里使用来自Sparklr和Google的OAuth保护服务,因此它使用标签设置了一个调用。然后,它使用标记设置 和 的引用。这些筛选器是使用 标记为每个受保护的资源提供程序创建实例的。ProtectedResourceDetailsServiceresourceDetailsoauth:resource-details-serviceOAuthConsumerContextFilterOAuthConsumerProcessingFilterresourceDetailsoauth:consumerProtectedResourceDetailsoauth:resource


从 tonr 的应用程序Context.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>


接下来创建和bean,每个bean都有自己的内部bean,每个bean都通过先前创建并注入bean的相应bean提供参考。sparklrServicegoogleServiceOAuthRestTemplateconstructor-argProtectedResourceDetailsProtectedResourceDetailsService


从tonr的弹簧服务.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示例那样充实。我会尽力而为,并在必要时进行编辑。

首先创建一个标签,并给出对Bean的引用。这似乎设置了适当的过滤器。然后使用.oauth:clientInMemoryOAuth2ClientTokenServicesOAuth2ProtectedResourceDetailsoauth:resource


从 tonr 2 的应用程序Context.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 都是使用内部 Bean 创建的。通过 此内部 Bean 被赋予对正确 Bean 的引用。OAuth2RestTemplateOAuth2ProtectedResourceDetailsconstructor-arg


从 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>

答案 2

推荐