Spring OAuth2 - 无法获取客户端令牌
Posted
技术标签:
【中文标题】Spring OAuth2 - 无法获取客户端令牌【英文标题】:Spring OAuth2 - Can't get client token 【发布时间】:2015-06-02 12:16:17 【问题描述】:我不能 100% 确定我正在执行正确的请求,但我似乎无法为客户获取令牌。我的解决方案基于本教程5 minutes with spring oauth 2.0。
我正在执行邮递员的这个请求:
/oauth/token?grant_type=client_credentials&client_id=mysupplycompany&client_secret=mycompanyk
该请求以异常响应:
org.springframework.security.authentication.InsufficientAuthenticationException: 没有客户端身份验证。尝试添加适当的 身份验证过滤器。 org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:91)
以下是一些您可能需要帮助我找出问题的文件/类。
WebInitializer
package com.squirrels.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;
public class WebInitializer implements WebApplicationInitializer
public void onStartup(ServletContext servletContext) throws ServletException
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
servletContext.addListener(new RequestContextListener());
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
servletContext.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, false, "/*");
private AnnotationConfigWebApplicationContext getContext()
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.squirrels.config");
return context;
PersistenceJPAConfig
package com.squirrels.config;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@ComponentScan( "com.squirrels.controller", "com.squirrels.services", "com.squirrels.persistence.dao", "com.squirrels.auth" )
@PropertySource(value = "classpath:squirrel.properties" )
@ImportResource("classpath:spring-security.xml")
public class PersistenceJPAConfig
@Autowired
private Environment environment;
@Bean
public SpringLiquibase liquibase()
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource());
liquibase.setDefaultSchema(environment.getRequiredProperty("db_schema"));
liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.xml");
return liquibase;
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPersistenceUnitName("SquirrelAuth");
em.setPackagesToScan(new String[] "com.squirrels.persistence.model" );
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
@Bean
public DataSource dataSource()
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("db_driverClass"));
dataSource.setUrl(environment.getRequiredProperty("db_jdbcUrl"));
dataSource.setUsername(environment.getRequiredProperty("db_user"));
dataSource.setPassword(environment.getRequiredProperty("db_password"));
return dataSource;
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation()
return new PersistenceExceptionTranslationPostProcessor();
Properties additionalProperties()
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", environment.getRequiredProperty("db_hibernateDialect"));
return properties;
security-spring.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/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.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="/api/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false" />
<sec:intercept-url pattern="/api/**" method="GET"
access="IS_AUTHENTICATED_FULLY" />
<sec:custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
<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>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider
user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="clientDetails" class="com.squirrels.auth.OAuthClientDetailsImpl">
<property name="id" value="mysupplycompany" />
<property name="secretKey" value="mycompanykey" />
</bean>
<sec:authentication-manager id="userAuthenticationManager">
<sec:authentication-provider ref="customUserAuthenticationProvider" />
</sec:authentication-manager>
<bean id="customUserAuthenticationProvider" class="com.squirrels.auth.OAuthAuthenticationProvider">
</bean>
<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.store.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="120"></property>
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<mvc:annotation-driven />
</beans>
OAuthAuthenticationProvider
package com.squirrels.auth;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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;
public class OAuthAuthenticationProvider implements AuthenticationProvider
@Autowired
private OAuthProxy proxy;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
boolean result = proxy.isValidUser("1", authentication.getPrincipal().toString(), authentication.getCredentials().toString());
if (result)
List<GrantedAuthority> grantedAuthorities =
new ArrayList<GrantedAuthority>();
OAuthAuthenticationToken auth = new OAuthAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), grantedAuthorities);
return auth;
else
throw new BadCredentialsException("Bad User Credentials.");
@Override
public boolean supports(Class<?> arg0)
return true;
WebMvcConfig
package com.squirrels.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
configurer.enable();
;
/*
* Configure ContentNegotiatingViewResolver
*/
@Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager)
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
// Define all possible view resolvers
List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
resolvers.add(jsonViewResolver());
resolver.setViewResolvers(resolvers);
return resolver;
/*
* Configure View resolver to provide JSON output using JACKSON library to
* convert object in JSON format.
*/
@Bean
public ViewResolver jsonViewResolver()
return new JsonViewResolver();
OAuthenticationToken
package com.squirrels.auth;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
public class OAuthAuthenticationToken extends AbstractAuthenticationToken
private static final long serialVersionUID = -1092219614309982278L;
private final Object principal;
private Object credentials;
public OAuthAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities)
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
@Override
public Object getCredentials()
return credentials;
@Override
public Object getPrincipal()
return principal;
OAuthClientDetailsImpl
package com.squirrels.auth;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
public class OAuthClientDetailsImpl implements ClientDetailsService
private String id;
private String secretKey;
@Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException
if (clientId.equals(id))
List<String> authorizedGrantTypes = new ArrayList<String>();
authorizedGrantTypes.add("password");
authorizedGrantTypes.add("refresh_token");
authorizedGrantTypes.add("client_credentials");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId(id);
clientDetails.setClientSecret(secretKey);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
return clientDetails;
else
throw new NoSuchClientException("No client recognized with id: " + clientId);
public String getId()
return id;
public void setId(String id)
this.id = id;
public String getSecretKey()
return secretKey;
public void setSecretKey(String secretKey)
this.secretKey = secretKey;
OAuthProxy
package com.squirrels.auth;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.squirrels.dto.UserDTO;
import com.squirrels.persistence.dao.UserDao;
import com.squirrels.persistence.model.UserModel;
import com.squirrels.services.UserUtil;
@Service
public class OAuthProxy
protected static final Logger logger = LogManager.getLogger(OAuthProxy.class);
@Autowired
private UserDao userDaoImpl;
@Autowired
private UserUtil userUtil;
@Transactional
public boolean isValidUser(String organizationId, String username, String password)
try
UserModel user = userDaoImpl.findByOrganizationAndUserName(organizationId, username);
UserDTO userDto = userUtil.getDTO(user);
//TODO validate password (or other means of auth)
return true;
catch(Exception e)
logger.error("No user: " + username + " found in organization: " + organizationId, e);
return false;
【问题讨论】:
不确定这是否真的是您的问题,但您发送到令牌端点的参数不应该是 URL 的一部分。它们应该是 POST 请求的一部分。 @LukeTaylor 我有同样的问题,但我正在发布请求。尚无解决方案,正在搜索... 【参考方案1】:我遇到了同样的问题。 在我的情况下是因为我像这样映射了我的 DispatcherServlet:
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/dispatcher/*</url-pattern>
</servlet-mapping>
这导致了问题,因为在 "/oauth/token"
而不是 "/dispatcher/oauth/token"
上配置了安全性:
<security:http pattern="/oauth/token" ...
所以我只需要这样做:
<security:http pattern="/dispatcher/oauth/token"
问题就解决了。
如果您不是这种情况,可能是因为您没有配置DelegatingFilterProxy
。
尝试在 web.xml 中配置它:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
如果你使用java config而不是web.xml,扩展类org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer
将对其进行配置。
【讨论】:
现在去试试这个其实很有意义。我将调度程序映射到 /api/*。我还将编辑我的问题并包含我的 JavaConfig 文件类。我所有的 spring 东西都是通过 java 配置的,除了 spring 安全的东西。 谢谢!这个答案和将我的调度程序从 /api/* 切换到 /* 一样有效。我终于运行了一个完全可操作的 oauth2 提供程序 很高兴为您提供帮助 :) 如果您想保留“/api/*”,也许this thread 可以帮助您。它说如果你覆盖默认的 oauth 端点 url,你可能需要配置另一个 DelegatingFilterProxy。以上是关于Spring OAuth2 - 无法获取客户端令牌的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot OAuth2 - 无法从令牌获取用户详细信息
Spring Boot OAuth2 - GoogleApi - 无法从令牌获取用户详细信息
尝试使用spring oauth2中的刷新令牌获取新的访问令牌时出现无效的客户端错误