SpringBoot + Spring Security OAuth2 2.0 资源服务器 JWT 在启动时未调用授权服务器

问题描述 投票:0回答:1

总结:

使用 SpringBoot 2.7.7 测试 Spring Security OAuth2 资源服务器 JWT,它使用 Spring Security 5.7.6 在启动时查询授权服务器。

根据: https://docs.spring.io/spring-security/reference/5.7/servlet/oauth2/resource-server/jwt.html#_startup_expectations

启动期望

当使用此属性和这些依赖项时,资源服务器 将自动配置自身以验证 JWT 编码的承载 代币。

它通过确定性的启动过程实现这一点:

  • 查询 jwks_url 属性的提供者配置或授权服务器元数据端点
  • 查询 jwks_url 端点以获取支持的算法
  • 配置验证策略以查询jwks_url以获取找到的算法的有效公钥
  • 配置验证策略以验证针对 idp.example.com 的每个 JWT iss 声明。

此过程的结果是授权服务器必须是 启动并接收请求以便资源服务器成功 启动。

如果资源服务器查询时授权服务器宕机了 (给定适当的超时),然后启动将失败。

我在

application.properties
中定义了属性,并且在
pom.xml
中定义了依赖项,如上所述。

但是,我尝试这个的非常小的示例不起作用(例如,资源服务器在启动时似乎根本没有查询授权服务器,因此资源服务器启动成功。

我原以为我的非常小的应用程序会根据文档在启动时失败,但事实并非如此!甚至关闭了授权服务器,SpringBoot资源服务器应用程序仍然启动。

这就是我所做的:

1) pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>oauth2-resource-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>oauth2-resource-server</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.5</spring-cloud.version>
        <bootstrap.version>5.2.3</bootstrap.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-jose</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency-->
        <!--dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>${bootstrap.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2。应用程序.属性

server.port=8081
spring.thymeleaf.cache=false

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://openam.localtest.me:8080/openam/oauth2/realms/subrealm/
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://openam.localtest.me:8080/openam/oauth2/realms/subrealm/connect/jwk_uri

3.配置类别:

package org.example.oauth2resourceserver;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration {

    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    String jwkSetUri;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .antMatchers(HttpMethod.GET, "/**").hasAuthority("SCOPE_message:read")
                        .antMatchers(HttpMethod.POST, "/**").hasAuthority("SCOPE_message:write")
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return http.build();
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
    }
}

4。控制器

package org.example.oauth2resourceserver;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ExampleMVCController {

    @GetMapping("/")
    public String main(Model model) {
        return "welcome";
    }

    @GetMapping("/welcome")
    public String welcome(Model model) {
        return "welcome";
    }
}

5。应用

package org.example.oauth2resourceserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Oauth2ResourceServerApplication {

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

}

授权服务器关闭后,运行资源服务器时控制台上的输出为:

2022-12-28 03:32:38.148  INFO 1377958 --- [           main] o.e.o.Oauth2ResourceServerApplication    : Starting Oauth2ResourceServerApplication using Java 11.0.15 on xxxxx-Inspiron-15-7510 with PID 1377958 (/home/xxxxx/projects/oauth2-resource-server/target/classes started by jsalvo in /home/xxxxx/projects/oauth2-resource-server)
2022-12-28 03:32:38.150  INFO 1377958 --- [           main] o.e.o.Oauth2ResourceServerApplication    : No active profile set, falling back to 1 default profile: "default"
2022-12-28 03:32:38.561  INFO 1377958 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=12f14043-de4d-3b01-a8aa-ef038a41274e
2022-12-28 03:32:38.733  INFO 1377958 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8081 (http)
2022-12-28 03:32:38.739  INFO 1377958 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-12-28 03:32:38.739  INFO 1377958 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.70]
2022-12-28 03:32:38.819  INFO 1377958 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-12-28 03:32:38.820  INFO 1377958 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 634 ms
2022-12-28 03:32:38.931  INFO 1377958 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@748ac6f3, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@68f6e55d, org.springframework.security.web.context.SecurityContextPersistenceFilter@2bfaba70, org.springframework.security.web.header.HeaderWriterFilter@5584d9c6, org.springframework.security.web.csrf.CsrfFilter@3bf54172, org.springframework.security.web.authentication.logout.LogoutFilter@58af5076, org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter@650c405c, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@9301672, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@2577a95d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6fff46bf, org.springframework.security.web.session.SessionManagementFilter@17e9bc9e, org.springframework.security.web.access.ExceptionTranslationFilter@4da39ca9, org.springframework.security.web.access.intercept.AuthorizationFilter@2954f6ab]
2022-12-28 03:32:39.140  INFO 1377958 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ''
2022-12-28 03:32:39.152  INFO 1377958 --- [           main] o.e.o.Oauth2ResourceServerApplication    : Started Oauth2ResourceServerApplication in 1.27 seconds (JVM running for 1.512)
  • 我错过了什么?
  • Spring Security OAuth2 资源服务器中的哪些代码在启动时调用授权服务器?

更新2022年12月28日

根据评论的建议,我已经完全删除了上面的OAuth2ResourceServerSecurityConfiguration。还是没有运气。

查看 OAuth2ResourceServerJwtConfiguration.JwtDecoderConfiguration 的源代码:

看来您必须仅指定以下属性之一,而不能同时指定两者:

spring.security.oauth2.resourceserver.jwt.issuer-uri
spring.security.oauth2.resourceserver.jwt.jwk-set-uri

方法在:

JwtDecoderConfiguration.jwtDecoderByIssuerUri() 方法 有一个

@IssuerUriCondition
注释,它规定了必须仅定义
spring.security.oauth2.resourceserver.jwt.issuer-uri
属性的条件。

所以我注释掉了另一个属性(从

spring.security.oauth2.resourceserver.jwt.jwk-set-uri
注释掉了
application.properties
),但是当我通过 IntelliJ 调试到该方法时,它在下面的第 139 行处步进,但它不会在 lambda 内的第 141 行处步进/停止,这是实际调用授权服务器的代码的位置,即使我尝试从第 139 行单步执行 (F7):

总之我还是很茫然。大家有什么想法吗?

spring-boot spring-security
1个回答
0
投票

如果你看一下这里的Spring文档:再进一步,它特别提到了这一点;

如果资源服务器必须能够独立于 授权服务器,那么也可以提供 jwk-set-uri:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://idp.example.com
          jwk-set-uri: https://idp.example.com/.well-known/jwks.json

如文档所述;

因此,资源服务器将 NOT ping 授权服务器 启动时

这似乎是你通过配置所做的;

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://openam.localtest.me:8080/openam/oauth2/realms/subrealm/connect/jwk_uri

我想如果你删除该行☝🏾,那么你就会得到你正在寻找的默认行为 - 资源服务器依赖于授权服务器的可用性来启动

© www.soinside.com 2019 - 2024. All rights reserved.