Spring Boot OAuth2在1.4.1版上无法正常运行 [英] Spring Boot OAuth2 not working properly on version 1.4.1

查看:221
本文介绍了Spring Boot OAuth2在1.4.1版上无法正常运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Spring OAuth2上使用Spring Boot 1.4.0。当我请求令牌时,服务器响应是:

I was using Spring Boot 1.4.0 with Spring OAuth2. When I requested a token, the server response was:

  {
  "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,服务器响应变为

When I updated my project to Spring Boot 1.4.1, the server response became

  {
  "error": "invalid_client",
  "error_description": "Bad client credentials"
  }

从版本1.4更改了什么.0到1.4.1?我该怎么办才能让我的请求再次发挥作用?

What was changed from version 1.4.0 to 1.4.1 ? And what should I do to make my request work again?

编辑

WebSecurityConfiguration:

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

The 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 的类。当我请求令牌时,会调用以下方法:

There an class called ProviderManager. When I request a token, the method below is called:

    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上,em> parent 为null,然后,在下面方法的片段中,条件为false,并且该方法抛出 BadClientException

The difference between version 1.4.0 and 1.4.1 is that the attribute parent is null on version 1.4.1, and then, on the snippet of the method below the condition is false, and the method throws 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之后,dependecy

I've found where this error is coming from. After the update from Spring Boot 1.4.0 to 1.4.1, the dependecy

        <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。

changed from version 2.0.10 to 2.0.11. If I force the version to be 2.0.10 on Spring Boot 1.4.1, the token request works normally. So, it seems to be an issue of Spring Security OAuth2 and not from Spring Boot.

编辑4

我在 github 上提交了一个示例项目,在那里你可以看到将Spring Boot的版本从1.4.0更改为1.4.1时我面临的问题

I commited a sample project on github where you will be able to see what I'm facing when changing the version of spring boot from version 1.4.0 to 1.4.1

推荐答案

这真的是一个春天的安全问题。在github上有一个问题。

https ://github.com/spring-projects/spring-security-oauth/issues/896

It is really a spring oauth security problem. There is an issue open on github.
https://github.com/spring-projects/spring-security-oauth/issues/896

这篇关于Spring Boot OAuth2在1.4.1版上无法正常运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆