Oauth2糟糕的凭据Spring Boot

时间:2014-10-29 17:01:24

标签: oauth spring-boot spring-security-oauth2

我收到错误:

“error”:“invalid_grant”, “error_description”:“凭据错误”

以下是我提出的要求:

POST /oauth/token HTTP/1.1
Host: localhost:8443
Authorization: Basic bW9iaWxlOg==
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded

username=admin&password=pass&client_id=mobile&grant_type=password&client_secret=

我的代码来自:https://github.com/juleswhite/mobilecloud-14/tree/master/examples/9-VideoServiceWithOauth2

这是代码: Application.java:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import com.capstone.auth.OAuth2SecurityConfiguration;
import com.google.common.io.BaseEncoding;

@Configuration
@EnableAutoConfiguration
@ComponentScan
@EnableWebMvc
@Import(OAuth2SecurityConfiguration.class)
public class Application extends RepositoryRestMvcConfiguration{

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);

    }

}

ClientAndUserDetailsS​​ervice.java

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
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.client.ClientDetailsUserDetailsService;

/**
 * A class that combines a UserDetailsService and ClientDetailsService
 * into a single object.
 * 
 * @author jules
 *
 */
public class ClientAndUserDetailsService implements UserDetailsService,
        ClientDetailsService {

    private final ClientDetailsService clients_;

    private final UserDetailsService users_;

    private final ClientDetailsUserDetailsService clientDetailsWrapper_;

    public ClientAndUserDetailsService(ClientDetailsService clients,
            UserDetailsService users) {
        super();
        clients_ = clients;
        users_ = users;
        clientDetailsWrapper_ = new ClientDetailsUserDetailsService(clients_);
    }

    @Override
    public ClientDetails loadClientByClientId(String clientId)
            throws ClientRegistrationException {
        return clients_.loadClientByClientId(clientId);
    }

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        UserDetails user = null;
        try{
            user = users_.loadUserByUsername(username);
        }catch(UsernameNotFoundException e){
            user = clientDetailsWrapper_.loadUserByUsername(username);
        }
        return user;
    }

}

OAuth2SecurityConfiguration.java

@Configuration
public class OAuth2SecurityConfiguration {
    // This first section of the configuration just makes sure that Spring
    // Security picks
    // up the UserDetailsService that we create below.
    @Configuration
    @EnableWebSecurity
    protected static class WebSecurityConfiguration extends
            WebSecurityConfigurerAdapter {

        @Autowired
        private UserDetailsService userDetailsService;

        @Autowired
        protected void registerAuthentication(
                final AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService);
        }
    }

    /**
     * This method is used to configure who is allowed to access which parts of
     * our resource server (i.e. the "/video" endpoint)
     */
    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends
            ResourceServerConfigurerAdapter {

        // This method configures the OAuth scopes required by clients to access
        // all of the paths in the video service.
        @Override
        public void configure(HttpSecurity http) throws Exception {

            http.csrf().disable();

            http.authorizeRequests().antMatchers("/oauth/token").anonymous();

            // If you were going to reuse this class in another
            // application, this is one of the key sections that you
            // would want to change

            // Require all GET requests to have client "read" scope
            http.authorizeRequests().antMatchers(HttpMethod.GET, "/**")
                    .access("#oauth2.hasScope('read')");

            // Require all other requests to have "write" scope
            http.authorizeRequests().antMatchers("/**")
                    .access("#oauth2.hasScope('write')");
        }

    }

    /**
     * This class is used to configure how our authorization server (the
     * "/oauth/token" endpoint) validates client credentials.
     */
    @Configuration
    @EnableAuthorizationServer
    @Order(Ordered.LOWEST_PRECEDENCE - 100)
    protected static class OAuth2Config extends
            AuthorizationServerConfigurerAdapter {

        // Delegate the processing of Authentication requests to the framework
        @Autowired
        private AuthenticationManager authenticationManager;

        // A data structure used to store both a ClientDetailsService and a
        // UserDetailsService
        private ClientAndUserDetailsService combinedService_;

        /**
         * 
         * This constructor is used to setup the clients and users that will be
         * able to login to the system. This is a VERY insecure setup that is
         * using hard-coded lists of clients / users / passwords and should
         * never be used for anything other than local testing on a machine that
         * is not accessible via the Internet. Even if you use this code for
         * testing, at the bare minimum, you should consider changing the
         * passwords listed below and updating the VideoSvcClientApiTest.
         * 
         * @param auth
         * @throws Exception
         */
        public OAuth2Config() throws Exception {

            // If you were going to reuse this class in another
            // application, this is one of the key sections that you
            // would want to change

            // Create a service that has the credentials for all our clients
            ClientDetailsService csvc = new InMemoryClientDetailsServiceBuilder()
                    // Create a client that has "read" and "write" access to the
                    // video service
                    .withClient("mobile")
                    .authorizedGrantTypes("password")
                    .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                    .scopes("read", "write")
                    .resourceIds("test")
                    .and()
                    // Create a second client that only has "read" access to the
                    // video service
                    .withClient("mobileReader")
                    .authorizedGrantTypes("password")
                    .authorities("ROLE_CLIENT").scopes("read")
                    .resourceIds("test").accessTokenValiditySeconds(3600)
                    .and().build();

            // Create a series of hard-coded users.
            UserDetailsService svc = new InMemoryUserDetailsManager(
                    Arrays.asList(
                            User.create("admin", "pass", "ADMIN", "USER"),
                            User.create("user0", "pass", "USER"),
                            User.create("username", "password", "USER")));

            // Since clients have to use BASIC authentication with the client's
            // id/secret,
            // when sending a request for a password grant, we make each client
            // a user
            // as well. When the BASIC authentication information is pulled from
            // the
            // request, this combined UserDetailsService will authenticate that
            // the
            // client is a valid "user".
            combinedService_ = new ClientAndUserDetailsService(csvc, svc);
        }

        /**
         * Return the list of trusted client information to anyone who asks for
         * it.
         */
        @Bean
        public ClientDetailsService clientDetailsService() throws Exception {
            return combinedService_;
        }

        /**
         * Return all of our user information to anyone in the framework who
         * requests it.
         */
        @Bean
        public UserDetailsService userDetailsService() {
            return combinedService_;
        }

        /**
         * This method tells our AuthorizationServerConfigurerAdapter to use the
         * delegated AuthenticationManager to process authentication requests.
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints)
                throws Exception {
            endpoints.authenticationManager(authenticationManager);
        }

        /**
         * This method tells the AuthorizationServerConfigurerAdapter to use our
         * self-defined client details service to authenticate clients with.
         */
        @Override
        public void configure(ClientDetailsServiceConfigurer clients)
                throws Exception {
            clients.withClientDetails(clientDetailsService());
        }

    }

    // This version uses the Tomcat web container and configures it to
    // support HTTPS. The code below performs the configuration of Tomcat
    // for HTTPS. Each web container has a different API for configuring
    // HTTPS.
    //
    // The app now requires that you pass the location of the keystore and
    // the password for your private key that you would like to setup HTTPS
    // with. In Eclipse, you can set these options by going to:
    // 1. Run->Run Configurations
    // 2. Under Java Applications, select your run configuration for this app
    // 3. Open the Arguments tab
    // 4. In VM Arguments, provide the following information to use the
    // default keystore provided with the sample code:
    //
    // -Dkeystore.file=src/main/resources/private/keystore
    // -Dkeystore.pass=changeit
    //
    // 5. Note, this keystore is highly insecure! If you want more securtiy, you
    // should obtain a real SSL certificate:
    //
    // http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html
    //
    @Bean
    EmbeddedServletContainerCustomizer containerCustomizer(
            @Value("${keystore.file:src/main/resources/private/keystore}") String keystoreFile,
            @Value("${keystore.pass:changeit}") final String keystorePass)
            throws Exception {

        // If you were going to reuse this class in another
        // application, this is one of the key sections that you
        // would want to change

        final String absoluteKeystoreFile = new File(keystoreFile)
                .getAbsolutePath();

        return new EmbeddedServletContainerCustomizer() {

            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
                tomcat.addConnectorCustomizers(new TomcatConnectorCustomizer() {
                    @Override
                    public void customize(Connector connector) {
                        connector.setPort(8443);
                        connector.setSecure(true);
                        connector.setScheme("https");

                        Http11NioProtocol proto = (Http11NioProtocol) connector
                                .getProtocolHandler();
                        proto.setSSLEnabled(true);
                        proto.setKeystoreFile(absoluteKeystoreFile);
                        proto.setKeystorePass(keystorePass);
                        proto.setKeystoreType("JKS");
                        proto.setKeyAlias("tomcat");
                    }
                });

            }
        };
    }

}

感谢您的关注和时间!

1 个答案:

答案 0 :(得分:3)

简短回答:如果您使用的是Spring Boot,则不能@Autowired AuthenticationManagerAuthorizationServerConfigurerAdapter

Long anwswer:this sample可以正常工作,因为它会自动装配AuthenticationManagerBuilder,并构造AuthenticationManager的惰性初始版本以供令牌使用者使用。使用Spring OAuth2 2.0.3,您必须自己创建惰性AuthenticationManager(例如this

    authenticationManager = new AuthenticationManager() {
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            return auth.getOrBuild().authenticate(authentication);
        }
    };

使用快照(或发布时为2.0.4),您可以在AuthorizationServerConfigurerAdapter中使用新的重载方法。