OAUTH 从 2.0.0 升级到 2.3.3

OAUTH Upgrade from 2.0.0 to 2.3.3

我想获得一些 OAUTH2 的经验,所以我尝试获取一些教程 运行...我已经从这个开始 spring-security-oauth2-tutorial

我从这个代码库升级到 spring-security-oauth2-2.0.0。经过一些更改后一切正常...... 在此之后,我尝试更新到 spring-security-oauth2-2.3.3,但这次我无法解决即将出现的问题。到目前为止,我通过 GET-Requests 进行身份验证,就像在教程中显示的那样...... GET-Request 看起来像这样:

http://localhost:8080/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=beingjavaguys&password=spring@java

并返回给我访问令牌等等...升级到 2.3.3 后,此调用无效。我发现,只允许 POST-请求...所以我尝试使用工具 "Postman" 将相同的数据发送到我的服务器,但后来我收到了这个异常:

142475 [qtp105302830-20] DEBUG org.springframework.security.web.util.matcher.AntPathRequestMatcher  - Checking match of request : '/oauth/token'; against '/oauth/token'
142475 [qtp105302830-20] DEBUG org.springframework.security.web.FilterChainProxy  - /oauth/token at position 1 of 7 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
142475 [qtp105302830-20] DEBUG org.springframework.security.web.FilterChainProxy  - /oauth/token at position 2 of 7 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
142475 [qtp105302830-20] DEBUG org.springframework.security.web.FilterChainProxy  - /oauth/token at position 3 of 7 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
142476 [qtp105302830-20] DEBUG org.springframework.security.web.FilterChainProxy  - /oauth/token at position 4 of 7 in additional filter chain; firing Filter: 'ClientCredentialsTokenEndpointFilter'
142476 [qtp105302830-20] DEBUG org.springframework.security.web.FilterChainProxy  - /oauth/token at position 5 of 7 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
142476 [qtp105302830-20] DEBUG org.springframework.security.web.FilterChainProxy  - /oauth/token at position 6 of 7 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
142476 [qtp105302830-20] DEBUG org.springframework.security.web.FilterChainProxy  - /oauth/token at position 7 of 7 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
142476 [qtp105302830-20] DEBUG org.springframework.security.web.util.matcher.AntPathRequestMatcher  - Checking match of request : '/oauth/token'; against '/oauth/token'
142476 [qtp105302830-20] DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor  - Secure object: FilterInvocation: URL: /oauth/token; Attributes: [IS_AUTHENTICATED_FULLY]
142476 [qtp105302830-20] DEBUG org.springframework.web.context.support.XmlWebApplicationContext  - Publishing event in Root WebApplicationContext: org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent[source=FilterInvocation: URL: /oauth/token]
142476 [qtp105302830-20] DEBUG org.springframework.security.web.access.ExceptionTranslationFilter  - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:339)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:198)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1317)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1219)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:219)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.Server.handle(Server.java:531)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
    at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:118)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:132)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:762)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:680)
    at java.base/java.lang.Thread.run(Thread.java:844)
142507 [qtp105302830-20] DEBUG org.springframework.security.web.access.ExceptionTranslationFilter  - Calling Authentication entry point.
142507 [qtp105302830-20] DEBUG org.springframework.security.oauth2.provider.error.DefaultOAuth2ExceptionRenderer  - Written [error="unauthorized", error_description="An Authentication object was not found in the SecurityContext"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@6fa103cd]
142507 [qtp105302830-20] DEBUG org.springframework.security.web.context.SecurityContextPersistenceFilter  - SecurityContextHolder now cleared, as request processing completed

我认为我犯了一个非常基本的错误,因为我试图调试我的代码但没有达到(MyClientDetailsS​​ervice、MyUserDetailsS​​ervice)。我在未经授权的情况下从邮递员那里进行了令牌调用(oauth/token),所有数据都作为表单数据发送到正文中。

这是我当前的 spring-security-xml:

<?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:context="http://www.springframework.org/schema/context"
    xmlns:sec="http://www.springframework.org/schema/security" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd 
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">

    <!-- This is default url to get a token from OAuth -->
    <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"
            after="BASIC_AUTH_FILTER" />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
    </http>

    <!-- This is where we tells spring security what URL should be protected 
        and what roles have access to them -->
    <http pattern="/api/**" 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="/api/**" access="ROLE_APP" />
        <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
    </http>


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

    <bean id="clientAuthenticationEntryPoint"
        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" />

    <bean id="clientCredentialsTokenEndpointFilter"
        class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="clientAuthenticationManager" />
    </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>

    <authentication-manager id="clientAuthenticationManager"
        xmlns="http://www.springframework.org/schema/security">
        <authentication-provider user-service-ref="clientDetailsUserService" />
    </authentication-manager>

    <authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
        <authentication-provider user-service-ref="userDetailsService">
        </authentication-provider>
    </authentication-manager>

    <bean id="userDetailsService" class="com.mystuff.MyUserDetailsService" />

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


    <!-- This defined token store, we have used inmemory tokenstore for now 
        but this can be changed to a user defined one -->
    <bean id="tokenStore"
        class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />

    <!-- This is where we defined token based configurations, token validity 
        and other things -->
    <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="120" />
        <property name="clientDetailsService" ref="clientDetails" />
    </bean>

    <bean id="userApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
        <property name="tokenStore" ref="tokenStore"/>
        <property name="requestFactory" ref="requestFactory"/>
    </bean>

    <bean id="requestFactory" class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
        <constructor-arg ref="clientDetails" />
    </bean>

    <oauth:authorization-server
        client-details-service-ref="clientDetails" 
        token-services-ref="tokenServices"
        user-approval-handler-ref="userApprovalHandler">
        <oauth:authorization-code />
        <oauth:implicit />
        <oauth:refresh-token />
        <oauth:client-credentials />
        <oauth:password />
    </oauth:authorization-server>

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

    <bean id="clientDetails" class="com.mystuff.MyClientDetailsService" />

    <sec:global-method-security
        pre-post-annotations="enabled" proxy-target-class="true">
        <!--you could also wire in the expression handler up at the layer of the 
            http filters. See https://jira.springsource.org/browse/SEC-1452 -->
        <sec:expression-handler ref="oauthExpressionHandler" />
    </sec:global-method-security>

    <oauth:expression-handler id="oauthExpressionHandler" />
    <oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>

好的,因为我认为这是一个非常基本的错误...

经过一段时间的调试后,我发现了错误,我只是通过 post-请求错误的方式发送了数据。我在 "Postman" 中使用了表单数据主体,但正确的解决方案是使用 x-www-form-urlencoded 主体...

我修改后一切正常!