首页 > 解决方案 > 访问此资源需要 Spring OAuth2 完整身份验证

问题描述

在我们开始之前,我已经查看了许多关于这个主题的帖子,但似乎没有一个帖子有任何帮助。

我正在尝试将我的 Spring Rest API 配置为使用 OAuth 密码授予身份验证。

这是我当前的安全配置。目前我对哪些端点是 permitAll() 或经过身份验证没有限制,但我之前已经设置它允许未经身份验证的访问 /oauth/** 但仍然有同样的问题。阅读文档后,这似乎是一件坏事,因为 /oauth/token 端点应该受到 http 基本身份验证的保护,其中用户名/密码是客户端 ID 和客户端密码。我还尝试将其设置为具有 anyRequest().authenticated() 并遇到与我相同的问题。

@Configuration
@EnableWebSecurity
@ComponentScan({ "com.mergg.webapp.security", "com.mergg.common.web" })
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private IUserService userService;

    @Override
    public void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }
}

这是我当前的 AuthorizationServerConfig 设置。请注意,我试图创建一个内存客户端,其中的秘密是 passwordEncoder.encode("secret")。发生了同样的问题。不确定使用哪种最佳实践,但这是另一个话题。

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    public AuthorizationServerConfiguration() {
        super();
    }

    @Bean
    public TokenStore tokenStore() {
//      return new JdbcTokenStore(dataSource);
        return new InMemoryTokenStore();
    }

    // config

    @Override
    public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer.passwordEncoder(this.passwordEncoder)
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
//      clients.jdbc(dataSource)
//              .passwordEncoder(passwordEncoder)
//              .withClient("mergg_mobile")
//              .secret(passwordEncoder.encode("secret"))
//              .authorizedGrantTypes("password");
        clients.inMemory()
                .withClient("test")
                .secret("secret")
                .authorizedGrantTypes("password", "refresh_token")
                .accessTokenValiditySeconds(3600);

    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }
}

这是我的资源服务器配置。我使用 http 安全元素的方式与我在安全配置中使用的方式相同。我还玩弄了无状态与需要时的会话创建策略。没运气。

@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //@formatter:off
            http
            .authorizeRequests()
            .antMatchers("/roles/**").hasRole("INTERNAL")
            .antMatchers("/priveleges/**").hasRole("INTERNAL")
            .anyRequest().authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //@formatter:on

    }
}

以下是我提出的一些请求示例(使用 curl 和 postman):

curl -u test:secret -X POST localhost:5000/oauth/token\?grant_type=password\&username=test\&password=password

curl -u test:password -X POST localhost:5000/oauth/token\?grant_type=password\&username=test\&password=password

curl -X POST -vu test:secret http://localhost:5000/oauth/token -H "Accept: application/json" -d "password=password&username=test&grant_type=password&client_secret=secret&client_id=test"

curl -X POST -vu test:password http://localhost:5000/oauth/token -H "Accept: application/json" -d "password=password&username=test&grant_type=password&client_secret=secret&client_id=test"

请注意,oauth 客户端 ID 是test,其秘密是secret. 一位用户test有密码password

这是我尝试请求令牌时的控制台输出:

23:07:36.571 [http-nio-5000-exec-2] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
23:07:36.625 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
23:07:36.627 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
23:07:36.627 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
23:07:36.629 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
23:07:36.629 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', GET]
23:07:36.630 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /oauth/token' doesn't match 'GET /logout'
23:07:36.630 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', POST]
23:07:36.630 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/oauth/token'; against '/logout'
23:07:36.630 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', PUT]
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /oauth/token' doesn't match 'PUT /logout'
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', DELETE]
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /oauth/token' doesn't match 'DELETE /logout'
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - No matches found
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in headers. Trying request parameters.
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in request parameters.  Not an OAuth2 request.
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - No token in request, will continue chain.
23:07:36.631 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
23:07:36.633 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
23:07:36.635 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
23:07:36.637 [http-nio-5000-exec-2] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@ad1846c9: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
23:07:36.637 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
23:07:36.637 [http-nio-5000-exec-2] DEBUG o.s.s.w.s.SessionManagementFilter - Requested session ID 61EE2368B212EC609873DFB621D5166A is invalid.
23:07:36.637 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
23:07:36.637 [http-nio-5000-exec-2] DEBUG o.s.security.web.FilterChainProxy - /oauth/token?password=password&username=test&grant_type=token at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
23:07:36.638 [http-nio-5000-exec-2] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /oauth/token?password=password&username=test&grant_type=token; Attributes: [#oauth2.throwOnError(authenticated)]
23:07:36.638 [http-nio-5000-exec-2] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@ad1846c9: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
23:07:36.645 [http-nio-5000-exec-2] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@73ac552e, returned: -1
23:07:36.652 [http-nio-5000-exec-2] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.mergg.webapp.security.SimpleCorsFilter.doFilter(SimpleCorsFilter.java:40)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:844)
23:07:36.660 [http-nio-5000-exec-2] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Calling Authentication entry point.
23:07:36.712 [http-nio-5000-exec-2] DEBUG o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5bf2a0a6
23:07:36.716 [http-nio-5000-exec-2] DEBUG o.s.s.o.p.e.DefaultOAuth2ExceptionRenderer - Written [error="unauthorized", error_description="Full authentication is required to access this resource"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@3b1af7db]
23:07:36.716 [http-nio-5000-exec-2] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed

这是我在邮递员中得到的回复:

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}

标签: springspring-bootspring-securityoauthoauth-2.0

解决方案


推荐阅读