Spring security with filters only for specific url - spring

I want security to trigger only for specific URLs with a key in them, what I think is happening is that I bypass spring security, but the added Authorization filter that extends the BasicAuthenticationFilter is triggering with every url how can I set urls to the added BasicAuthenticationFilter also. I am implementing Jwt token.
This is my config:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserServiceImpl userService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf()
.disable().authorizeRequests()
.antMatchers("**/auth/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilter(new AuthorizationFilter(authenticationManager()))
.addFilter(authenticationFilter())
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.headers().cacheControl();
}
public AuthenticationFilter authenticationFilter() throws Exception{
final AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager());
authenticationFilter.setFilterProcessesUrl("/login");
return authenticationFilter ;
}
}

Options
Extend BasicAuthenticationFilter and as a consequence of inheritance you can write your own authentication approach, during run time methods from sub-class will be taken into account from Spring security side.
Extend AbstractAuthenticationFilter and do required filtering there.

Related

Spring Security does not reject requests when missing HTTP basic authentication header

I'm trying to setup a simple HTTP basic authentication mechanism for accessing REST endpoints in an application.
Basically, all endpoints starting with /api/internal shall be secured with HTTP basic authentication, while further configurations shall secure other paths with e.g. OAuth2.
The problem is that, for example, a GET request to /api/internal/test is allowed even when the client does not provide any credentials in the request header.
This is my current security configuration class:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Order(1)
#Configuration
#EnableWebSecurity
public static class InternalApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
final PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("user")
.password(passwordEncoder.encode("password"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.mvcMatcher("/api/internal/**")
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
}
// Other security configuration follow here...
}
After having spent some more time on this problem, I found that the authentication works when adding the following to the chain:
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

Spring Boot Security UsernamePasswordAuthenticationFilter does not limit the login url to only POST

I looked all throughout and nobody else is having this issue that I can find. The authentication works correctly but the login url works on any HTTP method (GET, PUT, etc) vs. only working on POST. I tried manually setting filter.setPostOnly(true); on the custom JWTAuthenticationFilter I made, but it still allows on all methods.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String FILTER_PROCESS_URL = "/authentication";
private static final String HEALTH_RESOURCE_URL = "/health/**";
private CustomUserDetailService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig(CowCalfUserDetailService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, FILTER_PROCESS_URL).permitAll()
.antMatchers(HttpMethod.GET, HEALTH_RESOURCE_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(getJWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilter(new JWTAuthorizationFilter(authenticationManagerBean()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
public JWTAuthenticationFilter getJWTAuthenticationFilter() throws Exception {
final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManagerBean());
filter.setPostOnly(true);
filter.setFilterProcessesUrl(FILTER_PROCESS_URL);
return filter;
}
}
If i understand you correctly, you want to limit the /authentication only to the POST Http-Method. If yes, you could achieve it by adding the following code snippet to your method in the "RestController" for the Authentication:
#RequestMapping(value = "/authentication", method = RequestMethod.POST)
RequestMapping Docs
I found out how setPostOnly() works. UsernamePasswordAuthenticationFilter.attemptAuthentication() checks for POST before attempting the authentication and throws an exception. This method I have overridden with my custom JWTAuthenticationFilter. I just did the same and added a check in my overridden method too. Thank you for the suggestions!

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.

#Value annotation return empty value in an AbstractAuthenticationProcessingFilter filter

I'm developing a springboot application with spring security.
I'm trying to make my custom authentication filter reading some properties from the application.properties file without success.
I've read this other question which is similar but within a different context (not related to spring security filters). The reason for the failure makes sense to me but I've tried the way suggested with the DelegatingFilterProxy but without success (to be fair, I didn't really get the meaning of the part added to the Application class). The other solution does not fit my case as I don't have any onStartup method to override.
Here is the code I'm using:
public class JWTAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
#Value("${app.jwtSecret}")
public String SECRET2;
Almost the same code, in a controller class, works fine:
#RestController
#RequestMapping("/api")
#CrossOrigin
#EnableAutoConfiguration
public class UsersController {
#Value("${app.jwtSecret}")
public String SECRET2;
But I can't make it work in the filter. I'm using springboot 2.0.3.
Any suggestion? Is the DelegatingFilterProxy the right approach in this situation? In that case, any example/article I could follow?
Thanks,
Michele.
UPDATE:
to fully answer to the first comment, the filter is called by the following class:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private LdapAuthenticationProvider ldapAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/secureLogin").permitAll()
.antMatchers(HttpMethod.GET, "/api").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class)
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapAuthenticationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
No need to use #Value in filter class:
public class JWTAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
private String secret;
//... setter for secret
But inject the secret in the config class:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Value("${app.jwtSecret}")
public String secret;
//...
#Override
protected void configure(HttpSecurity http) throws Exception {
JWTAuthorizationFilter jwtFilter = new JWTAuthorizationFilter(authenticationManager());
//set secret
//...
}

Spring security oauth2 always returning 403

I have a Spring boot app serving Rest endpoints which I'm securing using Spring security and Oauth2.
I want to secure all my endpoints except the endpoints used to authenticate, to create an account or some info stuff.
The security configuration is like this :
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private MongoTokenStore tokenStore;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
//clients.withClientDetails(clientDetailsService);
clients.inMemory().withClient("app").secret("password")
.accessTokenValiditySeconds(30000).authorizedGrantTypes("password", "refresh_token")
.refreshTokenValiditySeconds(300000000)
.scopes("read");
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager)
.pathMapping("/oauth/confirm_access", "/access_confirmation");
}
#Bean
public TokenStore tokenStore() {
return this.tokenStore;
}
}
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserRepository userRepository;
#Autowired
private SecurityContextService securityContextService;
#Autowired
private MongoTemplate mongoTemplate;
#Bean
public MongoUserDetailsManager mongoUserDetailsManager() throws Exception {
return new MongoUserDetailsManager(userRepository, securityContextService, authenticationManagerBean(), mongoTemplate);
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.userDetailsService(mongoUserDetailsManager());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/login", "/oauth/authorize", "/oauth/token", "/server/version", "/clients/register").permitAll()
.and().csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.disable();
}
}
I can access to token endpoint to get my access_token, but I want to access to other secured endpoints using this access_token (by adding the Authorization:Bearer {access_toke} to the header), I always get HTTP 403.
Did I miss something? I'm not supposed as authorized if I add the Authorization header?
My Controllers are only annotated with these #RestController, #CrossOrigin
and #RequestMapping("/url")
There are 2 types of security configurations in case of OAuth security(as far as urls security is concerned) in Spring.
1. Basic Security Configuration
This class should implement WebSecurityConfigurerAdapter. It will handle all those requests coming without "Bearer" token type(urls that shouldn't be oauth protected).
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserRepository userRepository;
#Autowired
private SecurityContextService securityContextService;
#Autowired
private MongoTemplate mongoTemplate;
#Bean
public MongoUserDetailsManager mongoUserDetailsManager() throws Exception {
return new MongoUserDetailsManager(userRepository, securityContextService, authenticationManagerBean(), mongoTemplate);
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.userDetailsService(mongoUserDetailsManager());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/login", "/oauth/authorize", "/oauth/token", "/server/version", "/clients/register").permitAll()
.and().csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.disable();
}
}
2. Resource Server Configuration(OAuth Specific)
This class is responsible for handling all those requests coming with authorization header of type Bearer. It should be extended from ResourceServerConfigurerAdapter class. Here you should mention all those urls with security configurations that you like to be oauth protected.
#Configuration
#EnableResourceServer
public class OAuthResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/resources-to-be-protected/**").and().authorizeRequests()
.antMatchers("/resources-to-be-protected/**").access("#oauth2.isClient()");
}
}

Resources