복붙노트

[SPRING] Spring OAuth2 토큰 엔드 포인트에 대한 요청 당 액세스 토큰 생성

SPRING

Spring OAuth2 토큰 엔드 포인트에 대한 요청 당 액세스 토큰 생성

요청 당 client_credentials 또는 password 부여 유형을 사용하여 여러 유효한 액세스 토큰을 생성 할 수 있습니까?

위의 부여 유형을 사용하여 토큰을 생성하면 현재 요청이 요청마다 만료되면 새 토큰 만 제공됩니다.

비밀번호 부여 유형을 사용하여 새로 고침 토큰을 생성 한 다음 여러 액세스 토큰을 생성 할 수 있지만이를 수행하면 이전 액세스 토큰이 무효화됩니다.

어떻게하면 액세스 토큰이 / oauth / 토큰 끝점에 대한 요청마다 생성되고 이전 토큰이 무효화되지 않도록 할 수 있도록 변경 될 수 있습니까?

아래는 oauth 서버의 XML 설정입니다.

<!-- oauth2 config start-->
  <sec:http pattern="/test/oauth/token" create-session="never"
              authentication-manager-ref="authenticationManager" > 
        <sec:intercept-url pattern="/test/oauth/token" access="IS_AUTHENTICATED_FULLY" />
        <sec:anonymous enabled="false" />
        <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
        <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" /> 
        <sec:access-denied-handler ref="oauthAccessDeniedHandler" /> 
    </sec:http>


    <bean id="clientCredentialsTokenEndpointFilter"
          class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="authenticationManager" />
    </bean>

    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider user-service-ref="clientDetailsUserService" />
    </sec:authentication-manager>

    <bean id="clientDetailsUserService"
          class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <constructor-arg ref="clientDetails" />
    </bean>

    <bean id="clientDetails" class="org.security.oauth2.ClientDetailsServiceImpl"></bean>

    <bean id="clientAuthenticationEntryPoint"
          class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
        <property name="realmName" value="springsec/client" />
        <property name="typeName" value="Basic" />
    </bean>

    <bean id="oauthAccessDeniedHandler"
          class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>

    <oauth:authorization-server
        client-details-service-ref="clientDetails" token-services-ref="tokenServices">
        <oauth:authorization-code />
        <oauth:implicit/>
        <oauth:refresh-token/>
        <oauth:client-credentials />
        <oauth:password authentication-manager-ref="userAuthenticationManager"/>
    </oauth:authorization-server>

    <sec:authentication-manager id="userAuthenticationManager">
        <sec:authentication-provider  ref="customUserAuthenticationProvider">
        </sec:authentication-provider>
    </sec:authentication-manager>

    <bean id="customUserAuthenticationProvider"
          class="org.security.oauth2.CustomUserAuthenticationProvider">
    </bean>

    <bean id="tokenServices" 
          class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <property name="tokenStore" ref="tokenStore" />
        <property name="supportRefreshToken" value="true" />
        <property name="accessTokenValiditySeconds" value="300"></property>
        <property name="clientDetailsService" ref="clientDetails" />
    </bean>

    <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
        <constructor-arg ref="jdbcTemplate" />
    </bean>

    <bean id="jdbcTemplate"
           class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/oauthdb"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>
    <bean id="oauthAuthenticationEntryPoint"
          class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    </bean>

해결법

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

    1.다시 확인해 보면 InMemoryTokenStore가 OAuth2Authentication의 해시 문자열을 serveral Map의 키로 사용함을 발견했습니다. 그리고 같은 username, client_id, scope를 사용할 때 같은 키를 얻었습니다. 그래서 이것은 어떤 문제를 야기 할 수 있습니다. 그래서 저는 옛날 방식이 더 이상 사용되지 않을 것이라고 생각합니다. 다음은 내가이 문제를 피하기 위해 한 일이다.

    다시 확인해 보면 InMemoryTokenStore가 OAuth2Authentication의 해시 문자열을 serveral Map의 키로 사용함을 발견했습니다. 그리고 같은 username, client_id, scope를 사용할 때 같은 키를 얻었습니다. 그래서 이것은 어떤 문제를 야기 할 수 있습니다. 그래서 저는 옛날 방식이 더 이상 사용되지 않을 것이라고 생각합니다. 다음은 내가이 문제를 피하기 위해 한 일이다.

    UniqueAuthenticationKeyGenerator라는 고유 키를 계산할 수있는 다른 AuthenticationKeyGenerator를 만듭니다.

    /*
     * Copyright 2006-2011 the original author or authors.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
     * the License. You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
     * specific language governing permissions and limitations under the License.
     */
    
    /**
     * Basic key generator taking into account the client id, scope, resource ids and username (principal name) if they
     * exist.
     * 
     * @author Dave Syer
     * @author thanh
     */
    public class UniqueAuthenticationKeyGenerator implements AuthenticationKeyGenerator {
    
        private static final String CLIENT_ID = "client_id";
    
        private static final String SCOPE = "scope";
    
        private static final String USERNAME = "username";
    
        private static final String UUID_KEY = "uuid";
    
        public String extractKey(OAuth2Authentication authentication) {
            Map<String, String> values = new LinkedHashMap<String, String>();
            OAuth2Request authorizationRequest = authentication.getOAuth2Request();
            if (!authentication.isClientOnly()) {
                values.put(USERNAME, authentication.getName());
            }
            values.put(CLIENT_ID, authorizationRequest.getClientId());
            if (authorizationRequest.getScope() != null) {
                values.put(SCOPE, OAuth2Utils.formatParameterList(authorizationRequest.getScope()));
            }
            Map<String, Serializable> extentions = authorizationRequest.getExtensions();
            String uuid = null;
            if (extentions == null) {
                extentions = new HashMap<String, Serializable>(1);
                uuid = UUID.randomUUID().toString();
                extentions.put(UUID_KEY, uuid);
            } else {
                uuid = (String) extentions.get(UUID_KEY);
                if (uuid == null) {
                    uuid = UUID.randomUUID().toString();
                    extentions.put(UUID_KEY, uuid);
                }
            }
            values.put(UUID_KEY, uuid);
    
            MessageDigest digest;
            try {
                digest = MessageDigest.getInstance("MD5");
            }
            catch (NoSuchAlgorithmException e) {
                throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
            }
    
            try {
                byte[] bytes = digest.digest(values.toString().getBytes("UTF-8"));
                return String.format("%032x", new BigInteger(1, bytes));
            }
            catch (UnsupportedEncodingException e) {
                throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
            }
        }
    }
    

    마지막으로,

    <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
        <constructor-arg ref="jdbcTemplate" />
        <property name="authenticationKeyGenerator">
            <bean class="your.package.UniqueAuthenticationKeyGenerator" />
        </property>
    </bean>
    
  2. from https://stackoverflow.com/questions/27020702/spring-oauth2-generate-access-token-per-request-to-the-token-endpoint by cc-by-sa and MIT license