[英]Spring Boot OAuth2 not working properly on version 1.4.1
我在Spring OAuth2中使用Spring Boot 1.4.0。 當我請求令牌時,服務器響應為:
{
"access_token": "93f8693a-22d2-4139-a4ea-d787f2630f04",
"token_type": "bearer",
"refresh_token": "2800ea24-bb4a-4a01-ba87-2d114c1a2235",
"expires_in": 899,
"scope": "read write"
}
當我將項目更新為Spring Boot 1.4.1時,服務器響應變為
{
"error": "invalid_client",
"error_description": "Bad client credentials"
}
從1.4.0版本更改為1.4.1版本有什么? 我應該怎么做才能使我的請求再次起作用?
編輯
WebSecurityConfiguration:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
/** The client details service. */
@Autowired
private ClientDetailsService clientDetailsService;
/** The password encoder. */
@Autowired
private PasswordEncoder passwordEncoder;
/** The custom authentication provider. */
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
/** The o auth 2 token store service. */
@Autowired
private OAuth2TokenStoreService oAuth2TokenStoreService;
/**
* User details service.
*
* @return the user details service
*/
@Bean
public UserDetailsService userDetailsService() {
UserDetailsService userDetailsService = new ClientDetailsUserDetailsService(clientDetailsService);
return userDetailsService;
}
/**
* Register authentication.
*
* @param auth the auth
*/
@Autowired
protected void registerAuthentication(final AuthenticationManagerBuilder auth) {
try {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder);
} catch (Exception e) {
LOGGER.error("Não foi possível registrar o AuthenticationManagerBuilder.", e);
}
}
/**
* Authentication manager bean.
*
* @return the authentication manager
* @throws Exception the exception
*/
@Override
@Bean(name = "authenticationManagerBean")
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* Authentication manager.
*
* @return the authentication manager
* @throws Exception the exception
*/
@Override
@Bean(name = "authenticationManager")
protected AuthenticationManager authenticationManager() throws Exception {
UserAuthenticationManager userAuthenticationManager = new UserAuthenticationManager();
userAuthenticationManager.setCustomAuthenticationProvider(customAuthenticationProvider);
return userAuthenticationManager;
}
/**
* User approval handler.
*
* @param tokenStore the token store
* @return the token store user approval handler
*/
@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(oAuth2TokenStoreService);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
super.configure(auth);
}
}
OAuth2Config
@Configuration
@EnableAuthorizationServer
@Order(LOWEST_PRECEDENCE - 100)
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
/** The token store. */
@Autowired
private TokenStore tokenStore;
/** The user approval handler. */
@Autowired
private UserApprovalHandler userApprovalHandler;
/** The authentication manager. */
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
/**
* Para ativar o Authorization Basic remova a seguinte linha: security allowFormAuthenticationForClients()
*
* @see http://stackoverflow.com/questions/26881296/spring-security-oauth2-full-authentication-is-required-to-access-this-resource
*
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter#configure(org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer)
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler).authenticationManager(authenticationManager);
}
資源服務器
/**
* The Class ResourceServer.
*/
@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
private static final String CLIENTE_AUTHENTICATED_READ = "#oauth2.clientHasRole('ROLE_TRUSTED_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')";
private static final String CLIENTE_AUTHENTICATED_WRITE = "#oauth2.clientHasRole('ROLE_TRUSTED_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('write')";
private static final String CONTADOR_AUTHENTICATED_READ = "#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isUser() and #oauth2.hasScope('read')";
private static final String CONTADOR_AUTHENTICATED_WRITE = "#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isUser() and #oauth2.hasScope('write')";
private static final String CONTADOR_OR_CLIENTE_AUTHENTICATED_READ = "(#oauth2.clientHasRole('ROLE_TRUSTED_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')) or (#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isUser() and #oauth2.hasScope('read'))";
private static final String CONTADOR_OR_CLIENTE_AUTHENTICATED_WRITE = "(#oauth2.clientHasRole('ROLE_TRUSTED_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('write')) or (#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isUser() and #oauth2.hasScope('write'))";
private static final String URL_CONTADOR = "/v1/files/^[\\d\\w]{24}$/contadores/self";
private static final String URL_CLIENTE = "/v1/files/^[\\d\\w]{24}$/contadores/[0-9]{1,}";
/** The client details service. */
@Autowired
private ClientDetailsService clientDetailsService;
/** The o auth 2 token store service. */
@Autowired
private OAuth2TokenStoreService oAuth2TokenStoreService;
/**
* http.authorizeRequests().antMatchers("/v1/emails").fullyAuthenticated();
* https://github.com/ShuttleService/shuttle/blob/7a0001cfbed4fbf851f1b27cf1b952b2a37c1bb8/src/main/java/com/real/apps/shuttle/security/SecurityConfig.java
*
* @see org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
*
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS).and().
authorizeRequests()
//===========================COMUNS (SEM AUTORIZAÇÃO) ===============//
.antMatchers(POST, "/oauth/token").anonymous()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
//===========================FILE CONTROLLER=========================//
//===========================CONTADOR================================//
.antMatchers(POST, "/v1/files/randomId/contadores/self").access(CONTADOR_AUTHENTICATED_WRITE)
.regexMatchers(PUT, URL_CONTADOR).access(CONTADOR_AUTHENTICATED_WRITE)
.regexMatchers(GET, URL_CONTADOR).access(CONTADOR_AUTHENTICATED_READ)
.regexMatchers(DELETE, URL_CONTADOR).access(CONTADOR_AUTHENTICATED_WRITE)
//===========================CLIENTE=================================//
.regexMatchers(POST, "/v1/files/randomId/contadores/[0-9]{1,}").access(CLIENTE_AUTHENTICATED_WRITE)
.regexMatchers(PUT, URL_CLIENTE).access(CLIENTE_AUTHENTICATED_WRITE)
.regexMatchers(GET, URL_CLIENTE).access(CLIENTE_AUTHENTICATED_READ)
.regexMatchers(DELETE, URL_CLIENTE).access(CLIENTE_AUTHENTICATED_WRITE)
//===========================METADATA CONTROLLER=====================//
.antMatchers(GET, "/v1/metadatas/").access(CONTADOR_OR_CLIENTE_AUTHENTICATED_READ)
.regexMatchers(GET, "/v1/metadatas/^[\\d\\w]{24}$").access(CONTADOR_OR_CLIENTE_AUTHENTICATED_READ)
.regexMatchers(GET, "/v1/metadatas/self/folders/^[\\d\\w]{24}$").access(CONTADOR_OR_CLIENTE_AUTHENTICATED_READ)
//===========================FOLDER CONTROLLER=======================//
.regexMatchers(PUT, "/v1/folders/^[\\d\\w]{24}$/contadores/self/lock").access(CONTADOR_AUTHENTICATED_WRITE)
.regexMatchers(PUT, "/v1/folders/^[\\d\\w]{24}$/contadores/self/unlock").access(CONTADOR_AUTHENTICATED_WRITE)
.regexMatchers(GET, "/v1/folders/^[\\d\\w]{24}$").access(CONTADOR_AUTHENTICATED_READ)
//===========================ESPAÇO CONTROLLER=======================//
.antMatchers(GET, "/v1/espacos/contadores/self").access(CONTADOR_AUTHENTICATED_READ)
//===========================OBRIGACAO CONTROLLER====================//
.antMatchers(GET, "/v1/obrigacoes").access(CONTADOR_AUTHENTICATED_READ)
.antMatchers(POST, "/v1/obrigacoes").access(CONTADOR_OR_CLIENTE_AUTHENTICATED_WRITE)
//===========================PROTOCOLO CONTROLLER===================//
.regexMatchers(GET, "/v1/protocolos/^[\\d\\w]{24}$").access(CONTADOR_AUTHENTICATED_READ)
.and().authorizeRequests().antMatchers("/v1/**").authenticated();
}
/* (non-Javadoc)
* @see org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter#configure(org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer)
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(customTokenServices());
resources.resourceId("arquivos-upload-api").stateless(false);
}
/**
* Custom token services.
*
* @return the resource server token services
*/
@Primary
@Bean(name = "defaultAuthorizationServerTokenServices")
public ResourceServerTokenServices customTokenServices() {
final CustomTokenServices defaultTokenServices = new CustomTokenServices();
defaultTokenServices.setTokenStore(oAuth2TokenStoreService);
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setReuseRefreshToken(false);
defaultTokenServices.setClientDetailsService(clientDetailsService);
return defaultTokenServices;
}
}
編輯2
有一個名為ProviderManager的類。 當我請求令牌時,以下方法稱為:
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
}
catch (InternalAuthenticationServiceException e) {
prepareException(e, authentication);
throw e;
}
catch (AuthenticationException e) {
lastException = e;
}
}
if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
eventPublisher.publishAuthenticationSuccess(result);
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
}
prepareException(lastException, authentication);
throw lastException;
}
1.4.0版和1.4.1版之間的區別在於,在1.4.1版上,屬性parent為null,然后,在以下方法的代碼段中,條件為false,並且該方法引發BadClientException
if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = e;
}
}
編輯3
我發現了此錯誤的來源。 從Spring Boot 1.4.0更新到1.4.1之后,依賴
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
從版本2.0.10更改為2.0.11。 如果我在Spring Boot 1.4.1上強制版本為2.0.10,則令牌請求正常工作。 因此,這似乎是Spring Security OAuth2的問題,而不是Spring Boot的問題。
編輯4
我在github上提交了一個示例項目,當將Spring Boot的版本從1.4.0更改為1.4.1時,您將能夠看到我所面臨的問題
這確實是一個春季的oauth安全問題。 github上有一個未解決的問題。
https://github.com/spring-projects/spring-security-oauth/issues/896
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.