Gostaria de configurar o SecurityFilterChain Bean usado com base em uma propriedade configurável.
Temos o mesmo aplicativo, que suporta diferentes tipos de segurança.
@Configuration
@EnableWebSecurity
class SecurityConfiguration {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()).csrf(AbstractHttpConfigurer::disable);
return httpSecurity.build();
}
@Bean
public SecurityFilterChain securityFilterChainX509(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)");
return httpSecurity.build();
}
@Bean
public SecurityFilterChain securityFilterChainJWT(HttpSecurity httpSecurity) throws Exception {
}
[...]
}
Estou colando apenas um exemplo aqui, mas imagine que este arquivo está cheio de beans SecurityFilterChain com muitos tipos de autenticação, vamos imaginar 50.
O caso de uso é que cada uma das instâncias do mesmo aplicativo tem sua própria configuração de segurança.
Por exemplo, o aplicativo implantado nos EUA precisa verificar o certificado do cliente, portanto, precisaria usar o bean securityFilterChainX509, enquanto o mesmo aplicativo implantado na França precisaria verificar o nome de usuário e a senha, enquanto o aplicativo implantado no Japão precisaria de outro tipo de autenticação, etc., etc., etc.
Até agora, estamos usando uma abordagem um tanto "estranha", que usamos com base em perfis .
Com isso quero dizer:
@Bean
@Profile({"canada", "brazil"})
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()).csrf(AbstractHttpConfigurer::disable);
return httpSecurity.build();
}
@Bean
@Profile({"usa", "egypt"})
public SecurityFilterChain securityFilterChainX509(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)");
return httpSecurity.build();
}
@Bean
@Profile({"germany"})
public SecurityFilterChain securityFilterChainJWT(HttpSecurity httpSecurity) throws Exception {
}
Embora essa abordagem esteja funcionando, ela tem desvantagens e exige que:
- manter muitas propriedades application-profile.properties, apenas para obter o bean correto.
- quando precisamos mudar (hoje o Brasil usa o inseguro, amanhã precisa do JWT) somos obrigados a mudar o código também.
(Realmente quero evitar perfis se possível)
Pergunta:
Existe uma maneira, sem perfil, mas com uma propriedade configurável, algo como
@Value("${the.security.bean.to.use}")
private String configuredSecurityBeanToUse;
Para configurar o bean SecurityFilterChain a ser usado?
Sim. Você pode inicializar o bean com base na propriedade que você define. Você precisa adicionar
@ConditionalOnProperty
uma anotação ao método do bean.@ConditionalOnProperty
verificará o valor da propriedade tendo valor como você definiu, somente aquele bean será inicializado. O atributo matchIfMissing é usado quando nenhuma propriedade é definida, então ele corresponderá à condição e inicializará aquele bean. Então ele deve ser usado uma vez para inicialização do mesmo tipo de bean.No arquivo application.properties você precisa definir a seguinte configuração para usar a segurança do certificado.
@ConditionalOnProperty Docs Exemplo com detalhes