用于2脚(客户端凭据)OAuth2服务器的Spring-security上下文设置 [英] Spring-security context setup for 2-legged (client credentials) OAuth2 server

查看:123
本文介绍了用于2脚(客户端凭据)OAuth2服务器的Spring-security上下文设置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我想为一个客户端保护REST服务器,那么spring-security OAuth2的最小设置是什么?我不想使用任何不必要的设置或实现任何不必要的bean。也许有一个简单的教程/示例已经针对spring-security + OAuth2? (虽然我试图避免对此抱太大的希望)

What's the minimal setup for spring-security OAuth2 if I want to secure a REST server for one client? I don't want to use any unnecessary setup or implement any unnecessary beans. Maybe there's an "easy" tutorial / example out there already for spring-security + OAuth2? (Though I'm trying to avoid being too hopeful about that)

我当前的工作设置(使用来自sparklr上下文的副本+过去+ wtf)感觉也是如此很多:

My current working setup (working with the copy+past+wtf from the sparklr context) feels like too much:

<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(UserDetailsS​​ervice)作为实现基本spring-security的一部分,以便帐户和角色都是针对我们的数据库保留的。

I already have implemented the authenticationManager (UserDetailsService) as a part of implementing basic spring-security so that accounts and roles are persisted against our database.

我真正得到的豆子是:

userApprovalHandler :为什么我需要在client_credentials流/授权中获得任何用户批​​准?似乎,sparklr会覆盖默认的 TokenServicesUserApprovalHandler 以自动批准一个客户端。我是否也需要这样做以便在我信任的客户端和服务器之间进行通信?

userApprovalHandler: Why would I need any user approval in a client_credentials flow / grant? It seems, sparklr overrides the default TokenServicesUserApprovalHandler to auto-approve one client. Do I need to do that as well for the communication between my trusted client(s) and the server?

oauthAuthenticationEntryPoint :所有sparklr都是关于这是:

oauthAuthenticationEntryPoint: all sparklr does about this is:

<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="realmName" value="sparklr2" />
</bean>

该怎么办?

clientCredentialsTokenEndpointFilter
它说,只有当我想通过请求参数进行身份验证时才应包含此内容。所以我想到的就是:向我的服务器发送GET(?)请求有了秘密并获得一个令牌并使用该令牌访问资源?所以我在想,令牌的请求应该包含秘密作为请求参数..?

clientCredentialsTokenEndpointFilter It says, I should include this only if I want to authenticate via request parameters.. So what I have in mind is exactly that: Send a GET(?) request to my server with the secret and get a token and with that token access the resources? So I'm thinking, the request for the token should contain the secret as request parameter..?

resourceServerFilter
似乎对我来说,这表明一个单独的资源服务器?如果我的资源与身份验证提供程序位于同一服务器上,那么该如何应用?

resourceServerFilter It seems to me that this indicates a separate resource server? How does that apply if my resources are on the same server as the authentication provider?

accessDecisionManager
我不记得在设置我的自定义spring-security实现时必须使用它,为什么我要现在这样做?

accessDecisionManager I don't remember having to use this when setting up my custom spring-security implementation, why would I want to do so now?

感谢阅读!希望有人能回答我的一些问题..

Thanks for reading through! Hope someone can answer a few of my questions..

我已将设置更新为目前的工作状态。我现在可以使用客户端凭证请求访问令牌:

I've updated the setup to the current working state. I can now request an access token with the client credentials:

$ curl -X -v -d 'client_id=the_client&client_secret=secret&grant_type=client_credentials' -X POST "http://localhost:9090/our-server/oauth/token"

并使用该令牌访问受保护资源:

and use that token to access protected resources:

$ curl -H "Authorization: Bearer fdashuds-5432fsd5-sdt5s5d-sd5" "http://localhost:9090/our-server/rest/social/content/posts"

它仍然感觉像很多设置,我的问题仍然存在。另外,我想知道这是否是确保可信客户端和REST服务器之间通信的正确方法。

It still feels like a lot of setup and my questions remain. Also I'm wondering if this is the right way to go for securing the communication between trusted client and REST server in general.

除了通过https完成之外,它仍然感觉对令牌的初始请求不安全,但这样就足够了吗?

It also still feels like the initial request for the token is not secure except if done via https, but will that suffice?

那么令牌本身呢,我应该给它一个很长的生命周期并坚持在客户端吗?在任何情况下都意味着捕获令牌到期异常然后请求新的异常。或者我应该为每个请求做握手吗?刷新令牌怎么样?我想我在某处读到刷新令牌对于客户端凭证授权类型不安全..?是否有必要将令牌作为HTTP标头发送,还是可以更改?我不想为我们的客户端使用spring-security客户端堆栈,因为它有一个相当传统的设置(jboss 5),到目前为止我们所做的只是将REST通信功能与请求参数集成。

Also what about the token itself, should I give it a long lifetime and persist it on the client? that would in any case mean catching a token expiration exception and then requesting a new one. Or should I do the handshake for every request? What about refreshing the token? I think I read somewhere that refresh token is not secure for the client credentials grant type..? Is it necessary to send the token as HTTP header or can I change that? I don't want to use the spring-security client stack for our client as it has a rather legacy setup (jboss 5) and all we did so far was integrate REST communication capabilities with request parameters..

这也有助于更多地了解所有弹簧安全设置,但文档非常薄..

It would also help to know more about all the spring-security setup but the documentation is quite thin..

将弹簧安全配置更新为当前状态。另外,这是我们的web.xml:

Updated the spring security configuration to our current state. Also, here's our 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上下文。)

Note: The spring-security-context.xml from above will get initialized by the servlet-context. The spring-context.xml itself only initializes the beans. (Also: Our Server also has a few views so all rest resources run under /rest hence the url-pattern. But: It is always necessary to have a separate servlet and spring context.)

推荐答案

userApprovalHandler :如果您的系统中只有一个客户端,我同意用户不应该批准它访问他们的数据。

userApprovalHandler: if you only have one client in your system, I agree the users should not have to approve it accessing their data.

oauthAuthenticationEntryPoint :通常,如果身份验证失败,则响应类型为JSON。文档说如果身份验证失败并且调用者已请求特定内容类型响应,则此入口点可以发送一个,以及标准401状态。

oauthAuthenticationEntryPoint: Normally, if authentication fails, the response type is JSON. Documentation says "If authentication fails and the caller has asked for a specific content type response, this entry point can send one, along with a standard 401 status."

clientCredentialsTokenEndpointFilter :发出访问令牌分为两步。首先,将用户发送到资源服务器进行身份验证。此重定向由客户端验证,理想情况下使用HTTP标头(密钥+密钥)。作为回报,客户端获取代码,可以交换令牌。
您不直接交换密钥+密钥作为代币,因为它不包含用户的批准。

clientCredentialsTokenEndpointFilter: Issuing an access token is a two-step process. First, you send the user to the resource server to authenticate. This redirect is authenticated by the client, ideally with the HTTP Headers (key + secret). In return, the client gets a code, which can be exchanged for a token. You do not directly trade the key+secret for a token, as it contains no approval from the user.

resourceServerFilter :我认为这样做的目的是说明如果你有许多不同的资源,客户可以访问哪些资源。

resourceServerFilter: I think the purpose of this is indicating what clients have access to what resources, if you have many different resources.

accessDecisionManager :对于OAuth2,您需要一个ScopeVoter,因此默认的Manager不够好。

accessDecisionManager: For OAuth2 you need a ScopeVoter, so the default Manager is not good enough.

一般
如果你只有一个客户端代表用户访问资源,那么可以考虑使用Digest代替OAuth2吗?
如果您只想验证客户端(而不是用户),那么OAuth2就太过分了。 OAuth2中的客户端身份验证与基于https的基本身份验证完全相同。

Generally: If you will only have one client accessing the resources on behalf of the users, then maybe consider using Digest instead of OAuth2? And if you only want to authenticate the client (not the user), then OAuth2 is overkill. Client authentication in OAuth2 is really same as Basic Authentication over https.

这篇关于用于2脚(客户端凭据)OAuth2服务器的Spring-security上下文设置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆