我正在尝试使用0-Legged身份验证与Spring Security和OAuth 2.0一起使用我的RESTfull API。我也是Spring Security curl和OAuth的新手,所以我可能做错了,但我现在试图解决它。
执行curl命令时:
curl -X POST "http://localhost:8080/rest/oauth/token" -d \ "username=admin&password=admin&client_id=test&client_secret=test&grant_type=password"
我得到以下例外:
{"error":"invalid_grant","error_description":"Bad credentials"}
经过一些调试后,我发现方法AbstractUserDetailsAuthenticationProvider
中的Spring Security类authenticate(Authentication authentication)
中出现了用于用户凭据身份验证(curl:admin,admin),客户端凭据(curl:test,test)的错误被毫无例外地被处理。
此行引发异常:
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
由于此行上的用户名字符串设置为NONE_PROVIDED:
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
经过一些更多的调试后,我在这个方法中发现了Spring Security OAuth2类ResourceOwnerPasswordTokenGranter
中的类:
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
String username = parameters.get("username");
String password = parameters.get("password");
// Protect from downstream leaks of password
parameters.remove("password");
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
userAuth = authenticationManager.authenticate(userAuth);
}
catch (AccountStatusException ase) {
//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
throw new InvalidGrantException(ase.getMessage());
}
catch (BadCredentialsException e) {
// If the username/password are wrong the spec says we should send 400/invlid grant
throw new InvalidGrantException(e.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
由于parameters
beeing&#34;中的键,用户名在下一行设置为null用户名和#34; (注意前面的空白区域)而不是&#34;用户名&#34;:
String username = parameters.get("username");
所以我尝试切换username
和密码参数,是的,首先设置的参数会获得键值前面的空白区域。我可以通过添加另一个username
参数(或以先到者为准)解决这个问题,但这不是解决方案。
以下是Spring OAuth配置:
<?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:sec="http://www.springframework.org/schema/security"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="
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
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2.xsd">
<sec:http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="authenticationManager">
<sec:intercept-url pattern="/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>
<sec:http pattern="/logout" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false"/>
<sec:intercept-url pattern="/logout" access="ROLE_USER" method="GET"/>
<sec:logout invalidate-session="true" logout-url="/logout" success-handler-ref="logoutSuccessHandler"/>
<sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<sec:access-denied-handler ref="oauthAccessDeniedHandler"/>
</sec:http>
<sec:http pattern="/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false"/>
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<sec:access-denied-handler ref="oauthAccessDeniedHandler"/>
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService"/>
</sec:authentication-manager>
<sec:authentication-manager alias="userAuthenticationManager">
<sec:authentication-provider>
<sec:password-encoder hash="bcrypt" />
<sec:jdbc-user-service data-source-ref="jdbcDataSource"
users-by-username-query="SELECT username, passhash, enabled FROM appuser WHERE USERNAME=?;"
authorities-by-username-query="SELECT u.username, r.name FROM appuser u, roletype r, appuser_roletype a WHERE u.id = a.appuser_id AND r.id = a.roletype_id AND username =?;"/>
</sec:authentication-provider>
</sec:authentication-manager>
<sec:global-method-security
secured-annotations="enabled" />
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials/>
<oauth:password authentication-manager-ref="userAuthenticationManager"/>
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="springsec" token-services-ref="tokenServices"/>
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="accessTokenValiditySeconds" value="86400" />
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore">
</bean>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" />
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails"/>
</bean>
<bean id="clientDetails" class="com.mytest.oauth.ClientDetailsServiceImplementation"/>
<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="logoutSuccessHandler" class="com.mytest.oauth.LogoutImplementation">
<property name="tokenstore" ref="tokenStore"/>
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
</beans>
我该如何避免这种行为?