无法在 Spring Oauth2 密码授予中获取访问令牌
Posted
技术标签:
【中文标题】无法在 Spring Oauth2 密码授予中获取访问令牌【英文标题】:Unable to get the access token in Spring Oauth2 password grant 【发布时间】:2015-07-21 22:41:35 【问题描述】:我正在尝试将 Oauth2 与 Spring 集成,以在我的应用程序中保护我的 Restful 服务。 我面临的问题是,一旦我通过有效的客户端 ID 来获取 resfresh/访问令牌,应用程序就会重定向到带有 404 响应的 url,而不是在不考虑用户名、密码的情况下返回访问令牌参数。正在使用的Url如下:
http://localhost:8080/oauthSample/oauth/token?grant_type=password&client_id=testabc&username=user&password=password
对于客户端 ID 无效的 URL(例如 http://localhost:8080/oauthSample/oauth/token?grant_type=password&client_id=test&username=user&password=password),返回的响应是
<oauth>
<error_description>No client with requested id: test</error_description>
<error>unauthorized</error>
</oauth>
安全applicationContext.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<!-- SPRING OAUTH IMPLICIT SECURTY CONFIG START -->
<!-- Definition of the Authentication Service -->
<http pattern="/oauth/token" create-session="stateless"
xmlns="http://www.springframework.org/schema/security"
authentication-manager-ref="clientAuthenticationManager">
<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>
<!-- Protected resources -->
<http pattern="/services/**"
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="/services/**" access="ROLE_USER"/>
<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="dstest"/>
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="dstest/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 in config file -->
<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 id="userDetailsService">
<user name="admin" password="password" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails"/>
</bean>
<!-- Token Store -->
<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="true"/>
<property name="clientDetailsService" ref="clientDetails"/>
<!-- <property name="accessTokenValiditySeconds" value="100"/> -->
</bean>
<bean id="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
<property name="tokenServices" ref="tokenServices"/>
</bean>
<!-- Token management -->
<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="dstest"
token-services-ref="tokenServices"/>
<!-- Client Definition -->
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="testabc"
authorized-grant-types="password,authorization_code,refresh_token,implicit,redirect"
authorities="ROLE_CLIENT, ROLE_TRUSTED_CLIENT"
redirect-uri="/web"
scope="read,write,trust"
access-token-validity="30"
refresh-token-validity="600"/>
</oauth:client-details-service>
<security:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
<security:expression-handler ref="oauthExpressionHandler"/>
</security:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler"/>
<oauth:web-expression-handler id="oauthWebExpressionHandler"/>
<!-- SPRING OAUTH IMPLICIT SECURTY CONFIG END -->
</beans>
eclipse 控制台日志(如果需要)如下:
2015-05-11 17:47:40,315 [http-bio-8080-exec-6] DEBUG (AntPathRequestMatcher.java:103) Ð Checking match of request : '/oauth/token'; against '/oauth/token'
2015-05-11 17:47:40,315 [http-bio-8080-exec-6] DEBUG (FilterChainProxy.java:318) Ð /oauth/token?grant_type=password&client_id=testabc&username=user&password=password at position 1 of 6 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2015-05-11 17:47:40,315 [http-bio-8080-exec-6] DEBUG (FilterChainProxy.java:318) Ð /oauth/token?grant_type=password&client_id=testabc&username=user&password=password at position 2 of 6 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (FilterChainProxy.java:318) Ð /oauth/token?grant_type=password&client_id=testabc&username=user&password=password at position 3 of 6 in additional filter chain; firing Filter: 'ClientCredentialsTokenEndpointFilter'
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (AbstractAuthenticationProcessingFilter.java:188) Ð Request is to process authentication
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (ProviderManager.java:152) Ð Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (AbstractAuthenticationProcessingFilter.java:311) Ð Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a2dfd9fc: Principal: org.springframework.security.core.userdetails.User@ab371290: Username: testabc; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_CLIENT,ROLE_TRUSTED_CLIENT; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_CLIENT, ROLE_TRUSTED_CLIENT
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (FilterChainProxy.java:318) Ð /oauth/token?grant_type=password&client_id=testabc&username=user&password=password at position 4 of 6 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (FilterChainProxy.java:318) Ð /oauth/token?grant_type=password&client_id=testabc&username=user&password=password at position 5 of 6 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (FilterChainProxy.java:318) Ð /oauth/token?grant_type=password&client_id=testabc&username=user&password=password at position 6 of 6 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (AntPathRequestMatcher.java:103) Ð Checking match of request : '/oauth/token'; against '/oauth/token'
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (AbstractSecurityInterceptor.java:193) Ð Secure object: FilterInvocation: URL: /oauth/token?grant_type=password&client_id=testabc&username=user&password=password; Attributes: [IS_AUTHENTICATED_FULLY]
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (AbstractSecurityInterceptor.java:298) Ð Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a2dfd9fc: Principal: org.springframework.security.core.userdetails.User@ab371290: Username: testabc; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_CLIENT,ROLE_TRUSTED_CLIENT; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_CLIENT, ROLE_TRUSTED_CLIENT
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (AffirmativeBased.java:65) Ð Voter: org.springframework.security.access.vote.RoleVoter@1838b82, returned: 0
2015-05-11 17:47:40,316 [http-bio-8080-exec-6] DEBUG (AffirmativeBased.java:65) Ð Voter: org.springframework.security.access.vote.AuthenticatedVoter@13c2982, returned: 1
2015-05-11 17:47:40,317 [http-bio-8080-exec-6] DEBUG (AbstractSecurityInterceptor.java:214) Ð Authorization successful
2015-05-11 17:47:40,317 [http-bio-8080-exec-6] DEBUG (AbstractSecurityInterceptor.java:226) Ð RunAsManager did not change Authentication object
2015-05-11 17:47:40,317 [http-bio-8080-exec-6] DEBUG (FilterChainProxy.java:304) Ð /oauth/token?grant_type=password&client_id=testabc&username=user&password=password reached end of additional filter chain; proceeding with original chain
2015-05-11 17:47:40,318 [http-bio-8080-exec-6] DEBUG (ExceptionTranslationFilter.java:115) Ð Chain processed normally
2015-05-11 17:47:40,318 [http-bio-8080-exec-6] DEBUG (SecurityContextPersistenceFilter.java:97) Ð SecurityContextHolder now cleared, as request processing completed
我使用的环境如下:
1. Spring 3.1.0 Release
2. Oauth2 1.0.5 Release
3. Eclipse Luna
4. Maven
5. Apache tomcat 7.0.61
在客户端通过身份验证后,启用基于用户的身份验证是否需要进行任何更改?
【问题讨论】:
【参考方案1】:搞定了...错误出现在指向 /services/* 的 web.xml servlet 映射 url 模式中。相反,它应该如下所示:
<servlet-mapping>
<servlet-name>services</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
现在遇到另一个问题.. :( .. 调度程序 servlet 现在也在处理静态内容请求 (index.html)
Did not find handler method for [/index.html]
任何解决相同问题的指针将不胜感激。
【讨论】:
以上是关于无法在 Spring Oauth2 密码授予中获取访问令牌的主要内容,如果未能解决你的问题,请参考以下文章
如何使用密码授予在 Spring Boot Oauth2 资源服务器中处理 CORS
Spring Boot Oauth2 验证资源所有者密码凭证授予的访问令牌
使用 Spring Security 为 Web 客户端授予针对 REST 服务器的 Oauth2 密码