我试图在我的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配置错了吗?
由于