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

我正在将 Spring Security 与 Spring Boot 2.2.0 结合使用,尝试让 Azure AD B2C 正常工作,使用spring-security-oauth2-resource-server:5.2.0和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);

    }

}

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


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


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


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


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

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


慕的地6264312
浏览 171回答 3
3回答

慕勒3428872

我知道我参加聚会迟到了,但当前答案中缺少一些信息。非法状态异常:provided in the configuration did not match the requested issuer&nbsp;很可能是由于 @Dragana Le Mitova 已经在 issues-uri 属性末尾的斜线提到的那样引起的。这需要设置为:spring.security.oauth2.resourceserver.jwt.issuer-uri=https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/v2.0然后 Spring 将自动提取 OAuth 资源服务器的配置https://mycompb2ctestorg.b2clogin.com/mycompb2ctestorg.onmicrosoft.com/v2.0/.well-known/openid-configuration。为了确保我们只接受来自 Azure 应用程序的 JWT,我们需要检查 JWT 的受众 (aud) 属性。对于 Azure 应用程序,这通常与客户端/应用程序 ID 相同。正如 @Wim Deblauwe 已经回答的那样,这是通过JwtDecoder. Spring Security 甚至在他们的文档中为我们提供了一个自定义 JWT 验证器的示例,其中他们实现了受众声明检查。这是通过在JwtDecoderbean 中提供我们自己的验证器来完成的。受众(aud)声明验证器:public class AudienceValidator implements OAuth2TokenValidator<Jwt> {&nbsp; &nbsp; OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);&nbsp; &nbsp; public OAuth2TokenValidatorResult validate(Jwt jwt) {&nbsp; &nbsp; &nbsp; &nbsp; if (jwt.getAudience().contains("messaging")) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return OAuth2TokenValidatorResult.success();&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return OAuth2TokenValidatorResult.failure(error);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}Jwt解码器:@BeanJwtDecoder jwtDecoder() {&nbsp; &nbsp; NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport)&nbsp; &nbsp; &nbsp; &nbsp; JwtDecoders.withOidcIssuerLocation(issuerUri);&nbsp; &nbsp; OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator();&nbsp; &nbsp; OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);&nbsp; &nbsp; OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);&nbsp; &nbsp; jwtDecoder.setJwtValidator(withAudience);&nbsp; &nbsp; return jwtDecoder;}这与 @Wim Deblauwe 提供的答案略有不同,我们使用JwtValidators.createDefaultWithIssuer(issuerUri)而不是创建验证器JwtValidators.createDefault()。这很重要,因为我们本质上要检查 3 个 JWT 属性:到期日发行人观众我们JwtValidators.createDefault()只为过期属性创建一个验证器。我们为JwtValidators.createDefaultWithIssuer(issuerUri)过期和发行者属性创建验证器。JwtValidators.createDefaultWithIssuer(issuerUri)也是 Spring Security 设置 issues-uri 属性时的默认行为。如果您想更深入地了解,我们目前添加自定义验证器的方式是否是最佳方式正在进行讨论。

守着星空守着你

在阅读了Spring Security 文档中的自定义令牌验证器后,我添加了一个自定义验证器来检查受众声明,以确保令牌是为我自己的应用程序颁发的。为此,请创建此验证器类:private static class AudienceValidator implements OAuth2TokenValidator<Jwt> {&nbsp; &nbsp; @Override&nbsp; &nbsp; public OAuth2TokenValidatorResult validate(Jwt token) {&nbsp; &nbsp; &nbsp; &nbsp; if (token.getAudience().contains("my-application-id-here")) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return OAuth2TokenValidatorResult.success();&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return OAuth2TokenValidatorResult.failure(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new OAuth2Error("invalid_token", "The audience is not as expected, got " + token.getAudience(),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; null));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}并通过JwtDecoder在配置类中声明您自己的 bean来使用它WebSecurityConfigurerAdapter:@Beanpublic JwtDecoder jwtDecoder() {&nbsp; &nbsp; NimbusJwtDecoder result = NimbusJwtDecoder.withJwkSetUri(properties.getJwt().getJwkSetUri()).build();&nbsp; &nbsp; result.setJwtValidator(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new DelegatingOAuth2TokenValidator<Jwt>(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;JwtValidators.createDefault(),&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new AudienceValidator())&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; return result;}默认验证器将检查时间戳等内容。如果没问题,AudienceValidator 将检查受众声明。注意:您在验证器中传递的顺序DelegatingOAuth2TokenValidator定义了 JWT 令牌的检查顺序。在此示例中,在观众之前检查时间戳。如果你想让观众先检查,你需要把它放在构造函数的前面DelegatingOAuth2TokenValidator

动漫人物

我怀疑您在尝试声明自己的JwtDecoder光束时遇到的异常来自缺少的斜杠。请注意,“请求的发行者”始终缺少尾部斜杠,即使在配置中明确指定了尾部斜杠。按照规范的要求, Spring Security在附加之前会从颁发者 URI 中删除所有尾部斜杠。但是,在获取配置后,返回的配置的颁发者 URI 与问题 URI 的“清理”版本相匹配,而不是最初提供的版本。由于fromOidcIssuerLocation()不知道最初提供的颁发者 URL,因此它会与 cleanIssuer 进行匹配,从而导致所描述的问题。解决这个问题的最简单方法是在fromOidcIssuerLocation()中进行清理,然后仍然可以使用原始版本进行匹配。另一条注释与您提出的第一个问题相关。如果我知道您的 API 标识符 + 您的租户 ID,我可以使用客户端凭据获取您的 API 的访问令牌!令牌不会包含范围或角色,它不能。因此,检查是否存在有效的委派权限(也称为范围)或有效的应用程序权限(在角色声明中)至关重要。看看这个特定的代码。&nbsp;JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();&nbsp; &nbsp; &nbsp; &nbsp; TokenValidationParameters validationParameters = new TokenValidationParameters&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // We accept both the App Id URI and the AppId of this service application&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ValidAudiences = new[] { audience, clientId },&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Supports both the Azure AD V1 and V2 endpoint&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ValidIssuers = new[] { issuer, $"{issuer}/v2.0" },&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IssuerSigningKeys = signingKeys&nbsp; &nbsp; &nbsp; &nbsp; };
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java