繁体   English   中英

如何确保 Spring 安全保护资源服务器仅接受来自自己应用程序的 JWT 令牌

[英]How to ensure only JWT tokens from own application are accepted in Spring Security secured resource server

I am using Spring Security with Spring Boot 2.2.0, trying to get Azure AD B2C working, using spring-security-oauth2-resource-server:5.2.0 and spring-security-oauth2-jose:5.2.0 .

使用此配置:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/api/**")
            .authenticated()
            .and()
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
    }
}

在我的application.properties中设置了spring.security.oauth2.resourceserver.jwt.jwk-set-uri

我可以从 Azure AD B2C 获取令牌,并使用该令牌访问我自己的 API 端点。 但是,如果我使用另一个目录中的令牌,也可以访问端点。

我确实在委托人的声明中看到这来自另一个 azure 目录。 这是我需要在我的应用程序中手动添加的东西吗(测试应用程序 ID 是否与声明中的匹配)? 或者我应该添加一些我还没有完成的其他配置?

我还尝试使用JwtDecoders.fromOidcIssuerLocation("https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/v2.0/?p=B2C_1_ropc_flow");添加我自己的 JwtDecoder bean ,但这给出了:

java.lang.IllegalStateException: The Issuer "https://mycompb2ctestorg.b2clogin.com/60780907-bc3a-469a-82d1-b89ffed655af/v2.0/" 
provided in the configuration did not match the requested issuer 
"https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/v2.0/?p=B2C_1_ropc_flow"

此外,使用:

spring.security.oauth2.resourceserver.jwt.issuer-uri=https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/v2.0/?p=B2C_1_ropc_flow

给出与尝试声明我自己的JwtDecoder bean 相同的异常。

在阅读了 Spring 安全文档中的自定义令牌验证器之后,我添加了一个自定义验证器,用于检查受众声明以确保为我自己的应用程序颁发了令牌。 为此,请创建此验证器 class:

private static class AudienceValidator implements OAuth2TokenValidator<Jwt> {

    @Override
    public OAuth2TokenValidatorResult validate(Jwt token) {
        if (token.getAudience().contains("my-application-id-here")) {
            return OAuth2TokenValidatorResult.success();
        } else {
            return OAuth2TokenValidatorResult.failure(
                    new OAuth2Error("invalid_token", "The audience is not as expected, got " + token.getAudience(),
                                    null));
        }
    }
}

并通过在WebSecurityConfigurerAdapter配置 class 中声明自己的JwtDecoder bean 来使用它:

@Bean
public JwtDecoder jwtDecoder() {
    NimbusJwtDecoder result = NimbusJwtDecoder.withJwkSetUri(properties.getJwt().getJwkSetUri()).build();
    result.setJwtValidator(
            new DelegatingOAuth2TokenValidator<Jwt>(
                   JwtValidators.createDefault(), 
                   new AudienceValidator()) 
            );
    return result;
}

默认验证器将检查时间戳等内容。 如果没问题,AudienceValidator 将检查观众声明。

注意:您在DelegatingOAuth2TokenValidator中传入验证器的顺序定义了检查 JWT 令牌的顺序。 在此处的示例中,在观众之前检查时间戳。 如果你想让观众先检查,你需要把它放在DelegatingOAuth2TokenValidator的构造函数中

我知道我迟到了,但当前答案中缺少一些信息。

非法状态异常:

provided in the configuration did not match the requested issuer 

很可能是因为@Dragana Le Mitova 在 issue-uri 属性末尾的斜线已经提到过。 这需要设置为:

spring.security.oauth2.resourceserver.jwt.issuer-uri=https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/v2.0

Spring will then pull the configuration for the OAuth Resource Server from https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/v2.0/.well-known/openid-configuration automatically.


为确保我们只接受来自 Azure 应用程序的 JWT,我们需要检查 JWT 的受众 (aud) 属性。 对于 Azure 应用程序,这通常与客户端/应用程序 ID 相同。 正如@Wim Deblauwe 已经回答的那样,这是通过JwtDecoder的自定义验证器完成的。 Spring Security 甚至在他们的文档中为我们提供了一个自定义 JWT 验证器的示例,他们在其中实施了受众声明检查。 这是通过在JwtDecoder bean 中提供我们自己的验证器来完成的。

受众 (aud) 声明验证器:

public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
    OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);

    public OAuth2TokenValidatorResult validate(Jwt jwt) {
        if (jwt.getAudience().contains("messaging")) {
            return OAuth2TokenValidatorResult.success();
        } else {
            return OAuth2TokenValidatorResult.failure(error);
        }
    }
}

Jwt解码器:

@Bean
JwtDecoder jwtDecoder() {
    NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport)
        JwtDecoders.withOidcIssuerLocation(issuerUri);

    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator();
    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

    jwtDecoder.setJwtValidator(withAudience);

    return jwtDecoder;
}

这与@Wim Deblauwe 提供的答案略有不同,我们使用JwtValidators.createDefaultWithIssuer(issuerUri)而不是JwtValidators.createDefault()创建验证器。 这很重要,因为我们本质上要检查 3 个 JWT 属性:

  • 到期
  • 发行人
  • 观众

使用JwtValidators.createDefault()我们只为过期属性创建一个验证器。 使用JwtValidators.createDefaultWithIssuer(issuerUri)我们为过期和颁发者属性创建验证器。 JwtValidators.createDefaultWithIssuer(issuerUri)也是 Spring Security 在设置 issue-uri 属性时的默认行为。

如果您想更深入地了解 go,我们目前必须添加自定义验证器的方式是否最佳,目前 正在进行讨论

我怀疑您在尝试声明自己的JwtDecoder束时遇到的异常来自缺少的斜线。

请注意,“请求的颁发者”总是缺少尾部斜杠,即使在配置中明确指定了尾部斜杠。

Spring 按照规范的要求,安全性会在附加之前从颁发者 URI 中删除任何尾部斜杠。 但是,在获取配置后,返回的配置的颁发者 URI 与问题 URI 的“清理”版本匹配,而不是最初提供的版本。

因为fromOidcIssuerLocation()不知道最初提供的颁发者 URL,所以它与 cleanIssuer 匹配,这会导致所描述的问题。 解决这个问题的最简单方法是在fromOidcIssuerLocation()中进行清理,然后仍然可以匹配原始版本。

另一个与您提出的第一个问题有关的注释。

如果我知道您的 API 标识符 + 您的租户 ID,我可以使用客户端凭据为您的 API 获取访问令牌,该令牌将不包含范围或角色。 这不可以。 因此,检查是否存在有效的委派权限(也称为范围)或有效的应用程序权限(在角色声明中)至关重要。

看看这个特定的代码。

 JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

        TokenValidationParameters validationParameters = new TokenValidationParameters
        {
            // We accept both the App Id URI and the AppId of this service application
            ValidAudiences = new[] { audience, clientId },

            // Supports both the Azure AD V1 and V2 endpoint
            ValidIssuers = new[] { issuer, $"{issuer}/v2.0" },
            IssuerSigningKeys = signingKeys
        };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM