如果我想为一个客户端保护REST服务器,春季安全OAuth2的最低设置是什么?我不想使用任何不必要的设置或实现任何不必要的bean。也许对于spring- security + OAuth2已经有一个“简单”的教程/示例了?(尽管我试图避免对此抱太大希望)
我当前的工作设置(从sparklr上下文中使用copy + past + 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以自动批准一个客户端。在受信任的客户端和服务器之间进行通信时,我也需要这样做吗?
TokenServicesUserApprovalHandler
oauthAuthenticationEntryPoint :sparklr对此所做的所有操作是:
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> <property name="realmName" value="sparklr2" /> </bean>
那应该怎么办?
clientCredentialsTokenEndpointFilter 它说,仅当我想通过请求参数进行身份验证时,才应包括此请求。因此,我要牢记的是:将GET(?)请求与机密一起发送到我的服务器,并获得令牌和该令牌访问资源?所以我在想,对令牌的请求应该包含秘密作为请求参数..?
resourceServerFilter 在我看来,这表示一个单独的资源服务器?如果我的资源与身份验证提供程序位于同一服务器上,该如何应用?
accessDecisionManager 我不记得设置我的自定义spring-security实现时必须使用它,为什么现在要这样做?
感谢您的通读!希望有人可以回答我的几个问题。
我已将设置更新为当前的工作状态。现在,我可以使用客户端凭据请求访问令牌:
$ 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通信功能与请求参数集成在一起。
进一步了解所有spring-security设置也将有所帮助,但是文档非常薄。
将spring安全配置更新为我们的当前状态。另外,这是我们的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上下文。)
userApprovalHandler :如果您的系统中只有一个客户端,则我同意用户不必批准该客户端访问其数据。
oauthAuthenticationEntryPoint :通常,如果身份验证失败,则响应类型为JSON。文档说:“如果身份验证失败,并且呼叫者请求了特定的内容类型响应,则此入口点可以发送一个消息以及标准的401状态。”
clientCredentialsTokenEndpointFilter :发出访问令牌是一个两步过程。首先,将用户发送到资源服务器进行身份验证。客户端最好使用HTTP标头(密钥+机密)对重定向进行身份验证。作为回报,客户端获得一个代码,可以将其交换为令牌。您不直接将密钥+秘密交易为令牌,因为它不包含来自用户的批准。
resourceServerFilter :如果您有许多不同的资源,我 认为这样做 的目的是指示哪些客户端可以访问哪些资源。
accessDecisionManager :对于OAuth2,您需要一个ScopeVoter,因此默认的Manager不够好。
通常 :如果您只有一个客户端代表用户访问资源,那么也许考虑使用Digest而不是OAuth2?而且,如果您只想验证客户端(而不是用户),则OAuth2会显得过高。OAuth2中的客户端身份验证实际上与基于https的基本身份验证相同。