如果我想为一个客户端保护REST服务器,那么spring-security OAuth2的最小设置是什么?我不想使用任何不必要的设置或实现任何不必要的bean。对于spring-security + OAuth2,可能已经有一个“简单”的教程/示例了吗? (虽然我试图避免对此抱太大的希望)
我当前的工作设置(使用来自sparklr上下文的副本+过去+ wtf)感觉太多了:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:client-credentials />
</oauth:authorization-server>
<sec:authentication-manager alias="clientAuthenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<oauth:resource-server id="resourceServerFilter"
resource-id="rest_server" token-services-ref="tokenServices" />
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="the_client" authorized-grant-types="client_credentials"
authorities="ROLE_RESTREAD" secret="1234567890" />
</oauth:client-details-service>
<http pattern="/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/rest/**" access="ROLE_RESTREAD" method="GET" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="false" />
<property name="clientDetailsService" ref="clientDetails" />
<property name="accessTokenValiditySeconds" value="400000" />
<property name="refreshTokenValiditySeconds" value="0" />
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"
xmlns="http://www.springframework.org/schema/beans">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="theRealm" />
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="theRealm/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>
我已经实现了authenticationManager(UserDetailsService)作为实现基本spring-security的一部分,以便对我们的数据库保留帐户和角色。
我真正得到的豆子是:
userApprovalHandler :为什么我需要在client_credentials流/授权中获得任何用户批准?似乎,sparklr会覆盖默认的TokenServicesUserApprovalHandler
以自动批准一个客户端。我是否也需要这样做以便在我信任的客户端和服务器之间进行通信?
oauthAuthenticationEntryPoint :所有sparklr都是这样做的:
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="sparklr2" />
</bean>
该怎么办?
clientCredentialsTokenEndpointFilter 它说,我应该只包括这个,如果我想通过请求参数进行身份验证..所以我的想法正是这样:发送一个GET(?)请求到我的服务器与秘密并获得一个令牌和该令牌访问资源?所以我在想,令牌的请求应该包含秘密作为请求参数..?
resourceServerFilter 在我看来,这表明一个单独的资源服务器?如果我的资源与身份验证提供程序位于同一服务器上,那如何应用?
的AccessDecisionManager 我不记得在设置自定义弹簧安全实现时必须使用它,为什么我现在要这样做呢?
感谢您的阅读!希望有人能回答我的一些问题..
我已将设置更新为当前工作状态。我现在可以使用客户端凭据请求访问令牌:
$ curl -X -v -d 'client_id=the_client&client_secret=secret&grant_type=client_credentials' -X POST "http://localhost:9090/our-server/oauth/token"
并使用该令牌访问受保护资源:
$ curl -H "Authorization: Bearer fdashuds-5432fsd5-sdt5s5d-sd5" "http://localhost:9090/our-server/rest/social/content/posts"
它仍然感觉像很多设置,我的问题仍然存在。此外,我想知道这是否是确保可信客户端和REST服务器之间通信的正确方法。
除了通过https完成之外,它仍然感觉对令牌的初始请求不安全,但这样就足够了吗?
那么令牌本身呢,我应该给它一个很长的生命周期并坚持它在客户端?在任何情况下都意味着捕获令牌过期异常,然后请求新的异常。或者我应该为每个请求做握手吗?刷新令牌怎么样?我想我在哪里读过刷新令牌对于客户端凭证授权类型不安全..?是否有必要将令牌作为HTTP标头发送,还是可以更改?我不想为我们的客户端使用spring-security客户端堆栈,因为它有一个相当传统的设置(jboss 5),到目前为止我们所做的只是将REST通信功能与请求参数集成。
它还有助于更多地了解所有弹簧安全设置,但文档很薄。
将弹簧安全配置更新为当前状态。另外,这是我们的web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>the-display-name</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>base.package.rest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/servlet-context.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.appServlet</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
注意:上面的spring-security-context.xml将由servlet-context初始化。 spring-context.xml本身只初始化bean。 (另外:我们的服务器也有一些视图,因此所有其余资源都在/ rest下运行,因此url-pattern。但是:总是需要一个单独的servlet和spring上下文。)
答案 0 :(得分:9)
userApprovalHandler :如果您的系统中只有一个客户端,我同意用户不必批准它访问他们的数据。
oauthAuthenticationEntryPoint :通常,如果身份验证失败,则响应类型为JSON。文档说“如果身份验证失败并且调用者已请求特定内容类型响应,则此入口点可以发送一个,以及标准401状态。”
clientCredentialsTokenEndpointFilter :发出访问令牌需要两个步骤。首先,将用户发送到资源服务器进行身份验证。此重定向由客户端验证,理想情况下使用HTTP标头(密钥+密钥)。作为回报,客户端获取代码,可以交换令牌。 您不直接交换密钥+密钥作为令牌,因为它不包含用户的批准。
resourceServerFilter :我认为这样做的目的是说明如果您有许多不同的资源,哪些客户可以访问哪些资源。
accessDecisionManager :对于OAuth2,您需要一个ScopeVoter,因此默认的Manager不够好。
<强>一般强>: 如果您只有一个客户端代表用户访问资源,那么可以考虑使用Digest而不是OAuth2? 如果您只想验证客户端(而不是用户),那么OAuth2就太过分了。 OAuth2中的客户端身份验证与基于https的基本身份验证完全相同。
答案 1 :(得分:4)
REST客户端和具有spring security oauth 2.0的用户的oauth2的一个很好的例子: http://www.e-zest.net/blog/rest-authentication-using-oauth-2-0-resource-owner-password-flow-protocol/