복붙노트

[SPRING] 2-legged (클라이언트 자격 증명) OAuth2 서버에 대한 스프링 보안 컨텍스트 설정

SPRING

2-legged (클라이언트 자격 증명) OAuth2 서버에 대한 스프링 보안 컨텍스트 설정

한 클라이언트에 대해 REST 서버를 보호하려면 스프링 보안 OAuth2의 최소 설정은 무엇입니까? 불필요한 설정을 사용하거나 불필요한 빈을 구현하고 싶지 않습니다. 이미 봄 보안 + OAuth2를위한 "쉬운"튜토리얼 / 예제가 있습니까? (나는 그것에 대해 너무 희망을 피하려고 노력하고 있지만)

내 현재 작업 설정 (스파크 컨텍스트에서 복사 + 붙여 넣기 + 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)를 이미 구현했다.

내가 얻지 못하는 콩들은 :

userApprovalHandler : client_credentials flow / grant에서 사용자 승인이 필요한 이유는 무엇입니까? 스파크 러는 기본 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를 통해 수행되는 경우를 제외하고는 토큰에 대한 초기 요청이 안전하지 않은 것처럼 느껴지 겠지만 충분합니까?

또한 토큰 자체는 무엇입니까? 긴 수명을 제공하고 클라이언트에서 유지해야합니까? 어쨌든 토큰 만료 예외를 catch 한 다음 새 토큰을 요청한다는 의미입니다. 아니면 모든 요청에 ​​대해 악수를해야합니까? 토큰 새로 고침은 어때? 내가 새로 고침 토큰은 클라이언트 자격 증명 유형에 대한 보안되지 않은 어딘가에 읽는 것 같아요 ..? 토큰을 HTTP 헤더로 보내야합니까, 아니면 변경할 수 있습니까? 나는 오히려 레거시 설정 (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은 서블릿 컨텍스트로 초기화됩니다. spring-context.xml 자체는 bean 만 초기화합니다. (또한 우리 서버에는 몇 가지 뷰가 있기 때문에 모든 나머지 리소스가 / rest로 실행되므로 url 패턴을 사용합니다.하지만 : 항상 별도의 서블릿과 스프링 컨텍스트가 있어야합니다.)

해결법

  1. ==============================

    1.userApprovalHandler : 시스템에 하나의 클라이언트 만있는 경우 사용자가 데이터에 액세스하는 것을 승인해서는 안되는 것에 동의합니다.

    userApprovalHandler : 시스템에 하나의 클라이언트 만있는 경우 사용자가 데이터에 액세스하는 것을 승인해서는 안되는 것에 동의합니다.

    oauthAuthenticationEntryPoint : 일반적으로 인증에 실패하면 응답 유형은 JSON입니다. 문서에 "인증이 실패하고 발신자가 특정 콘텐츠 유형 응답을 요청한 경우이 진입 점은 표준 401 상태와 함께 하나를 보낼 수 있습니다."

    clientCredentialsTokenEndpointFilter : 액세스 토큰을 발급하는 것은 두 단계 프로세스입니다. 먼저 사용자를 리소스 서버에 보내 인증합니다. 이 리디렉션은 클라이언트에서 이상적으로 HTTP 헤더 (key + secret)로 인증됩니다. 그 대가로 클라이언트는 토큰을 교환 할 수있는 코드를 얻습니다. 사용자의 승인이 없으므로 토큰의 키 + 암호를 직접 거래하지 않습니다.

    resourceServerFilter : 많은 다른 리소스가 있다면이 리소스의 목적은 클라이언트가 어떤 리소스에 액세스 할 수 있는지 나타내는 것입니다.

    accessDecisionManager : OAuth2의 경우 ScopeVoter가 필요하므로 기본 Manager가 충분하지 않습니다.

    일반적으로: 사용자 대신 리소스에 액세스하는 클라이언트가 하나 뿐인 경우 OAuth2 대신 다이제스트를 사용하는 것이 좋습니다. 그리고 사용자 (사용자 아님) 만 인증하려면 OAuth2가 과도합니다. OAuth2의 클라이언트 인증은 https를 통한 기본 인증과 실제로 동일합니다.

  2. from https://stackoverflow.com/questions/14138556/spring-security-context-setup-for-2-legged-client-credentials-oauth2-server by cc-by-sa and MIT license