代码之家  ›  专栏  ›  技术社区  ›  Jonathon

JavaSpring安全性:401未授权用于令牌OAuth2端点

  •  2
  • Jonathon  · 技术社区  · 7 年前

    我的Spring Boot项目有一个相当基本的设置。我试图设置OAuth2来保护我的API,但是我的API遇到了问题 /oauth/token 终点。发一个帖子或者请求我的 /oauth/令牌 终点结果如下(带有 401 Unauthorized 状态代码):

    {
        "timestamp": "2018-09-17T16:46:59.961+0000",
        "status": 401,
        "error": "Unauthorized",
        "message": "Unauthorized",
        "path": "/oauth/token"
    }
    

    这是我的授权服务器配置。

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
        @Autowired
        private TokenStore tokenStore;
    
        @Autowired
        private UserApprovalHandler userApprovalHandler;
    
        @Autowired
        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("client_id")
                    .secret("secret")
                    .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                    .scopes("read", "write")
                    .accessTokenValiditySeconds(600)
                    .refreshTokenValiditySeconds(3600);
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(this.tokenStore)
                    .userApprovalHandler(this.userApprovalHandler)
                    .authenticationManager(this.authenticationManager);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) {
            security.tokenKeyAccess("permitAll()")
                    .checkTokenAccess("isAuthenticated()")
                    .passwordEncoder(this.passwordEncoder);
        }
    }
    

    这是我的资源服务器配置。还没有什么重要的东西:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
        @Autowired
        private TokenStore tokenStore;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenStore(this.tokenStore);
        }
    }
    

    最后是我的标准web安全配置:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.oauth2.provider.ClientDetailsService;
    import org.springframework.security.oauth2.provider.approval.ApprovalStore;
    import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
    import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
    import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
    import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private ClientDetailsService clientDetailsService;
    
        @Autowired
        public void globalUserDetails(AuthenticationManagerBuilder builder) throws Exception {
            builder.inMemoryAuthentication()
                    .withUser("user").password("password").roles("ADMIN")
                    .and()
                    .withUser("admin").password("password").roles("USER");
        }
    
        @Override
        protected void configure(HttpSecurity security) throws Exception {
            security.csrf().disable()
                    .anonymous().disable()
                    .authorizeRequests()
                    .antMatchers("/oauth/token").permitAll();
        }
    
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Bean
        public TokenStore tokenStore() {
            return new InMemoryTokenStore();
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder(8);
        }
    
        @Bean
        @Autowired
        public UserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
            TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
            handler.setTokenStore(tokenStore);
            handler.setRequestFactory(new DefaultOAuth2RequestFactory(this.clientDetailsService));
            handler.setClientDetailsService(this.clientDetailsService);
    
            return handler;
        }
    
        @Bean
        @Autowired
        public ApprovalStore approvalStore(TokenStore tokenStore) {
            TokenApprovalStore store = new TokenApprovalStore();
            store.setTokenStore(tokenStore);
    
            return store;
        }
    }
    

    我已经用不同的匹配器模式弄得乱七八糟,看看我能不能让它工作,但我没有任何运气。我在根上下文和servlet路径中运行它 http://localhost:8080

    我可以确认,当springboot启动时,端点被映射到输出中,并且尝试到达稍微不同的端点会导致预期的404。

    2 回复  |  直到 7 年前
        1
  •  8
  •   Jonathon    7 年前

    结果发现我没有正确到达终点。我通过httppost发送了我所有的数据,包括客户端凭据。

    POST http://localhost:8080/oauth/token
    ...
    client_id=client_id&secret=secret&scope=read&grant_type=password&username=user&password=password
    

    我需要使用HTTP Basic Auth来发送客户端凭据,而不是发布它们:

    POST http://localhost:8080/oauth/token
    Authorization: Basic Y2xpZW50X2lkOnNlY3JldA==
    ...
    scope=read&grant_type=password&username=user&password=password
    
        2
  •  6
  •   kingGarfield    7 年前

    尝试更改密码编码器从您的 AuthorizationServerConfig 使用这个简单的编码器初始化(它不加密密码)。因为您不使用加密将客户端密码保存在内存存储中。

    private PasswordEncoder getPasswordEncoder() {
        return new PasswordEncoder() {
            public String encode (CharSequence charSequence) {
                return charSequence.toString();
            }
            public boolean matches(CharSequence charSequence, String s) {
                return true;
            }
        };
    }
    

    希望能成功。