【问题标题】:Spring-boot application not authenticating when using OAuth2 (updated #2)使用 OAuth2 时 Spring-boot 应用程序未进行身份验证(更新 #2)
【发布时间】:2020-05-29 18:13:54
【问题描述】:

我正在尝试创建一个 OAuth2 客户端来访问第三方 OAuth2 服务器。我已将此行添加到我的 application.properties 文件中:

spring.security.oauth2.client.registration.mercadolivre=mercadolivre
spring.security.oauth2.client.registration.mercadolivre.client-id=...
spring.security.oauth2.client.registration.mercadolivre.client-secret=...
spring.security.oauth2.client.registration.mercadolivre.client-authentication-method=basic
spring.security.oauth2.client.registration.mercadolivre.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.mercadolivre.redirect-uri=http://localhost:8080
spring.security.oauth2.client.provider.mercadolivre.authorization-uri=https://auth.mercadolivre.com.br/authorization
spring.security.oauth2.client.provider.mercadolivre.token-uri=https://auth.mercadolivre.com.br/oauth

我有这个 Spring-security 配置:

@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
public class Security extends WebSecurityConfigurerAdapter {
  @Override
  public void configure(HttpSecurity http) throws Exception {
      http
        .csrf().disable()
        .authorizeRequests()
          .antMatchers("/css/**", "/images/**", "/js/**", "/**").permitAll()
          .anyRequest().authenticated()
        .and()
          .oauth2Login()
        .and()
        .logout()
          .logoutUrl("/logout");
  }
...
}

这个 html 用于我的开放和安全区域:

  <body>
    <ul>
      <!--<li class="navbar-brand">
        <img th:src="@{/img/logo.png}" class="sticky" width="120px" height="120px" alt="logo">
      </li> -->

      <li>
        Loja
      </li>

      <li class="right" sec:authorize="isAnonymous()">
        <a class="button" th:href="@{/oauth2/authorization/mercadolivre}">Entrar</button>
      </li>

      <li sec:authorize="isAuthenticated()">
        Logged in as: <span sec:authentication="name">Bob</span>
      </li>

      <li class="right" sec:authorize="isAuthenticated()">
        <a class="button" th:href="@{/logout}">Sair</button>
      </li>
    </ul>
  </body>

但是当我在浏览器上打开应用程序并尝试登录时,我会正确转发到授权页面,但是当我重定向回我的应用程序时,我没有通过身份验证。

任何人都可以看到这里出了什么问题?

更新

在我将 spring-security 配置更改为此之后

@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
public class Security extends WebSecurityConfigurerAdapter {
  @Override
  public void configure(HttpSecurity http) throws Exception {
      http
        .csrf().disable()
        .authorizeRequests()
          .antMatchers("/css/**", "/images/**", "/js/**", "/**").permitAll()
          .anyRequest().authenticated()
        .and()
        .formLogin()
          .loginPage("/login")
          .loginProcessingUrl("/doLogin").permitAll()
        .and()
        .oauth2Login()
          .loginPage("/login")
          .loginProcessingUrl("/login/oauth")
        .and()
        .logout()
          .logoutUrl("/logout");
  }
...
}

application.propertiesredirect_uri 到这个:

spring.security.oauth2.client.registration.mercadolivre.redirect-uri=http://localhost:8080/login/oauth

现在,当我尝试登录时出现此错误:

java.lang.NullPointerException: null
        at org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient.getTokenResponse(DefaultAuthorizationCodeTokenResponseClient.java:84) ~[spring-security-oauth2-client-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient.getTokenResponse(DefaultAuthorizationCodeTokenResponseClient.java:52) ~[spring-security-oauth2-client-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.authenticate(OAuth2LoginAuthenticationProvider.java:103) ~[spring-security-oauth2-client-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175) ~[spring-security-core-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:185) ~[spring-security-oauth2-client-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:160) ~[spring-security-oauth2-client-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.2.1.RELEASE.jar!/:5.2.1.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.3.RELEASE.jar!/:5.2.3.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1598) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.30.jar!/:9.0.30]
        at java.base/java.lang.Thread.run(Thread.java:830) ~[na:na]

(重定向网址看起来不错,包括附加的必要代码:http://localhost:8080/login/oauth?code=...&amp;state=...

更新 2

我当前的设置如下。有了这个,我被转发到服务授权页面,但是当我返回我的项目时,我没有通过身份验证(获取访问令牌的授权代码成功附加到 url)。

App.java

@EnableOAuth2Client
@EnableOAuth2Sso
@SpringBootApplication
@RestController
public class App extends WebSecurityConfigurerAdapter {

    @GetMapping("/user")
    public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
            return Collections.singletonMap("name", principal.getAttribute("name"));
    }

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests(a -> a
                            .antMatchers("/", "/error", "/webjars/**", "/css/**", "/js/**", "/img/**").permitAll()
                            .anyRequest().authenticated()
                    )
                    .exceptionHandling(e -> e
                            .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
                    )
                    .csrf(c -> c
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            )
                    .logout(l -> l
                        .logoutSuccessUrl("/").permitAll()
                    )
                    .oauth2Login();
    }

}

application.properties

security.basic.enabled=false

spring.security.oauth2.client.registration.mercadolivre.client-id=...
spring.security.oauth2.client.registration.mercadolivre.client-secret=...

spring.security.oauth2.client.registration.mercadolivre.client-authentication-method=basic
spring.security.oauth2.client.registration.mercadolivre.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.mercadolivre.scope=read
spring.security.oauth2.client.registration.mercadolivre.redirect-uri=http://localhost:8080

spring.security.oauth2.client.provider.mercadolivre.authorization-uri=https://auth.mercadolibre.com/authorization
spring.security.oauth2.client.provider.mercadolivre.token-uri=https://api.mercadolivre.com.br/oauth/token
spring.security.oauth2.client.provider.mercadolivre.user-info-uri=https://api.mercadolibre.com/users/me
spring.security.oauth2.client.provider.mercadolivre.user-info-authentication-method=form

spring.security.oauth2.client.clientId=...
spring.security.oauth2.client.clientSecret=...
spring.security.oauth2.client.accessTokenUri=https://api.mercadolivre.com.br/oauth/token
spring.security.oauth2.client.userAuthorizationUri=https://auth.mercadolibre.com/authorization
spring.security.oauth2.client.clientAuthenticationScheme=form

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title></title>
    <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}"/>
</head>
<body>
  <nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-dark">
    <a class="navbar-brand" href="#">
      <img th:src="@{/img/bootstrap-solid.svg}" width="30" height="30" class="d-inline-block align-top" alt="kleber app store">
      Kleber App Store
    </a>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav mr-auto">
      </ul>
      <ul class="navbar-nav">
        <li class="nav-item" sec:authorize="isAnonymous()">
          <a class="nav-link" href="/oauth2/authorization/mercadolivre">
            Fazer login com Mercado Livre
          </a>
        </li>
        <li class="nav-item" sec:authorize="isAuthenticated()">
          <a class="nav-link" href="#">
            Logged in as: <span sec:authentication="name">Bob</span>
          </a>
        </li>
        <li class="nav-item" sec:authorize="isAuthenticated()">
          <a class="btn btn-primary" th:href="@{/logout}">
            Logout
          </a>
        </li>
      </ul>
    </div>
  </nav>

  <script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
  <script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
  <script type="text/javascript" th:src="@{/webjars/js-cookie/js.cookie.js}"></script>
  <script type="text/javascript" th:src="@{/js/script.js}"></script>
</body>
</html>

【问题讨论】:

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


    【解决方案1】:

    我认为 OAuth2 客户端的重定向 Uri 应该设置为:

    spring.security.oauth2.client.registration.mercadolivre.redirect-uri=http://localhost:8080/login/oauth2/mercadolivre
    

    并且这个相同的 URI 应该作为重定向 URL 输入到您的第三方 OAuth2 授权服务器中,因为这是授权服务器应该发送 Spring 安全的授权代码以最终获取令牌的地方。

    佩罗

    【讨论】:

    • 好的,我按照您的建议修改了我的 spring-security 配置和 application.properties 文件,现在我收到了 NullPointerException(我用详细信息更新了问题)。什么都没有改变。你能看到我现在缺少什么吗?
    • 这个我试过了,也不行。如果我只是将 application.properties redirect-uri 属性更改为http://localhost:8080/login/oauth2/mercadolivre,则会出现 404 错误,如果我将 spring-security 配置类中的 oauth2login().loginProcessingUrl(...) 更改为该属性,则会出现 NullPointerException 错误。
    • 我也尝试完全删除oauth2login.loginProcessingUrl(...),但也不起作用(出现 NullPointerException 错误)。
    • 如果您在没有任何登录参数的情况下尝试第一个 WebSecurityConfigurerAdapter 会发生什么
    猜你喜欢
    • 1970-01-01
    • 2017-10-01
    • 2022-01-05
    • 1970-01-01
    • 2018-05-14
    • 2011-06-27
    • 1970-01-01
    • 2018-08-29
    • 2023-03-19
    相关资源
    最近更新 更多