Spring Security多令牌验证器

huangapple go评论62阅读模式
英文:

Spring security multiple token validators

问题

这是我的SecurityConfig到目前为止:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private String jwkIssuerUri = "https://login.microsoftonline.com/xxxxxxxxxxxxx/v2.0";

    @Autowired
    JwtTokenValidator jwtTokenValidator;

    private final CustomUserDetailsService userDetailsService;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.cors(Customizer.withDefaults()).csrf().disable()
                .authorizeRequests()
                .antMatchers("/auth/authenticate")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .oauth2ResourceServer(oauth -> oauth.jwt())
                .exceptionHandling((ex) ->
                        ex.authenticationEntryPoint(
                                new BearerTokenAuthenticationEntryPoint())
                                .accessDeniedHandler(new BearerTokenAccessDeniedHandler())).build();
    }

    @Bean
    public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(authProvider);
    }

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

    @Bean
    JwtEncoder jwtEncoder() {
        String key = "secret key";
        return new NimbusJwtEncoder(new ImmutableSecret<>(key.getBytes()));
    }

    @Bean
    JwtDecoder jwtDecoder() {
        if(******* TO DO IF AZURE AD*******) {
            NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(this.jwkIssuerUri);
            OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(this.jwkIssuerUri);
            OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, this.jwtTokenValidator);
            jwtDecoder.setJwtValidator(withAudience);
            return jwtDecoder;
        } else {
            String key = "secret key";
            byte[] bytes = key.getBytes();
            SecretKeySpec originalKey = new SecretKeySpec(bytes, 0, bytes.length,"RSA");
            return NimbusJwtDecoder.withSecretKey(originalKey).macAlgorithm(MacAlgorithm.HS512).build();
        }
    }
}

如你所见,在jwtDecoder中,我不确定如何根据需要验证的JWT获取不同的JwtDecoder

英文:

I have a web application in React and backend in spring boot (2.7.0). Web application has two options for login, username/password and Azure AD popup.
If username and password option is selected then authentication is done on my backend and JWT is generated and sent in response.
If Azure AD option is selected then pop-up in shown (using MSAL library) and after successful login the JWT is also generated using MSAL library and used to fetch resources from my backend.
On my backend I need to have an option to validate both JWT generated from that same backend and JWT generated from Azure AD.
What is the best possible way to achieve that?

Here is my SecurityConfig so far:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private String jwkIssuerUri = &quot;https://login.microsoftonline.com/xxxxxxxxxxxxx/v2.0&quot;;
@Autowired
JwtTokenValidator jwtTokenValidator;
private final CustomUserDetailsService userDetailsService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.cors(Customizer.withDefaults()).csrf().disable()
.authorizeRequests()
.antMatchers(&quot;/auth/authenticate&quot;)
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement(session -&gt; session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.oauth2ResourceServer(oauth -&gt; oauth.jwt())
.exceptionHandling((ex) -&gt;
ex.authenticationEntryPoint(
new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler())).build();
}
@Bean
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return new ProviderManager(authProvider);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
JwtEncoder jwtEncoder() {
String key = &quot;secret key&quot;;
return new NimbusJwtEncoder(new ImmutableSecret&lt;&gt;(key.getBytes()));
}
@Bean
JwtDecoder jwtDecoder() {
if(******* TO DO IF AZURE AD*******) {
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(this.jwkIssuerUri);
OAuth2TokenValidator&lt;Jwt&gt; withIssuer = JwtValidators.createDefaultWithIssuer(this.jwkIssuerUri);
OAuth2TokenValidator&lt;Jwt&gt; withAudience = new DelegatingOAuth2TokenValidator&lt;&gt;(withIssuer, this.jwtTokenValidator);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
} else {
String key = &quot;secret key&quot;
byte[] bytes = key.getBytes();
SecretKeySpec originalKey = new SecretKeySpec(bytes, 0, bytes.length,&quot;RSA&quot;);
return NimbusJwtDecoder.withSecretKey(originalKey).macAlgorithm(MacAlgorithm.HS512).build();
}
}
}

As you can see in jwtDecoder, I'm not sure how to get different JwtDecoder depending on the JWT that I need to validate.

答案1

得分: 1

你现在正在尝试实现的目标被称为多租户

目标不是为每个租户创建一个新的JwtDecoder,而是在运行时从给定的令牌中选择要使用的正确密钥,配置JwtDecoder。

这个选项将告诉您如何配置自己的jwtDecoder。您可能希望以以下方式自定义示例:

@Component
public class TenantJWSKeySelector
    ...
    @SneakyThrows
	private JWSKeySelector<SecurityContext> fromUri(String uri) {
		if(Objects.equals(uri, "Azure AD issuerUrl"){
             return JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(new URL("https://login.microsoftonline.com/common/discovery/v2.0/keys"));
        } else {
             return JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(new URL("Your internal JWKS uri"));
        }
	}
}

您可以轻松地用Spring配置替换这些硬编码的URI,因为TenantJWSKeySelector是一个组件。

获取jwks URL的标准方式是从开放ID配置中获取。例如,对于Azure AD,它是https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration,您可以从中推断出jwks_uri。您想要的是一个类似于这样的响应

我只熟悉JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL,但似乎JWSAlgorithmFamilyJWSKeySelector.fromJWKSource允许您以编程方式创建您的JWKS。这个答案可能对这部分有所帮助。

英文:

What your are trying to achieve is called multi tenancy

The goal is not to create a new JwtDecoder for each tenant, but instead configure the JwtDecoder to select the right key to use from the given token, at runtime.

This option will tell your how to configure your own jwtDecoder. You probably want to customize the example in this way

@Component
public class TenantJWSKeySelector
    ...
    @SneakyThrows
	private JWSKeySelector&lt;SecurityContext&gt; fromUri(String uri) {
		if(Objects.equals(uri, &quot;Azure AD issuerUrl&quot;){
             return JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(new URL(&quot;https://login.microsoftonline.com/common/discovery/v2.0/keys&quot;));
        } else {
             return JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(new URL(&quot;Your internal JWKS uri&quot;));
        }
	}
}

You can replace those hard coded URIs with spring configuration easily since TenantJWSKeySelector is a component.

The standard way of getting the jwks url is from the open-id configuration. For example, for azure AD it is https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration, which you can deduct the jwks_uri. What your want is a response that looks like this.

I am only familiar with JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL, but it seems that JWSAlgorithmFamilyJWSKeySelector.fromJWKSource let your create your JWKS programmatically. This anwser might help for this part

huangapple
  • 本文由 发表于 2023年5月30日 06:11:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76360576.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定