春季安全oauth2返回404

Posted

技术标签:

【中文标题】春季安全oauth2返回404【英文标题】:spring security oauth2 returning 404 【发布时间】:2017-10-22 18:01:54 【问题描述】:

尝试实施 OAuth2 并在网络上的各种资源之后遇到一些问题,但找不到任何解决方案。问题是当我访问 /token 端点时,我得到一个 404。这是我的配置:

web.xml - Dispatcher servlet 映射到 /services/api/* 并且我的安全配置 security.xml 由 ContextLoaderListener 加载。

security.xml 中的 OAuth2 映射:

    <!-- OAUTH2 Configuration -->

<!-- Token request Endpoint configuration -->
<http pattern="/services/api/oauth2/token" use-expressions="true" create-session="stateless" authentication-manager-ref="OAuth2ClientAuthenticationManager">
    <intercept-url pattern="/services/api/oauth2/token" access="hasAnyRole('ADMIN','USER')"/>
    <anonymous enabled="false"/>
    <http-basic entry-point-ref="OAuth2TokenEntryPoint"/>
    <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/> <!-- processes client credentials params on the request -->
    <access-denied-handler ref="OAuth2AccessDeniedHandler"/>        
</http>

<!-- Resource endpoints protected by Oauth2 -->
<http pattern="/services/api/oauth2/**" create-session="never" entry-point-ref="OAuth2EntryPoint" access-decision-manager-ref="OAuth2AccessDecisionManager">
    <anonymous enabled="false"/>
    <intercept-url pattern="/services/api/oauth2/**" access="hasAnyRole('ADMIN','USER')"/>
    <custom-filter ref="OAuth2ResourceServer" before="PRE_AUTH_FILTER"/>
    <access-denied-handler ref="OAuth2AccessDeniedHandler"/>        
</http>

<!-- Authentication entrypoint for calls to obtain access tokens -->
<b:bean id="OAuth2TokenEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <b:property name="realmName" value="oauth/oauthClient"/>
    <b:property name="typeName" value="Basic"/>     
</b:bean>

<!-- Authentication entrypoint for calls to access Oauth2 protected resources -->
<b:bean id="OAuth2EntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <b:property name="realmName" value="oauth"/>
</b:bean>

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

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

<b:bean id="OAuth2AccessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
 <b:constructor-arg>
  <b:list>
   <b:bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
   <b:bean class="org.springframework.security.access.vote.RoleVoter" />
   <b:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
   <b:bean class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
  </b:list>
 </b:constructor-arg>
</b:bean>

<!-- Authentication Manager for OAuth2 clients -->
<authentication-manager id="OAuth2ClientAuthenticationManager">
    <authentication-provider user-service-ref="OAuth2ClientDetailsUserService" />
</authentication-manager>

<b:bean id="OAuth2ClientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
    <b:constructor-arg ref="OAuth2ClientDetails" />
</b:bean>

<!-- Configure Oauth2 Token storage and services -->
<b:bean id="OAuth2TokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
 <b:constructor-arg ref="pvDataSource" />
</b:bean>

<b:bean id="OAuth2TokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
    <b:property name="tokenStore" ref="OAuth2TokenStore" />
    <b:property name="supportRefreshToken" value="true" />
    <b:property name="accessTokenValiditySeconds" value="120"/>
    <b:property name="clientDetailsService" ref="OAuth2ClientDetails" />
</b:bean>

<b:bean id="OAuth2RequestFactory" class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
    <b:constructor-arg name="clientDetailsService" ref="OAuth2ClientDetails" />
</b:bean>

<b:bean id="OAuth2UserApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
    <b:property name="tokenStore" ref="OAuth2TokenStore"/>
    <b:property name="requestFactory" ref="OAuth2RequestFactory" />
</b:bean>   

<oauth:authorization-server client-details-service-ref="OAuth2ClientDetails" token-services-ref="OAuth2TokenServices">
    <oauth:authorization-code />
    <oauth:implicit/>
    <oauth:refresh-token/>
    <oauth:client-credentials />
    <oauth:password/>
</oauth:authorization-server>

<oauth:resource-server id="OAuth2ResourceServer" resource-id="oauth" token-services-ref="OAuth2TokenServices" /> 

<!-- Configuration of OAuth2 clients -->
<oauth:client-details-service id="OAuth2ClientDetails">
    <oauth:client client-id="client1" authorized-grant-types="password,authorization_code,refresh_token,implicit,client_credentials" authorities="ROLE_ADMIN,ROLE_USER" scope="read,write,trust" secret="secret"/>    
</oauth:client-details-service>

在我的数据库中,我在 oauth_client_details 表中有一条客户记录,其中 client_id = client1, client_secret = secret, authority = ROLE_ADMIN, scope = read,trust, authorized_grant_types = client_credentials,authorization_code,implicit

我的请求是 URL:http://localhost:8080/springsec/services/api/oauth2/token?grant_type=client_credentials

标题:基本身份验证,用户名:client1,密码:secret

在日志中我看到以下内容,请注意在数据库中找到了 OAuth2 客户端帐户并且授权成功,在此之后我希望它返回一个有效的 OAuth2 令牌并使用令牌信息填充相应的表,但它进入 404,因为没有找到端点模式,这真的很奇怪:

16:28:39.853 [tomcat-http--3] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'client1'
16:28:39.855 [tomcat-http--3] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
16:28:39.861 [tomcat-http--3] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Authentication success: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@c80a02b4: Principal: org.springframework.security.core.userdetails.User@334b85c6: Username: client1; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
16:28:39.861 [tomcat-http--3] DEBUG o.s.security.web.FilterChainProxy - /services/api/oauth2/token?grant_type=client_credentials at position 7 of 9 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
16:28:39.863 [tomcat-http--3] DEBUG o.s.security.web.FilterChainProxy - /services/api/oauth2/token?grant_type=client_credentials at position 8 of 9 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
16:28:39.863 [tomcat-http--3] DEBUG o.s.security.web.FilterChainProxy - /services/api/oauth2/token?grant_type=client_credentials at position 9 of 9 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
Checking match of request : '/services/api/oauth2/token'; against '/services/api/oauth2/token'
16:28:39.863 [tomcat-http--3] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /services/api/oauth2/token?grant_type=client_credentials; Attributes: [hasAnyRole('ADMIN','USER')]
16:28:39.863 [tomcat-http--3] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@c80a02b4: Principal: org.springframework.security.core.userdetails.User@334b85c6: Username: client1; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
16:28:39.871 [tomcat-http--3] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2105b1f8, returned: 1
16:28:39.871 [tomcat-http--3] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Authorization successful
16:28:39.871 [tomcat-http--3] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - RunAsManager did not change Authentication object
16:28:39.871 [tomcat-http--3] DEBUG o.s.security.web.FilterChainProxy - /services/api/oauth2/token?grant_type=client_credentials reached end of additional filter chain; proceeding with original chain
16:28:39.876 [tomcat-http--3] DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'webservices' processing GET request for [/springsec/services/api/oauth2/token]
16:28:39.880 [tomcat-http--3] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /oauth2/token
16:28:39.886 [tomcat-http--3] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [/oauth2/token]
16:28:39.886 [tomcat-http--3] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Looking up handler method for path /oauth2/token
16:28:39.887 [tomcat-http--3] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Did not find handler method for [/oauth2/token]
16:28:39.887 [tomcat-http--3] WARN  o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/springsec/services/api/oauth2/token] in DispatcherServlet with name 'webservices'
16:28:39.887 [tomcat-http--3] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
16:28:39.888 [tomcat-http--3] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
16:28:39.888 [tomcat-http--3] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed

我在这里做错了什么?任何帮助表示赞赏。

【问题讨论】:

我想我对这里发生的事情有预感: 查看 TokenEndpoint.java 看起来映射与我的 DispatcherServlet 配置不在上下文中。我尝试将此行:&lt;context:component-scan base-package="org.springframework.security.oauth2.provider.endpoint"/&gt; 添加到我的配置中,但收到一条错误消息:"Caused by: java.lang.IllegalStateException: TokenGranter must be provided TokenEndpoint 的“oauth/token”映射没有被加载,因为它没有被任何配置扫描到配置中......我该如何添加这个? 【参考方案1】:

我通过在根目录上创建一个新的 Dispatcher(因为我没有)解决了这个问题,并将授权服务器的端点设置为使用默认的“/oauth/token”。

现在,令牌正在数据库中生成,但是我得到了一个空的 Json 块而不是令牌。在 TokenEndpoint.postAccessToken() 中我可以看到端点正确生成了 ResponseEntity 但响应为空白,不知道为什么。

这是日志:

10:50:52.837 [tomcat-http--3] DEBUG o.s.jdbc.core.JdbcTemplate - Executing prepared SQL statement [insert into oauth_refresh_token (token_id, token, authentication) values (?, ?, ?)] 10:50:52.837 [tomcat-http--3] DEBUG o.s.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 10:50:52.837 [tomcat-http--3] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:oracle:thin:@localhost:1521:pvdev1] 10:50:52.865 [tomcat-http--3] DEBUG o.s.j.support.lob.DefaultLobHandler - Set bytes for BLOB with length 322 10:50:52.865 [tomcat-http--3] DEBUG o.s.j.support.lob.DefaultLobHandler - Set bytes for BLOB with length 1623 10:50:52.872 [tomcat-http--3] DEBUG o.s.jdbc.core.JdbcTemplate - SQL update affected 1 rows 10:50:52.872 [tomcat-http--3] DEBUG o.s.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource 10:50:52.898 [tomcat-http--3] DEBUG o.s.w.s.m.m.a.HttpEntityMethodProcessor - Written [0244ec49-3bbd-43d9-8b3e-00c746114fa7] as "application/json" using [org.springframework.http.converter.json.GsonHttpMessageConverter@7c0eb476] 10:50:52.899 [tomcat-http--3] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'root': assuming HandlerAdapter completed request handling 10:50:52.899 [tomcat-http--3] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request 10:50:52.900 [tomcat-http--3] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally

需要注意的是,可以将属性 token-endpoint-url 和 authentication-server-url 添加到授权服务器定义中,但是 TokenEndpoint 映射将其附加到请求中,导致端点映射为重复。

【讨论】:

以上是关于春季安全oauth2返回404的主要内容,如果未能解决你的问题,请参考以下文章

OAuth2用户详细信息未在春季安全(SpringBoot)中更新

Spring安全和角度

Spring Security OAuth2 JWT 匿名令牌

春季安全过滤器问题

如何在春季测试 LDAP 安全配置?

SpringCloud 微服务应用安全——Security