使用 Spring Boot 1.5 在 oauth2 实现中返回错误凭据

Posted

技术标签:

【中文标题】使用 Spring Boot 1.5 在 oauth2 实现中返回错误凭据【英文标题】:Returning bad credential in oauth2 implemention using spring boot 1.5 【发布时间】:2018-04-07 08:26:45 【问题描述】:

当我尝试使用 Spring Boot 在 oauth2 实现中创建简单登录时。不幸的是它不起作用,因为我是春天的新手 我的配置

ApplicationStarter.java

@SpringBootApplication
@EnableAutoConfiguration

public class ApplicationStarter extends SpringBootServletInitializer 
     @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 
            return application.sources(ApplicationStarter.class);
        
    public static void main(String[] args) throws Exception 
        SpringApplication.run(ApplicationStarter.class, args);
    

ResourceServerConfiguration.java

@Configuration

@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter 

    private static final String RESOURCE_ID = "my_rest_api";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) 
        resources.resourceId(RESOURCE_ID);
    

    @Override
    public void configure(HttpSecurity http) throws Exception 
           System.out.println("Inside ResourceServerConfiguration");
        http.
        anonymous().disable()
        .requestMatchers().antMatchers("/user/**")
        .and().authorizeRequests()
        .antMatchers("/user/**").access("hasRole('ADMIN')")
        .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    


AuthorizationServerConfiguration.java

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter 

    private static String REALM="MY_OAUTH_REALM";

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
           System.out.println("Inside AuthorizationServerConfiguration");
        clients.inMemory()
            .withClient("my-trusted-client")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("read", "write", "trust")
            .secret("secret")
            .accessTokenValiditySeconds(120).//Access token is only valid for 2 minutes.
            refreshTokenValiditySeconds(600);//Refresh token is only valid for 10 minutes.
    

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
                .authenticationManager(authenticationManager);
    

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception 
        oauthServer.realm(REALM+"/client");
    


MethodSecurityConfig.java

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration 
    @Autowired
    private OAuth2SecurityConfiguration securityConfig;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() 
        return new OAuth2MethodSecurityExpressionHandler();
    

OAuth2SecurityConfiguration.java

@Configuration

@ComponentScan
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception 
        System.out.println("inside OAuth2SecurityConfiguration : globalUserDetails()");
        auth.inMemoryAuthentication()
        .withUser("bill").password("abc123").roles("ADMIN").and()
        .withUser("bob").password("abc123").roles("USER");
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        System.out.println("inside OAuth2SecurityConfiguration : configure()");
        http
        .csrf().disable()
        .anonymous().disable()
        .authorizeRequests()
        .antMatchers("/oauth/token").permitAll();
    

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception 
        System.out.println("inside OAuth2SecurityConfiguration : authenticationManagerBean()");
        return super.authenticationManagerBean();
    


    @Bean
    public TokenStore tokenStore() 
        System.out.println("inside OAuth2SecurityConfiguration : tokenStore()");
        return new InMemoryTokenStore();
    

    @Bean
    @Autowired
    public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore)
        System.out.println("inside OAuth2SecurityConfiguration : userApprovalHandler()");
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
        handler.setClientDetailsService(clientDetailsService);
        return handler;
    

    @Bean
    @Autowired
    public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception 
        System.out.println("inside OAuth2SecurityConfiguration : approvalStore()");
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore);
        return store;
    


请纠正我哪里出错了?是否需要更多配置?

我跟着 http://websystique.com/spring-security/secure-spring-rest-api-using-oauth2/ 作为参考。

春季开机日志

2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] o.s.b.w.f.OrderedRequestContextFilter :将请求上下文绑定到 线程:org.apache.catalina.connector.RequestFacade@1f2fcd1 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/css/'] 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:检查 请求匹配:'/oauth/token';反对'/css/' 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/js/'] 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:检查 请求匹配:'/oauth/token';反对 '/js/' 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/images/'] 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:检查 请求匹配:'/oauth/token';反对'/images/' 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/webjars/'] 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:检查 请求匹配:'/oauth/token';反对 '/webjars/' 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='//favicon.ico'] 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:检查 请求匹配:'/oauth/token';反对'//favicon.ico' 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/error'] 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:检查 请求匹配:'/oauth/token';针对“/错误” 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher:找不到匹配项 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/'] 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:请求 '/oauth/token' 匹配通用模式 '/' 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher:匹配 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.security.web.FilterChainProxy : /oauth/token?grant_type=password&username=bill&password=abc123 at 在附加过滤器链中的 11 中的第 1 位;发射过滤器: 'WebAsyncManagerIntegrationFilter' 2017-10-26 11:15:07.851 调试 21456 --- [nio-8080-exec-5] os.security.web.FilterChainProxy : /oauth/token?grant_type=password&username=bill&password=abc123 at 附加过滤器链中 11 个中的第 2 位;发射过滤器: 'SecurityContextPersistenceFilter' 2017-10-26 11:15:07.852 调试 21456 --- [nio-8080-exec-5] os.security.web.FilterChainProxy : /oauth/token?grant_type=password&username=bill&password=abc123 at 在附加过滤器链中的 11 中的第 3 位;发射过滤器: 'HeaderWriterFilter' 2017-10-26 11:15:07.852 调试 21456 --- [nio-8080-exec-5] os.security.web.FilterChainProxy : /oauth/token?grant_type=password&username=bill&password=abc123 at 在附加过滤器链中的 11 位中的第 4 位;发射过滤器: 'LogoutFilter' 2017-10-26 11:15:07.852 调试 21456 --- [nio-8080-exec-5] os.s.s.web.util.matcher.OrRequestMatcher:试图 匹配使用 Ant [pattern='/logout', GET] 2017-10-26 11:15:07.852 DEBUG 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher : 请求“POST /oauth/token”与“GET /logout 2017-10-26”不匹配 11:15:07.852 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/logout', POST] 2017-10-26 11:15:07.852 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:检查 请求匹配:'/oauth/token';反对“/注销” 2017-10-26 11:15:07.852 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/logout',PUT] 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:请求 “POST /oauth/token”与“PUT /logout 2017-10-26 11:15:07.869”不匹配 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [模式='/注销',删除] 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] os.s.w.u.matcher.AntPathRequestMatcher:请求 “POST /oauth/token”与“DELETE /logout 2017-10-26”不匹配 11:15:07.869 调试 21456 --- [nio-8080-exec-5] os.s.web.util.matcher.OrRequestMatcher:找不到匹配项 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] os.security.web.FilterChainProxy : /oauth/token?grant_type=password&username=bill&password=abc123 at 附加过滤器链中的 11 位中的第 5 位;发射过滤器: 'BasicAuthenticationFilter' 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] os.s.w.a.www.BasicAuthenticationFilter :基本 为用户“my-trusted-client”找到身份验证授权标头 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] o.s.s.authentication.ProviderManager :身份验证尝试 使用 org.springframework.security.authentication.dao.DaoAuthenticationProvider 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] o.s.s.a.dao.DaoAuthenticationProvider : 用户 'my-trusted-client' 未找到 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] o.s.s.w.a.www.BasicAuthenticationFilter :身份验证请求 失败的: org.springframework.security.authentication.BadCredentialsException: 凭证错误 2017-10-26 11:15:07.869 DEBUG 21456 --- [nio-8080-exec-5] os.s.w.header.writers.HstsHeaderWriter:不是 注入 HSTS 标头,因为它与 requestMatcher 不匹配 org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@1ff799 2017-10-26 11:15:07.869 调试 21456 --- [nio-8080-exec-5] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder 现在 清除,因为请求处理完成 2017-10-26 11:15:07.869 DEBUG 21456 --- [nio-8080-exec-5] o.s.b.w.f.OrderedRequestContextFilter : 清除线程绑定请求上下文: org.apache.catalina.connector.RequestFacade@1f2fcd1 2017-10-26 11:15:07.870 调试 21456 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : 带名称的 DispatcherServlet 'dispatcherServlet' 处理 [/auth/error] 的 POST 请求 2017-10-26 11:15:07.870 调试 21456 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping :查找处理程序方法 对于路径/错误 2017-10-26 11:15:07.870 DEBUG 21456 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping:返回 处理程序方法 [公共 org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)] 2017-10-26 11:15:07.870 调试 21456 --- [nio-8080-exec-5] o.s.b.f.s.DefaultListableBeanFactory :返回缓存的实例 单例 bean 'basicErrorController' 2017-10-26 11:15:07.874 DEBUG 21456 --- [nio-8080-exec-5] o.s.w.s.m.m.a.HttpEntityMethodProcessor : 书面 [timestamp=Thu Oct 26 11:15:07 IST 2017, status=401, 错误=未经授权,消息=错误凭据,路径=/auth/oauth/token] 作为“应用程序/json”使用 [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@32886a] 2017-10-26 11:15:07.874 调试 21456 --- [nio-8080-exec-5] os.web.servlet.DispatcherServlet :返回 Null ModelAndView 到名为“dispatcherServlet”的 DispatcherServlet:假设 HandlerAdapter 完成请求处理 2017-10-26 11:15:07.874 调试 21456 --- [nio-8080-exec-5] os.web.servlet.DispatcherServlet : 成功完成请求

gradle.build

> /*  * This build file was generated by the Gradle 'init' task.  *  *
> This generated file contains a sample Java Library project to get you
> started.  * For more details take a look at the Java Libraries chapter
> in the Gradle  * user guide available at
> https://docs.gradle.org/3.5/userguide/java_library_plugin.html  */
> buildscript 
>     ext  springBootVersion = '1.5.7.RELEASE' 
>     repositories  mavenCentral() 
>     dependencies  classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
>   // Apply the java-library plugin to add support for Java Library
> apply plugin: 'java' apply plugin: 'eclipse' apply plugin:
> 'org.springframework.boot' apply plugin: 'war'
> 
> 
> sourceCompatibility = 1.8 // In this section you declare where to find
> the dependencies of your project repositories 
>     // Use jcenter for resolving your dependencies.
>     // You can declare any Maven/Ivy/file repository here.    // jcenter()    mavenCentral() 
> 
> dependencies 
>     // This dependency is exported to consumers, that is to say found on their compile classpath.
>     //api 'org.apache.commons:commons-math3:3.6.1'
>     //providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat:1.5.2.RELEASE'
>     // This dependency is used internally, and not exposed to consumers on their own compile classpath.
>     implementation 'com.google.guava:guava:21.0'
> 
>     // Use JUnit test framework
>     testImplementation 'junit:junit:4.12'
>     
>    
>     // compile("org.springframework.boot:spring-boot-starter-security:1.4.1.RELEASE")
>     // compile("org.springframework.security.oauth:spring-security-oauth2:2.0.2.RELEASE")
> //  
> compile("org.springframework.security:spring-security-config:3.2.0.RELEASE")
>     //    compile("org.gitlab4j:gitlab4j-api:4.6.0")
>      //    compile("org.springframework.boot:spring-boot-starter-tomcat:1.5.2.RELEASE")
>          
>     compile('org.springframework.boot:spring-boot-starter-actuator')
>     compile('org.springframework.boot:spring-boot-starter-security')
>        compile('org.springframework.security.oauth:spring-security-oauth2')
>        compile('org.springframework.security:spring-security-config')
>     compile('org.springframework.boot:spring-boot-starter-web')
>     providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
>    
>     testCompile('org.springframework.boot:spring-boot-starter-test') 

【问题讨论】:

你的上下文路径是什么? server.context-path=/auth 您能按照我的回答再试一次吗?让我知道状态。 它不工作..还是一样 【参考方案1】:

像这样更改您的授权服务器配置

AuthorizationServerConfiguration.java

@Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
           System.out.println("Inside AuthorizationServerConfiguration");
        clients.inMemory()
            .withClient("my-trusted-client")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("read", "write", "trust").resourceIds("my_rest_api")
            .secret("secret")
            .accessTokenValiditySeconds(120).//Access token is only valid for 2 minutes.
            refreshTokenValiditySeconds(600);//Refresh token is only valid for 10 minutes.
    

这里的变化是我在作用域之后放置了一个 resourceIds,该资源 id 将与资源服务器的 RESOURCE_ID 相同。

你在 ResourceServerConfiguration 中这样声明

private static final String RESOURCE_ID = "my_rest_api";

所以我相信将字符串放入授权服务器可以解决您的问题。 谢谢。

【讨论】:

如果您正在查看本教程,那么您可能在资源服务器中缺少这一行 resources.resourceId(RESOURCE_ID).stateless(false); 但该功能不存在..而不是有任何替代方案吗? 我看到它还在@Override public void configure(ResourceServerSecurityConfigurer resources) resources.resourceId(RESOURCE_ID).stateless(false); ResourceServerSecurityConfigurer 不支持 .stateless(false); 如果是maven项目,请分享你的pom.xml

以上是关于使用 Spring Boot 1.5 在 oauth2 实现中返回错误凭据的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring Boot 1.5 在 oauth2 实现中返回错误凭据

带有查询 Spring-Boot jpa 1.5 的可选参数

Spring Boot 1.5 禁用 oauth2 安全性

Spring boot + oauth2:访问此资源需要完全身份验证

java 带有Spring Boot 1.5的JPA

从Spring Boot 1.5升级到2.0