Discrepancy in the user roles(authorities) in the

2019-08-22 02:55发布

问题:

I have a situation:

Step 1: Obtained access token (grant_type=password) (A1) and also a refresh token.(RT1)

Step 2: Accessed resource (R) using the token (A1) - Success

Step 3:Revoked user access role for Resource R.

Step 4: Obtained access token (grant_type=password) (A2) and also a refresh token.(RT2)

Step 5: Accessed resource (R) using the token (A2) - Failed

till here all fine.now comes the unexpected part.

Step 6: Obtained new access token (grant_type=refresh_token) using RT2. Unexpectedly using this access token i was able to access resource R.

During this whole flow none of the token was expired one.

I see two issues here:- User roles aren't getting updated for refresh token on grant_type=password and for grant_type=refresh_token. Although access token has changed (Step 4) but refresh token remains same RT1 == RT2. hence any further usage of RT gives access token with previous roles.

How do i tell spring (oauth2) to update user roles (for the newly created token's) while obtaining the access token using refresh token and also while updating RT with new roles (step4), to resolve this discrepancy.

Below is the Authorization server configuration:

 <?xml version="1.0" encoding="UTF-8"?>
<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:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd
                           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
                           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


  <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
    <property name="userDetailsService">
      <bean class="com.dummy.mc.security.service.UserDetailsServiceImpl">
        <property name="userRepository" ref="userRepository" />
        <property name="grantedAuthorityRepository" ref="grantedAuthorityRepository" />
      </bean>
    </property>
    <property name="passwordEncoder">
      <bean class="com.dummy.mc.security.password.McpmPasswordEncoder">
        <property name="encodeHashAsBase64" value="true" />
      </bean>
    </property>
    <property name="saltSource">
      <bean class="org.springframework.security.authentication.dao.ReflectionSaltSource">
        <property name="userPropertyToUse" value="salt" />
      </bean>
    </property>
  </bean>


    <!--https://stackoverflow.com/questions/49761597/spring-oauth2-clientid-passed-in-as-username-for-password-grant-type-->

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

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

    </bean>

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

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



    <!-- Authentication manager for client (not resource-owner) authentication required to
        protect the token endpoint URL -->

    <security:authentication-manager id="clientDetailAuthenticationManager">
        <security:authentication-provider user-service-ref="clientDetailsUserService"/>
    </security:authentication-manager>

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

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

    <security:http pattern="/oauth/token" create-session="stateless" use-expressions="true" authentication-manager-ref="authenticationManager">
        <security:intercept-url pattern="/oauth/token" access="isAuthenticated()" />
        <security:anonymous enabled="false" />
        <security:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
        <!-- include this only if you need to authenticate clients via request
            parameters -->
        <security:custom-filter ref="clientCredentialsTokenEndpointFilter"
                                after="BASIC_AUTH_FILTER" />
        <security:access-denied-handler ref="oauthAccessDeniedHandler" />
    </security:http>

    <authorization-server client-details-service-ref="clientDetailsService"
                          xmlns="http://www.springframework.org/schema/security/oauth2" token-services-ref="tokenServices" >
        <authorization-code />
        <implicit />
        <refresh-token />
        <client-credentials />
        <password authentication-manager-ref="authenticationManager" />
    </authorization-server>

    <!-- <oauth:resource-server id="resourceFilter" token-services-ref="tokenServices" authentication-manager-ref="authenticationManager" />
 -->
    <security:authentication-manager id="authenticationManager">
        <security:authentication-provider ref="daoAuthenticationProvider">
        </security:authentication-provider>
    </security:authentication-manager>

    <oauth:client-details-service id="clientDetailsService">

        <oauth:client client-id="core-api" secret="secret"
                      authorized-grant-types="password,client_credentials,refresh_token" scope="read"
                      resource-ids="api-core" access-token-validity="36000"
                      authorities="ROLE_CLIENT,ROLE_TRUSTED_CLIENT" />
    </oauth:client-details-service>
</beans>

Resource Server Configuration:

 <mvc:default-servlet-handler />

    <mvc:annotation-driven/>
    <security:global-method-security pre-post-annotations="enabled"/>

    <!-- TODO: make an access denied view that tells me something useful -->
    <security:http  use-expressions="true"  entry-point-ref="oauthAuthenticationEntryPoint">
        <security:intercept-url pattern="/**" access="isFullyAuthenticated() and hasRole('api.core')" />
        <security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
        <security:access-denied-handler ref="oauthAccessDeniedHandler" />

        <security:anonymous />
    </security:http>


    <!-- It's just a "feature" of the Spring Security that an authentication manager is mandatory.
        so install an empty one because it isn't used at run time -->
    <security:authentication-manager/>



    <oauth:resource-server id="resourceServerFilter"  token-services-ref="tokenServices" resource-id="api-core"/>


    <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices" >
        <property name="tokenStore" ref="tokenStore" />
    </bean>


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

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


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

回答1:

Authorities are loaded when access token its required. Using jdbc store, authorities are saved to OAUTH_ACCESS_TOKEN table, AUTHENTICATION column.

When refresh token its required, authorities are loaded from database.

If authorities changed after access token was required, you will have to implement custom token store.

Take a look to org.springframework.security.oauth2.provider.token.store.JdbcTokenStore, and extend from it.