Multiple WebSecurityConfigurerAdapter: one as a library, in the other users can add their own security access - spring

I am creating a Spring Security configuration to be used as a library by any developer who wants to create a Stormpath Spring application secured by Spring Security.
For that I have sub-classed WebSecurityConfigurerAdapter and defined the Stormpath Access Controls in configure(HttpSecurity) as well as the Stormpath AuthenticationProvider by means of configure(AuthenticationManagerBuilder). All this can be seen in this abstract class and its concrete sub-class:
#Order(99)
public abstract class AbstractStormpathWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
//Removed properties and beans for the sake of keeping focus on the important stuff
/**
* The pre-defined Stormpath access control settings are defined here.
*
* #param http the {#link HttpSecurity} to be modified
* #throws Exception if an error occurs
*/
protected void configure(HttpSecurity http, AuthenticationSuccessHandler successHandler, LogoutHandler logoutHandler)
throws Exception {
if (loginEnabled) {
http
.formLogin()
.loginPage(loginUri)
.defaultSuccessUrl(loginNextUri)
.successHandler(successHandler)
.usernameParameter("login")
.passwordParameter("password");
}
if (logoutEnabled) {
http
.logout()
.invalidateHttpSession(true)
.logoutUrl(logoutUri)
.logoutSuccessUrl(logoutNextUri)
.addLogoutHandler(logoutHandler);
}
if (!csrfProtectionEnabled) {
http.csrf().disable();
} else {
//Let's configure HttpSessionCsrfTokenRepository to play nicely with our Controllers' forms
http.csrf().csrfTokenRepository(stormpathCsrfTokenRepository());
}
}
/**
* Method to specify the {#link AuthenticationProvider} that Spring Security will use when processing authentications.
*
* #param auth the {#link AuthenticationManagerBuilder} to use
* #param authenticationProvider the {#link AuthenticationProvider} to whom Spring Security will delegate authentication attempts
* #throws Exception if an error occurs
*/
protected void configure(AuthenticationManagerBuilder auth, AuthenticationProvider authenticationProvider) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
#Configuration
public class StormpathWebSecurityConfiguration extends AbstractStormpathWebSecurityConfiguration {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
#Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
In short, we are basically defining our login and logout mechanisms and integrating our CSRF code to play nicely with Spring Security's one.
Up to this point everything works OK.
But this is just the "library" and we want users to build their own applications on top of it.
So, we have created a Sample application to demonstrate how a user will use our library.
Basically users will want to create their own WebSecurityConfigurerAdapter. Like this:
#EnableStormpathWebSecurity
#Configuration
#ComponentScan
#PropertySource("classpath:application.properties")
#Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
/**
* {#inheritDoc}
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
}
In case this is actually needed, the WebApplicationInitializer looks like this:
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringSecurityWebAppConfig.class);
context.register(StormpathMethodSecurityConfiguration.class);
sc.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = sc.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//Stormpath Filter
FilterRegistration.Dynamic filter = sc.addFilter("stormpathFilter", new DelegatingFilterProxy());
EnumSet<DispatcherType> types =
EnumSet.of(DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
filter.addMappingForUrlPatterns(types, false, "/*");
//Spring Security Filter
FilterRegistration.Dynamic securityFilter = sc.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
}
}
All this code boots up correctly. If I go to localhost:8080 I see the welcome screen. If I go to localhost:8080/login I see the login screen. But, if I go to localhost:8080/restricted I should be redirected to the login page since we have this line: http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();. However I am seeing the Access Denied page instead.
Then, if I add the login url in the App's access control, like this:
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login")
.and()
.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
It now redirects me to the login page but as soon as I submit the credentials I get an CSRF problem meaning that all our configuration is not actually part of this filter chain.
When I debug it all it seems that each WebApplicationInitializer is having its own instance with its own Filter Chain. I would expect them to be concatenated somehow but it seems that it is not actually happening...
Anyone has ever tried something like this?
BTW: As a workaround users can do public class SpringSecurityWebAppConfig extends StormpathWebSecurityConfiguration instead of SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter. This way it works but I want users to have pure Spring Security code and extending from our StormpathWebSecurityConfiguration diverges from that goal.
All the code can be seen here. The Stormpath Spring Security library for Spring is under extensions/spring/stormpath-spring-security-webmvc. The Sample App using the library is under examples/spring-security-webmvc.
It is very simple to run... You just need to register to Stormpath as explained here. Then you can checkout the spring_security_extension_redirect_to_login_not_working branch and start the sample app like this:
$ git clone git#github.com:mrioan/stormpath-sdk-java.git
$ git checkout spring_security_extension_redirect_to_login_not_working
$ mvn install -DskipTests=true
$ cd examples/spring-security-webmvc
$ mvn jetty:run
Then you can go to localhost:8080/restricted to see that you are not being redirected to the login page.
Any help is very much appreciated!

In my experience there are issues with having multiple WebSecurityConfigurers messing with the security configuration on startup.
The best way to solve this is to make your library configuration into SecurityConfigurerAdapters that can be applied where appropriate.
public class StormpathHttpSecurityConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
}
public class StormpathAuthenticationManagerConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
You then have your users apply these in their own configuration:
#EnableStormpathWebSecurity
#Configuration
#ComponentScan
#PropertySource("classpath:application.properties")
#Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/restricted").fullyAuthenticated()
.and()
.apply(new StormPathHttpSecurityConfigurer(...))
;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new StormPathAuthenticationManagerConfigurer(...));
}
}

It's definitely the problem regarding either order of your Antmatchers or hasn't specified ROLES of users that you permit to access the URL.
What do you have anything above "/restricted"?
Is something completely blocking anything below that URL? You should specify more specific URLS first then, generalised URLs.
Try configuring above URL properly (or tell me what it is so I can help you out), perhaps apply "fullyAuthenticated" also "permitAll" ROLEs on the parent URL of "/restricted".

Related

Cannot upload files, Upload Components returns forbidden

I am not able to upload files with the UploadComponent. I have a Spring application, using Vaadin 21 with enabled Spring Security.
The spring seccurity file, looks the following way:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends VaadinWebSecurityConfigurerAdapter {
// Our custom authentication provider
#Autowired
private AppCustomAuthenticationProvider authProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.rememberMe().alwaysRemember(false);
http.authorizeRequests().antMatchers("/VAADIN/**").permitAll();
super.configure(http);
// This is important to register your login view to the
// view access checker mechanism:
setLoginView(http, LoginView.class);
// Set the default success Url
http.formLogin().defaultSuccessUrl(ApplicationUrl.APP);
// Set the default failure Url
http.formLogin().failureUrl(ApplicationUrl.APP_LOGIN_FAILURE_URL);
}
/**
* Configuration of the custom authentication provider
*/
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
/**
* Exclude Vaadin-framework communication and static assets from Spring Security
*/
#Override
public void configure(WebSecurity web) throws Exception {
// Configure your static resources with public access here:
web.ignoring().antMatchers(
"/images/**"
);
// Delegating the ignoring configuration for Vaadin's
// related static resources to the super class:
super.configure(web);
}
}
Screenshot of the error:
The upload component is integrated into a Dialog.
Hopefully someone can help me,
Florian
This was fixed in Vaadin 21 alpha 8, see https://github.com/vaadin/flow/pull/11278

disable spring formlogin and basic auth

I have the following spring boot 2.0 config but I am still getting the basic auth login screen. I DO NOT want to disable all spring security like almost every post on the internet suggests. I only want to stop the form login page and basic auth so I can use my own.
I have seen all the suggestions with permitAll and exclude = {SecurityAutoConfiguration.class} and a few others that I can't remember anymore. Those are not what I want. I want to use spring security but I wan my config not Spring Boots. Yes I know many people are going to say this is a duplicate but I disagree because all the other answers are to disable spring security completely and not just stop the stupid login page.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomSecurity extends WebSecurityConfigurerAdapter {
private final RememberMeServices rememberMeService;
private final AuthenticationProvider customAuthProvider;
#Value("${server.session.cookie.secure:true}")
private boolean useSecureCookie;
#Inject
public CustomSecurity(RememberMeServices rememberMeService, AuthenticationProvider customAuthProvider) {
super(true);
this.rememberMeService = rememberMeService;
this.bouncerAuthProvider = bouncerAuthProvider;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/**").antMatchers("/webjars/**").antMatchers("/swagger-resources/**")
.antMatchers("/swagger-ui.html");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable().formLogin().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).headers().frameOptions().disable();
http.authenticationProvider(customAuthProvider).authorizeRequests().antMatchers("/health").permitAll()
.anyRequest().authenticated();
http.rememberMe().rememberMeServices(rememberMeService).useSecureCookie(useSecureCookie);
http.exceptionHandling().authenticationEntryPoint(new ForbiddenEntryPoint());
}
}
If you want to redirect to your own login page, i can show your sample code and configuration
remove the http.httpBasic().disable().formLogin().disable();, you should set your own login page to redirect instead of disable form login
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/my_login").permitAll().and().authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/my_login");
}
then create your own LoginController
#Controller
public class LoginController {
#RequestMapping("/my_login")
public ModelAndView myLogin() {
return new ModelAndView("login");
}
}
you can specified the login with thymeleaf view resolver

Spring Boot differentiate authN/authZ based on HTTP verb + URL pattern

I want to switch authentication and authorization scheme based on http verb and url pattern in a spring boot application, below is a simplification of my use-case.
Most /mgmt/... requests will be authorized based on basic auth.
Some /mgmt/... will be be publicly open, and require no authentication.
Any path not previously defined, should be ignored/blocked.
My plan was to define rules and evaluate them in order.
For the ones used in this example there would be three rules, evaluated in order, whichever matches first will be used.
GET /mgmt/configuration/** -> Publicly open (anonymous)
ANY /mgmt/** -> ADMIN role user
ANY /** -> deny
I'm having problem with rules for public and admin, specifically that they overlap (both starts with /mgmt).
If I put the public under it's own context path (/public) the below code works - but I don't want to do that (nor have I control over these patterns).
Perhaps I need to group configurators by context path rather than authentication scheme. Configurations are actually not static as in this example, but comes from other modules (microservices) and are assembled in a security lib in an attempt to centralize auth code.
#EnableWebSecurity
public class MultiHttpSecurityConfig {
private static final String MGMT_PATTERN = "/mgmt/**";
private static final String PUBLIC_PATTERN = "/public/**";
#Configuration
#Order(1)
public static class PublicConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher(PUBLIC_PATTERN).authorizeRequests().anyRequest().permitAll();
}
}
#Configuration
#Order(2)
public static class AdminConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher(MGMT_PATTERN)
.authorizeRequests().anyRequest()
.hasRole("ADMIN")
.and()
.httpBasic()
.and().csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
/**
* Some operation.
*
* #return some value.
*/
#Bean
public UserDetailsService userDetailsService() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
UserDetails user = User.withUsername("user")
.password(encoder.encode("password"))
.roles("ADMIN").build();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(user);
return manager;
}
}
#Configuration
#Order(3)
public static class DenyConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().anyRequest().denyAll();
}
}
}
I changed configurations and grouped them by url pattern rather than type and that solves the issue I had with different configurations overlapping each other.

How to disable spring security for certain resource paths

I am implementing spring security in a spring boot application to perform JWT validation where I have a filter and an AuthenticationManager and an AuthenticationProvider. What I want to do is that I want to disable security for certain resource paths (make them unsecure basically).
What I have tried in my securityConfig class (that extends from WebSecuirtyConfigurerAdapater) is below:
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(),
UsernamePasswordAuthenticationFilter.class);
httpSecurity.authorizeRequests().antMatchers("/**").permitAll();
httpSecurity.csrf().disable();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
What I am trying to do right now is that I want to make all my resource paths to be un-secure,
but the above code doesn't work and my authenticate method in my CustomAuthenticationProvider (that extends from AuthenticationProvider) get executed every time
Authentication piece gets executed irrespective of using permitAll on every request. I have tried anyRequest too in place of antMatchers:
httpSecurity.authorizeRequests().anyRequest().permitAll();
Any help would be appreciated.
Override the following method in your class which extends WebSecuirtyConfigurerAdapater:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/unsecurePage");
}
try updating your code in order to allow requests for specific paths as below
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(),
UsernamePasswordAuthenticationFilter.class);
httpSecurity.authorizeRequests().antMatchers("/").permitAll().and()
.authorizeRequests().antMatchers("/exemptedPaths/").permitAll();
httpSecurity.csrf().disable();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

Spring Boot setup with multiple authentication providers (API+Browser)

My application serves both API and browser. I've implemented API Token authentication with all custom providers and filter. The configuration now seems to interfere with the browser version.
I have two questions that I need advice on how to solve, as I'm not getting anywhere after digging through the documentation and other examples.
1) My StatelessAuthenticationFilter is being called despite a request
coming from the browser. I have e.g. specified the request matcher to "/api/**". Why is that?
2) The AuthenticationManager have not registered two AuthenticationProviders. This is my conclusion after debugging my StatelessAuthenticationFilter that's being called wrongly.
Here's the configuration classes that I have
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Order(1)
#Configuration
public static class A extends WebSecurityConfigurerAdapter {
#Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
#Autowired
ApiEntryPoint apiEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
StatelessAuthenticationFilter filter = new StatelessAuthenticationFilter();
AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/api/**");
filter.setRequiresAuthenticationRequestMatcher(requestMatcher);
filter.setAuthenticationManager(super.authenticationManager());
http.csrf().disable()
.exceptionHandling().authenticationEntryPoint(apiEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/user/register");
}
}
#Configuration
public static class B extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new DaoAuthenticationProvider());
}
}
}
As you can see, B class doesn't specify anything, yet when I access localhost:8080 the StatelessAuthenticationFilter is called. What is going on here?
In class A you are configuring the StatelessAuthenticationFilter to use a requestMatcher. Whatever you do with that, spring does not know or care about that.
You must also restrict your security configuration using
http.antMatcher("/api/**")
otherwise its configured for every URI and the StatelessAuthenticationFilter will be invoked for every request, exactly as you described.
You should also annotate class A and B with #Order as shown in the example at multiple-httpsecurity

Resources