我有一个问题试图在我的Spring项目中实现oauth2身份验证方面 . 这是我的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: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-1.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd ">


<sec:global-method-security pre-post-annotations="enabled"/>

<http pattern="/oauth/token" create-session="stateless"
          authentication-manager-ref="authenticationManager"
          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" />
    <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" /> 
    <access-denied-handler ref="oauthAccessDeniedHandler" />
</http>

<http pattern="/resources/**" create-session="never"
          entry-point-ref="oauthAuthenticationEntryPoint"
          xmlns="http://www.springframework.org/schema/security">
    <anonymous enabled="false" />
    <intercept-url pattern="/resources/**" method="GET" />
    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
</http>

<http pattern="/logout" create-session="never" 
          entry-point-ref="oauthAuthenticationEntryPoint"
          xmlns="http://www.springframework.org/schema/security">
    <anonymous enabled="false" />
    <intercept-url pattern="/logout" method="GET" />
    <sec:logout invalidate-session="true" logout-url="/logout" success-handler-ref="logoutSuccessHandler"   />
    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
</http>

<bean id="logoutSuccessHandler" class="com.*****.rest.security.oauth.LogoutImpl" >
    <property name="tokenstore" ref="tokenStore"></property>
</bean>

<bean id="oauthAuthenticationEntryPoint"
          class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>

<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="oauthAccessDeniedHandler"
          class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
</bean>

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

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

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

<bean id="clientDetails" class="com.*****.rest.security.oauth.ClientServiceImpl">
    <property name="mobileUserService" ref="mobileUserService" />
</bean>

<!-- authentication Manager -->
<authentication-manager id="userAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
    <authentication-provider ref="customUserAuthenticationProvider"/>
</authentication-manager>

<bean id="customUserAuthenticationProvider"
          class="com.*****.rest.security.oauth.MobileUserAuthenticationProvider">
</bean>

<!-- Authorization Server -->
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
    <oauth:authorization-code />
    <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="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="accessTokenValiditySeconds" value="300000"></property>
    <property name="clientDetailsService" ref="clientDetails" />
</bean>

<mvc:annotation-driven />   <!-- Declares explicit support for annotation-driven MVC controllers  @RequestMapping, @Controller -->

<mvc:default-servlet-handler />

我有两个身份验证管理器:一个用于客户端凭据,另一个用于用户凭据 .

这是web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring-servlet.xml, /WEB-INF/spring-oauth-security4.xml</param-value>
</context-param>

<context-param>
    <param-name>resteasy.use.deployment.sensitive.factory</param-name>
    <param-value>false</param-value>
</context-param>

<context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/services</param-value>
</context-param>

<filter>
    <filter-name>encoding-filter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter>
    <filter-name>oemInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    <init-param>
        <param-name>entityManagerFactoryBeanName</param-name>
        <param-value>entityManagerFactory</param-value>
    </init-param>
</filter>



<!-- Filters Mapping -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>
        org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
</filter>

<filter-mapping>
    <filter-name>encoding-filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
    <filter-name>oemInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Listeners -->
<listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
    <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>



<!-- RESTEasy HTTP Request processor-->
<servlet>
    <servlet-name>restServlet</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>restServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

我有两个自定义类:实现“ClientDetailsService”的ClientServiceImpl:

package com.******.rest.security.oauth;

    import com.*****.beans.MobileUser;
    import com.******.service.MobileUserService;
    import com.******.service.core.exception.ServiceException;
    import java.util.ArrayList;
    import java.util.List;
    import javax.persistence.NoResultException;
    import org.hibernate.NonUniqueResultException;
    import org.jasypt.springsecurity3.authentication.encoding.PasswordEncoder;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
    import org.springframework.security.oauth2.provider.BaseClientDetails;
    import org.springframework.security.oauth2.provider.ClientDetails;
    import org.springframework.security.oauth2.provider.ClientDetailsService;
    import org.springframework.security.oauth2.provider.NoSuchClientException;
    import org.springframework.stereotype.Service;

    @Service
    public class ClientServiceImpl implements ClientDetailsService {

    @Autowired
    private MobileUserService mobileUserService;

    private static final org.slf4j.Logger log = LoggerFactory.getLogger(ClientServiceImpl.class);

    @Override
    public ClientDetails loadClientByClientId(String clientId) throws OAuth2Exception {

        log.info("OAUTH2::inLoadClientByClientId");
        MobileUser mobileUser;
        try {
            mobileUser = this.mobileUserService.findMobileUserByPublicId(clientId);

            List<String> authorizedGrantTypes = new ArrayList<String>();
            authorizedGrantTypes.add("password");
            authorizedGrantTypes.add("refresh_token");
            authorizedGrantTypes.add("client_credentials");

            BaseClientDetails clientDetails = new BaseClientDetails();
            clientDetails.setClientId(mobileUser.getUsrPublicId());
            clientDetails.setClientSecret("secret");
            clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);

            log.info("OAUTH2::inLoadClientByClientId::ClientFounded!");
            log.info("OAUTH2::clientId=" + clientDetails.getClientId());
            log.info("OAUTH2::clientSecret=" + clientDetails.getClientSecret());
            log.info("OAUTH2::inLoadClientByClientId::All Clear!?");
            return clientDetails;
        } 
        catch (NonUniqueResultException ex) {
            log.info("OAUTH2::NonUniqueResultException::" + ex.getMessage());
            throw new NoSuchClientException("No client recognized with id: "
                    + clientId + "2");
        } 
        catch (NoResultException ex) {
            log.info("OAUTH2::NoResultException::" + ex.getMessage());
            throw new NoSuchClientException("No client recognized with id: "
                    + clientId);
        }
        catch (BadCredentialsException ex) {
            log.info("OAUTH2::BadCredentialsException::" + ex.getMessage());
            throw new BadCredentialsException("Bad User Credentials.");
        }
        catch (ServiceException ex) {
            log.info("OAUTH2::ServiceException::" + ex.getMessage());
            return null;
        }
    }

    public MobileUserService getMobileUserService() {
        return mobileUserService;
    }

    public void setMobileUserService(MobileUserService mobileUserService) {
        this.mobileUserService = mobileUserService;
    }
}

和MobileUserAuthenticationProvider实现“AuthenticationProvider”:

package com.*****.rest.security.oauth;

import java.util.ArrayList;
import java.util.List;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;

public class MobileUserAuthenticationProvider implements AuthenticationProvider {

private static final org.slf4j.Logger log = LoggerFactory.getLogger(MobileUserAuthenticationProvider.class);

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    log.info("OAUTH2::inAuthenticate");
    if (authentication.getPrincipal().equals("test@test.com") && authentication.getCredentials().equals("TEST")) {

        log.info("OAUTH2::inAuthenticate::User Match!");
        List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
        CustomMobileUserPasswordAuthenticationToken auth = new CustomMobileUserPasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), grantedAuthorities);

        return auth;
    } else {
        log.info("OAUTH2::inAuthenticate::User DONT Match!");
        throw new BadCredentialsException("Bad User Credentials.");
    }
}

@Override
public boolean supports(Class<?> type) {
    log.info("OAUTH2::inSupports");
    return true;
}
}

问题是:从不调用MobileUserAuthenticationProvider . 当我尝试执行像这样的oauth / token请求时:

http://127.0.0.1:8081/*****-rest/oauth/token?username=test@test.com&password=TEST&client_id=test@test.com&grant_type=password

结果如下:

11:48:25,458 INFO  [com.*****.rest.security.oauth.ClientServiceImpl] (http--127.0.0.1-8081-1)      OAUTH2::inLoadClientByClientId
11:48:25,739 INFO  [com.*****.rest.security.oauth.ClientServiceImpl] (http--127.0.0.1-8081-1) OAUTH2::inLoadClientByClientId::ClientFounded!
11:48:25,740 INFO  [com.*****.rest.security.oauth.ClientServiceImpl] (http--127.0.0.1-8081-1) OAUTH2::clientId=test@test.com
11:48:25,741 INFO  [com.*****.rest.security.oauth.ClientServiceImpl] (http--127.0.0.1-8081-1) OAUTH2::clientSecret=secret
11:48:25,741 INFO  [com.*****.rest.security.oauth.ClientServiceImpl] (http--127.0.0.1-8081-1) OAUTH2::inLoadClientByClientId::All Clear!?
11:48:25,747 DEBUG [org.springframework.security.authentication.dao.DaoAuthenticationProvider] (http--127.0.0.1-8081-1) Authentication failed: password does not match stored value
11:48:25,748 DEBUG [org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter] (http--127.0.0.1-8081-1) Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
11:48:25,750 DEBUG [org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter] (http--127.0.0.1-8081-1) Updated SecurityContextHolder to contain null Authentication
11:48:25,751 DEBUG [org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter] (http--127.0.0.1-8081-1) Delegating to authentication failure handlerorg.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter$1@4450d62b
11:48:25,804 DEBUG [org.springframework.security.oauth2.provider.error.DefaultOAuth2ExceptionRenderer] (http--127.0.0.1-8081-1) Written [error="invalid_client", error_description="Bad client credentials"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@15db49b7]
11:48:25,806 DEBUG [org.springframework.security.web.context.SecurityContextPersistenceFilter] (http--127.0.0.1-8081-1) SecurityContextHolder now cleared, as request processing completed

所以,我想这个过程的第一部分做得很好 . 调用“ClientServiceImpl”,我能够在我的数据库中检查是否有一个具有此ID的客户端 .

但是,第二部分:

<oauth:password authentication-manager-ref="userAuthenticationManager"/>

我的自定义authenticationManager永远不会被调用 . 我不明白为什么Spring Security会调用他的DaoAuthenticationProvider而不是我的 .

有人有想法吗?我的xml配置错了吗?

谢谢