Spring 0-Legged身份验证会抛出错误" invalid_grant"描述"不良凭证"使用curl

时间:2015-09-24 14:23:45

标签: java curl oauth-2.0 spring-security-oauth2

我正在尝试使用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;:

screen shot from debugger

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>

我该如何避免这种行为?

0 个答案:

没有答案