What is implication of adding #Component to custom Spring Security filter - spring

I have a custom Spring Security filter extending GenericFilterBean.
To do automatic dependency and bean creation I added a #Component annotation.
In my Security config I also register the filter like:
#Autowired
private RestAuthenticationFilter restAuthenticationFilter;
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.addFilterBefore(restAuthenticationFilter, LogoutFilter.class)
Everything works well except that my filter is called twice...
It seems Spring adds filters also automatically to standard filters.
What should be the best approach here?
UPDATE
#Dave is this what you mean? It seems to work.
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application extends WebMvcConfigurerAdapter {
#Autowired
private RestAuthenticationFilter restAuthenticationFilter;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
#Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setEnabled(false);
filterRegistrationBean.setFilter(restAuthenticationFilter);
return filterRegistrationBean;
}
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationFilter restAuthenticationFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.addFilterBefore(restAuthenticationFilter, LogoutFilter.class)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// #formatter:on
}
}
}

You need to explicitly register the filter and mark it as "enabled=false" using the FilterRegistrationBean API. Then Spring Security will use it in its chain, but Boot will not try and register it as well.

Related

Spring boot security add custom filter in which chain?

I create a custom filter and use addFilterBefore add the filter in configure method.
#Component
public class JsonWebTokenFilter extends OncePerRequestFilter { ... }
configure method in SecurityConfig which extends WebSecurityConfigurerAdapter
#Autowired
private JsonWebTokenFilter jsonWebTokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/api/**")
.addFilterBefore(jsonWebTokenFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated();
}
Above setting add the jsonWebTokenFilter in both springSecurityChain(internal, additionFilter) and originalChain, is it the correct way or I should add FilterRegistrationBean to avoid the filter add in originalChain?
I need to handler different authication filters with different urls. Without FilterRegistrationBean, all custom filters add in originalChain. I set the different configuration with #Order and separate the url path, but still the requests should pass all custom filters.
#Configuration
#Order(1)
public static class PrivateSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired private PrivateTokenFilter privateTokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
//...
.antMatcher("/api/private/**")
.addFilterBefore(privateTokenFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
}
#Configuration
#Order(2)
public static class PublicSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired private JsonWebTokenFilter jsonWebTokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
//...
.antMatcher("/api/**")
.addFilterBefore(jsonWebTokenFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
}
To solve the problem, I should add FilterRegistrationBean setting for both custom filters, is that the right way?
spring-boot version: 2.6.6
spring-security : 5.6.2

configure(HttpSecurity http) and configure(AuthenticationManagerBuilder) get ignored with certain annotations in WebSecurityConfigurerAdapter

I'm trying to secure my spring application that has different user roles. While the Authentication part is set and works flawlessly, I realised during the implementation of the Authorisation part that with certain annotations, one of the two overrides methods inside my SecurityConfiguration extends WebSecurityConfigurerAdapter class, gets ignored.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private WebApplicationContext applicationContext;
private CredentialsService userDetailsService;
#Autowired
private DataSource dataSource;
#PostConstruct
public void completeSetup() {
userDetailsService = applicationContext.getBean(CredentialsService.class);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.and()
.formLogin()
.permitAll()
.and()
.httpBasic()
.disable()
.authorizeRequests()
.antMatchers("/admin", "/admin/**")
.hasRole("ADMIN")
.and()
.authorizeRequests()
.antMatchers("/employee", "/employee/**")
.hasRole("EMPLOYEE")
.and()
.authorizeRequests()
.antMatchers("/customer", "/customer/**")
.hasRole("CUSTOMER");
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder())
.and()
.authenticationProvider(authenticationProvider())
.jdbcAuthentication()
.dataSource(dataSource);
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(12);
}
}
Now the problem is the following, as it is, this class authenticate my users but has one major drawback: the
configure(final HttpSecurity http) throws Exception {
gets completely ignored.
On the other side though, if I add the #Configuration annotation on top of my class, the
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
gets completely ignored, hence will break the authorisation as it won't be able to call the getUsername() and getPassword on my custom UserDetailsService implementation.
As you can see, I've used a DaoAuthenticationProvider instance as authenticationProvider, since my application retrieve the users/password from an external database.
The quick fix I adopted right now it's the addition of the following method on my main class
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
and the use of the #Secured annotation on my restricted controllers. That works, but I'd like to understand why Spring has such strange behaviour and what step can I take to address these problems.
Since you are assigning roles to your users, use the syntax
.antMatchers("/admin", "/admin/**")
.hasRole("ADMIN")
OR
.antMatchers("/admin", "/admin/**")
.hasAuthority("ROLE_ADMIN")
Roles are just stored as authorities with the "ROLE_" prefix.
So the role "ADMIN" is equivalent to the authority "ROLE_ADMIN".
EDIT 1
You can also simplify your configuration to make it clear where everything is coming from.
Since you UserDetailsService (CredentialsService) is already a bean, it will be picked up automatically by Spring Security.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// The password encoder should be a bean
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(12);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.and()
.formLogin()
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/admin", "/admin/**")
.hasRole("ADMIN")
.and()
.authorizeRequests()
.antMatchers("/manager", "/manager/**")
.hasRole("MANAGER")
.and()
.authorizeRequests()
.antMatchers("/customer", "/customer/**")
.hasRole("CUSTOMER");
}
}

Spring Security - Filter Ordering and Multiple HttpSecurity

I want two diffrent http configurations to come in depending on the url i am entering. For example, when i type in "localhost:8080/HQ/test_web" i want this configuration to come in.
#Configuration
#Order(1)
public static class FirstWaveFilters extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/HQ/test_web/**").anonymous().and().addFilterBefore(new CustomFilter(),BasicAuthenticationFilter.class);
}
}
But, if its anything else, i want this configuration to come in:
#Configuration
#Order(2)
public static class SecondWaveFilters extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/**").permitAll();
http.csrf().disable();
http.headers().frameOptions().disable();
}
}
I have them set up in the same class as the Spring Security doc suggested:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsServiceImpl userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Configuration
#Order(1)
public static class FirstWaveFilters extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/HQ/test_web/**").anonymous().and().addFilterBefore(new CustomFilter(),BasicAuthenticationFilter.class);
}
}
#Configuration
#Order(2)
public static class SecondWaveFilters extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/**").permitAll();
http.csrf().disable();
http.headers().frameOptions().disable();
}
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
But it does not seem to work. Whatever url i enter, only the CUSTOMFILTER() gets called, so only the first configuration. Basically what i am trying to achieve is, if the user enters the first url i want that customfilter() to be the filter the request has to go through, if its any other url, i want it to go the second configuration and the two filters defined there to be the ones the request must go through. Why is this not working ?
http.antMatcher(...) - means, apply this http and all what is configured here when pattern in antMatcher is met.
http.authorizeRequests()... - defines your permissions, if user hit that endpoint he should has "ADMIN", "logged" etc.
In your FirstWaveFilters you have to start your http with http.antMatcher():
http.antMatcher("/HQ/test_web/**");
http.authorizeRequests().antMatchers("/HQ/test_web/**").anonymous()
.and()
.addFilterBefore(new CustomFilter(),BasicAuthenticationFilter.class);
If you are not add http.antMatcher(...); than that http will intercept all urls and SecondWaveFilters never will be reached.
http.authorizeRequests().antMatchers("/HQ/test_web/**").anonymous() - means that any anonymous user may hit /HQ/test_web/**, but it doesn't say "apply FirstWaveFilters when /HQ/test_web/**" it just mean anyone who hists /HQ/test_web/** may be anonymous.

spring-boot project with spring-security do not access views

I start a new project based on spring-boot, using this working configuration class for spring-security from another project:
#Configuration
#ComponentScan
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private PermissionEvaluator permissionEvaluator;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/signin")
.loginProcessingUrl("/login").permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
#Override
public void configure(WebSecurity web) throws Exception {
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
web.expressionHandler(handler);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
}
when I deploy the project in the tomcat server, without errors, and try access the application, instead of the index page, I get a popup windows asking me for username and password.
the full code for the project is: https://github.com/klebermo/basic_webapp
anyone can see what's wrong with this configuration?
add this to your application.properties file:
security.basic.enabled=false
By default everything is secured with HTTP Basic authentication.
Ref: http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.html

how to define the secured URLs within an application dynamically

I am using Spring framework version: 3.2.3.RELEASE and
Spring security version: 3.2.3.RELEASE
I want to define the secured URLs within an application dynamically,and I have tried for several ways . I just can't make it,please help me!
According to http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata ,
I learn that dynamically defining access control to URLs needs using an explicitly declared security filter chain in order to customize the FilterSecurityInterceptor bean.
My question is
how to explicitly declared security filter chain?are there any example?
according to the book Spring security 3.1
which said"We can use a custom BeanPostProcessor to replace the standard FilterInvocationServiceSecurityMetadataSource with our custom implementation."
I followed this way and it dosen't work, I wondering if I have done something wrong?
the Java config class looks like this:
public class MessageSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
#Configuration
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
#Component
public class KpiFilterInvocationSecurityMetadataSourceBeanPostProcessor implements BeanPostProcessor {
#Autowired
private KpiFilterInvocationSecurityMetadataSource metadataSource;
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof FilterInvocationSecurityMetadataSource) {
return metadataSource;
}
if(bean instanceof FilterChainProxy.FilterChainValidator) {
return new FilterChainProxy.FilterChainValidator() {
#Override
public void validate(FilterChainProxy filterChainProxy) {
}
};
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
I found a method withObjectPostProcessor,and try to do the following,it still doesn't worked. so what is best way to leverage this method?
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private KpiFilterInvocationSecurityMetadataSource metadataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/signup/**", "/about", "/login/**", "/index")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.loginPage("/login/form")
.loginProcessingUrl("/login")
.usernameParameter("policeNo")
.passwordParameter("password")
.failureUrl("/login/form?error")
.defaultSuccessUrl("/default")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login/form?logout")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.withObjectPostProcessor(
new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setSecurityMetadataSource(metadataSource);
return fsi;
}
});
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/resources/**");
}
}

Resources