I currently use Spring Zuul with spring security to dynamically release url and routing mycoservices.
So instead of adding -
.antMatchers("/api/AgendarCotacaoWSPort/**").HasRole("NAO_CORRENTISTA ")
I added in my class the function that loads the role belonging to the url with .withObjectPostProcessor to call my class that searches the database for the url.
My DynamicSecurity Class implements the FilterInvocationSecurityMetadataSource
which gives me permission to add in the Collection<ConfigAttribute>
the relevant url to that URL.
I am currently studying Spring Cloud Gateway with Spring WebFlux and have not found a way to do the same thing instead of adding
.pathMatchers ("/login").HasRole("TEST")
for each service that I want to release effect the release so that it searches the database (Redis) and returns in the settings which is the role for that endpoint that it is trying to access.
Does anyone have any idea how I can do this in the new spring security model with webflux?
My Class SecurityConfig.java
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DynamicSecurity dynamicSecurity;
@Autowired
private UrlAccessDecisionManager accessDecisionManager;
@Autowired
private SessionInterceptorFilter sessionFilter;
@Bean
public JwtAuthenticationConfig jwtConfig() {
return new JwtAuthenticationConfig();
}
private static final String[] AUTH_WHITELIST = {
"**/v2/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/swagger-ui.html",
"/configuration/ui",
"/configuration/security",
"/webjars/**",
"/brasilsegapi/**",
"/health"
};
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/login")
.antMatchers("/loginoauthbb/**")
.antMatchers("/oauth/**")
.antMatchers("/logoff")
.antMatchers("/health")
.antMatchers("**/healthcheck")
.antMatchers("/alterarsenha")
.antMatchers("/esquecisenha")
.antMatchers("/criarusuario")
.antMatchers("/alteraremail")
.antMatchers("/resetsenha")
.antMatchers("/actuator")
.antMatchers("/actuator/**")
.antMatchers(AUTH_WHITELIST)
.antMatchers("/api/ConsultarInformacoesOuvidoria/**")
.antMatchers("/api/ValidacaoProtocoloSirWSPort/**")
.antMatchers("/api/AgendamentoCotacaoWSPort/**")
.antMatchers("/api/ConsultarDadosAgile/**")
.antMatchers("/api/EnviarDadosFormulario/**")
.antMatchers("/api/sensoriamento-mobile/login/**")
.antMatchers("/swagger-ui.html")
.antMatchers("/favicon.ico")
.antMatchers("/api/MobileInstitucionalWSPort/**")
.antMatchers("/api/DadosClienteWSPort/**")
;
}
protected void configure(final HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/").denyAll()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setSecurityMetadataSource(dynamicSecurity);
fsi.setAccessDecisionManager(accessDecisionManager);
return fsi;
}
})
.and()
.addFilterBefore(sessionFilter, UsernamePasswordAuthenticationFilter.class)
.logout()
.deleteCookies("auth_code", "JSESSIONID").invalidateHttpSession(true)
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
.and()
.exceptionHandling().authenticationEntryPoint(
(req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()));
}
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new CustomLogoutSuccessHandler();
}
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
}
My Class DynamicSecurity
import java.io.IOException;
import java.util.Collection;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
@Component
public class DynamicSecurity implements FilterInvocationSecurityMetadataSource {
@Autowired
RoleRepository roleRepository;
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
final HttpServletRequest request = ((FilterInvocation) object).getRequest();
final HttpServletResponse response = ((FilterInvocation) object).getResponse();
String urlWithoutContextPath = request.getRequestURI().substring(request.getContextPath().length());
Optional<Permissao> foundUrl = roleRepository.findByUri(urlWithoutContextPath);
if (foundUrl.isPresent()) {
System.out.println(
foundUrl.get().getRolesAllowed().stream().map(this::configAttribute).collect(Collectors.toList()));
return foundUrl.get().getRolesAllowed().stream().map(this::configAttribute).collect(Collectors.toList());
} else if (urlWithoutContextPath.equals("/login") || urlWithoutContextPath.equals("/alterarsenha")
|| urlWithoutContextPath.equals("/esquecisenha") || urlWithoutContextPath.equals("/health")) {
return null;
} else {
try {
response.sendError(403, "acesso negado");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return FilterInvocation.class.isAssignableFrom(clazz);
}
private ConfigAttribute configAttribute(Roles role) {
return new ConfigAttribute() {
private static final long serialVersionUID = -474661209383691172L;
@Override
public String getAttribute() {
return role.getAuthority();
}
};
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
}
Comment From: bclozel
Thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use the issue tracker only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add some more details if you feel this is a genuine bug.