Lazy initialise spring security at runtime + reload spring security configuration - java

Spring usually eagerly loading the spring security configuration while starting the application. I'm using OAuth with Spring Security
I'm maintaining a configuration table for storing the SSO related values (like jwk-url, client_id, client_secret). This values will be populated by an admin user via CRUD in the same spring boot application.
Then only the jwk-url is available to be configure in the Spring security configuration (refer below code - jwkSetUri(...)). This would not available at the application startup.
So I wanted to initialise the spring security configuration after the value is loaded into the table, like a lazy loading (#Lazy) at runtime. I know how to do Lazy loading of a regular class/service.
But still I'm not sure how to invoke the configure(HttpSecurity http) method at runtime and how to p
ass the HttpSecurity parameter. When I just try invoke new ResourceServerConfiguration() like a lazy loading at runtime, I don't see the configure() method is called. (Or) this class needs to be maintained as bean and lazy load whenever needed. But still not sure about how to call configure() in code.
Another thing is how to refresh/reload the spring security configuration at runtime, if the JWK url is changed by admin. Then only the spring security configuration can take effect of the changes.
#Configuration
#EnableWebSecurity
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.authenticationEntryPoint(oAuth2AuthenticationEntryPoint)
.accessDeniedHandler(oAuth2AccessDeniedHandler)
.jwt()
// Some Auth server URL which would be fetch from table
.jwkSetUri(ssoConfigService.getActiveSSOCertificateURL());
// Eg. http://localhost:8090/auth/realms/demo-app/protocol/openid-connect/certs
}
}
I have already referred these links. But it doesn't help for my purpose. Any help would be appreciated.
How do I lazy load Spring Security?
How to reload the Configure method of WebSecurityConfigurerAdapter when the application is up and running
Modify Spring Security Config at Runtime
Configure Spring HTTP Security at Runtime

Please, check this link Customizing CORS Filtering at Runtime that include a similar use case related to your but for him, he needed to change allowed origins dynamically. They decide to create a new filter and simple extends OncePerRequestFilter.
Take in account to check the OAuth2ResourceServerProperties for your use case.
UPDATING:
Try with this code for this scenario:
Another thing is how to refresh/reload the spring security configuration at runtime, if the JWK url is changed by admin. Then only the spring security configuration can take effect of the changes.
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated()
// TODO: test with and without this and check if work for you
.and()
.oauth2ResourceServer()
.authenticationEntryPoint(oAuth2AuthenticationEntryPoint)
.accessDeniedHandler(oAuth2AccessDeniedHandler)
.jwt()
// Some Auth server URL which would be fetch from table
.jwkSetUri(ssoConfigService.getActiveSSOCertificateURL());
// Eg. http://localhost:8090/auth/realms/demo-app/protocol/openid-connect/certs
http.addFilterBefore(new OncePerRequestFilter() {
// Every time a request occur, this method will be called.
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
try {
http.oauth2ResourceServer()
.authenticationEntryPoint(oAuth2AuthenticationEntryPoint)
.accessDeniedHandler(oAuth2AccessDeniedHandler)
.jwt()
// Some Auth server URL which would be fetch from table
.jwkSetUri(ssoConfigService.getActiveSSOCertificateURL());
} catch (Exception e) {
e.printStackTrace();
}
}
}, BasicAuthenticationFilter.class);
}
I hope this info can help you.

Related

Spring Security Configuration - HttpSecurity vs WebSecurity

I just need to understand something in Spring Security Configuration. Using the example below...
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests().antMatchers("/secret/**").authenticated()
.and()
.authorizeRequests().antMatchers("/**").permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
}
What is the purpose of configure(WebSecurity web) method?
Can't I just add /resources/** in the configure(HttpSecurity http) method in this line .authorizeRequests().antMatchers("/**", "/resources/**").permitAll();
Shouldn't it work the same i.e. permitting all requests to /resources/** without any authentication?
General use of WebSecurity ignoring() method omits Spring Security and none of Spring Security’s features will be available.
WebSecurity is based above HttpSecurity.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**")
.antMatchers("/publics/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/publics/**").hasRole("USER") // no effect
.anyRequest().authenticated();
}
WebSecurity in the above example lets Spring ignore /resources/** and /publics/**. Therefore the .antMatchers("/publics/**").hasRole("USER") in HttpSecurity is unconsidered.
This will omit the request pattern from the security filter chain entirely.
Note that anything matching this path will then have no authentication or authorization services applied and will be freely accessible.
configure(HttpSecurity) allows configuration of web-based security at a resource level, based on a selection match - e.g. The example below restricts the URLs that start with /admin/ to users that have ADMIN role, and declares that any other URLs need to be successfully authenticated.
configure(WebSecurity) is used for configuration settings that impact global security (ignore resources, set debug mode, reject requests by implementing a custom firewall definition). For example, the following method would cause any request that starts with /resources/ to be ignored for authentication purposes.
Let's consider the below code, we can ignore the authentication for the endpoint provided within antMatchers using both the methods.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/login", "/register", "/api/public/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login", "/register", "/api/public/**").permitAll()
.anyRequest().authenticated();
}
configure(WebSecurity web)
Endpoint used in this method ignores the spring security filters, security features (secure headers, csrf protection etc) are also ignored and no security context will be set and can not protect endpoints for Cross-Site Scripting, XSS attacks, content-sniffing.
configure(HttpSecurity http)
Endpoint used in this method ignores the authentication for endpoints used in antMatchers and other security features will be in effect such as secure headers, CSRF protection, etc.
When you use HttpSecurity and try to permitAll() requests. Your requests will be allowed to be accessed from the Spring Security Filter Chain. This is costly as there will be requests other requests which would also come into this filter chain which needs to be allowed or disallowed based on Authentication/Authorization.
HttpSecurity.authorizeRequests().antMatchers("/**", "/resources/**").permitAll();
But when you use, any requests to resources will completely by pass the Spring Security Filter Chain all together. It is safe because you don't need any Authentication/Authorization to be in place to see an image or read a javascript file.
WebSecurity.ignoring().antMatchers("/resources/**");
I want to contribute to the great answers by including some code. There are three beans that are super important in Spring Security. based on their types they are DelegatingFilterProxy, FilterChainProxy and SecurityFilterChain.
DelegatingFilterProxy delegates the job of filtering requests to a bean of type FilterChainProxy which its name is springSecurityFilterChain, and FilterChainProxy is configured like this:
#Bean(
name = {"springSecurityFilterChain"}
)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
return (Filter)this.webSecurity.build();
}
springSecurityFilterChain (or FilterChainProxy) itself has a list of SecurityFilterChain. SecurityFilterChain itself has a list of Filter instances that do the actual logic.
Every time we extend WebSecurityConfigurerAdapter and override the configure(HttpSecurity httpSecurity) method, we actually created a SecurityFilterChain that is going to be used by springSecurityFilterChain
How springSecurityFilterChain selects the appropriate SecurityFilterChain from the list? based on the boolean matches(HttpServletRequest request) method that is defined in the SecurityFilterChain interface.
So HttpSecurity is used to create a customized SecurityFilterChain.
Now when WebSecurity actually comes into play? WebSecurity actually allow us to customize springSecurityFilterChain(or FilterChainProxy). take a look at how springSecurityFilterChain is created.
It is the performBuild method of WebSecurity that is called for creating springSecurityFilterChain bean.
#Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this done by adding a #Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
As you can see when Spring wants to registers SecurityFilterChain into springSecurityFilterChain bean for each web.ignoring().... Spring is going to add a DefaultSecurityFilterChain which is a custom implementation of SecurityFilterChain into the beginning of the list.
When a request comes along springSecurityFilterChain is going to check its list of SecurityFilterChain in order to delegate the filtering job to that SecurityFilterChain. springSecurityFilterChain is going to call match method of each SecurityFilterChain. if the request URL starts with "/resources/**" in your case Spring delegates the job of filtering request to an instance of DefaultSecurityFilterChain in the beginning of the list and our custom SecurityFilterChains which is added by this line:
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
completely ignored.
configure(HttpSecurity) : It allows configuring web based security for specific http requests. It is used for configuration of web based security at a resource level, based on a selection match.
configure (WebSecurity) : Allows adding RequestMatcher instances that Spring Security should ignore.

SpringBoot Security Configuration not working as intended

I have been trying to configure Spring Boot security in order to allow some urls without requiring an authentication and not allowing any other requests without an authentication. I am having trouble achieving this.
As per my understanding, anyRequest().authenticated() requires previously declared antMatchers to require authentication.
How is it possible to achieve my requirement.
My Http Security configuration
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.antMatchers(HttpMethod.POST,SIGN_UP_URL).permitAll()
.antMatchers(HttpMethod.GET,banner_top_url).permitAll()
.antMatchers(HttpMethod.GET,banner_bottom_url).permitAll()
.antMatchers(HttpMethod.GET,javascript_url).permitAll()
.antMatchers(HttpMethod.GET,stylesheet_url).permitAll()
.antMatchers(HttpMethod.GET,photos_url).permitAll()
.antMatchers(HttpMethod.GET,transformed_photos_url).permitAll()
.antMatchers(HttpMethod.GET,preview_url).permitAll()
.antMatchers(HttpMethod.GET, "/", "/**/*.html", "/static/favicon.ico", "/**/*.js", "/**/*.js.map", "/**/*.css", "/**/*.png", "/**/*.jpg", "/**/*.jpeg", "/**/*.gif", "/**/*.ttf", "/**/*.json", "/**/*.woff", "/**/*.woff2", "/**/*.eot", "/**/*.svg").permitAll()// allows static content from resource folder
.antMatchers("/error").permitAll() // By default Security framework disables error pages (Unauthrorized)
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().addFilterBefore(jwtExceptionHandler,CorsFilter.class)
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().formLogin().disable();
}
I assume that the below urls must be granted access without authentication.
SIGN_UP_URL
banner_top_url
banner_bottom_url
javascript_url
stylesheet_url
photos_url
transformed_photos_url
preview_url
The problem is this line : .anyRequest().authenticated()
If I remove it, then all the endpoints within the REST interface becomes available without authentication which I do not want.
Why aren't you excluding the static resource files globally via web.ignoring?
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
By default, Spring-security allows to pass everything. You have to tell Spring what can pass and what cannot pass. By removing anyRequest().authenticated you are telling to spring that everything that matches the patterns you mentioned are allowed to go and with the rest do what you do by default, that means, proceed. Here you are Spring Security doc: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-httpsecurity
I have found that Spring-Working as intended. That being said,any antMAtchers will match the requestPath and not the resourcePath. An example is provided below.
*localhost:8080/image.jpg*
points at the root of the application which is src/main/resources/static/image.jpg
Now why is static used as a resource handler, that is because in the staticResourceConfiguration.java class I had the following lines
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");

Spring Boot - how to conditionally enable/disable sessions

I've built a REST API service using Spring where I've enabled sessions using MongoDB:
#Configuration
#EnableMongoHttpSession(maxInactiveIntervalInSeconds = Globals.SESSION_MAX_INTERVAL)
public class SessionConfig {
#Bean
public AbstractMongoSessionConverter createSessionConverterBean() {
return new JacksonMongoSessionConverter(Collections.singletonList(new GeoModule()));
}
}
I would however, like to have control over which connections should be issued a session. Currently, every HTTP request has a session generated for it, but there are scenarios where the session is not needed, and I'd prefer not to clutter up the session storage with session objects that will never be used.
One such scenario is a standalone desktop application that acts as a content management system. This application has no need for HTTP sessions because authentication is done via the application side via a custom authorization header. This application also only accesses endpoints from a certain root route mapping:
Public traffic routes to api.domain.com/pub and the CMS traffic routes through api.domain.com/cpi.
It would be nice to be able to tell Spring that it does not need to create a session for any requests coming to /cpi. The desktop application also provides a unique Origin that I can match as well if that is more easily done.
My Web security looks like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.permitAll()
.and()
.cors()
.and()
.httpBasic();
http.csrf().disable(); // Self-implemented
}
I've searched all over and haven't found a thing. Can anyone point me in the right direction?
Thanks!
You could add multiple security configuration in the following scheme. Where one is explicitly matching for the all /cpi requests and the other one handling the remaining requests.
You could also configure different authentication methods this way.
#Order(1)
#Configuration
public static class Custom1WebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
http
.antMatcher("/cpi/**")
.authorizeRequests()
...
http.sessionManagement() // dont create a session for this configuration
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Order(2)
#Configuration
public static class Custom2WebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
http
.authorizeRequests()
...
}
You could try below in application.yml file.
server:
servlet:
session:
persistent: false
timeout: 0

Java Spring common way to implement user authentication

I am new to Java and I trying to write first web application with Spring framework (I have some experience with django/python and sinatra/ruby).
I need to implement user authentication system and have no idea what is the best way to do this correctly. Should I use Spring Security for this? Or there is some another ways?
Since you starting a new application, I strongly urge to use Spring Boot. It will take away a lot of your initial pain in configuring the various aspects of Spring.
As for security, you should go with Spring Security which of course is easy to configure with Spring Boot.
Following this guide, a simple Spring Security configuration which uses in memory authentication would look like:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
As you can see, the first method is used to configure which parts of the application are to be secured and how, while the second method configures how the users will be authenticated.
One common way to provide a customized way to authenticate users, is to provide a custom UserDetailsService implementation as is done here
A very easy way to configure authentication when you already have configured a DataSource and the user credentials are stored there, is the following:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN").and()
.withDefaultSchema();
}
}
Here is the complete reference of Spring Security. Also here is security part of the Spring Boot documentation. You can find a multitude of sample applications using various aspects of Spring Security here
Initially it may seem that Spring Security is complicated, but once you get the hang of it, you'll appreciate it's extended feature set and customizability.
One final note, when to comes to things like security which are common in so many applications, there is no need to reinvent the wheel! Go with Spring Security (or perhaps Apache Shiro)

Spring Security 3.1.3 request querystring stripped

I am securing my application using Spring Security 3.1.3 and I have a requirement to allow users to login via a link in a third-party application.
However, the link in the third-party application will redirect to a specific resource and not to the login page, where the resource that the user wishes
to access will be defined as a querystring parameter. So, for example, the link would be of the form :
//server.com/app/build/panel.jsp?resourceid='blah'
When a user clicks this link they should be taken to the login page defined in my Spring Security configuration and if authenticated then should be redirected
to the original link including the querystring parameter. The querystring parameter has no influence on how the user should be authenticated it's
merely an id of resource.
Now, this all works fine apart from the querystring, which gets stripped by Spring Security before it enters the request processing flow.
This is shown in the debug output from Spring Security;
org.springframework.security.web.savedrequest.HttpSessionRequestCache: DefaultSavedRequest added to Session:
DefaultSavedRequest[http://server.com:8080/app/build/panel.jsp]
ie, the querystring is not saved and resourceid='blah' has been removed.
Note, I'm currently using Ant matching. I have no need to actually match against the querystring.
In earlier versions of Spring Security, it seemed like you could influence this behaviour by using a BeanPostProcessor as per this post,
Spring Security - Url with request parameters rules ignored. But the method
DefaultFilterInvocationSecurityMetadataSource.setStripQueryStringFromUrls() has been removed from Spring Security 3.1.3.
How do I configure Spring Security to not strip the querystring from the original request? So that when the user is redirected after the login to
the original URL the querystring parameter will be retained?
Many Thanks
Howard
U can get it from SuccessHandler
SecurityConfiguration class
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
SuccessHandler getSuccessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/dashboard/**",
"/feedback/**"
).access("hasRole('ROLE_SYSTEM_ADMIN') or hasRole('ROLE_COMPANY_ADMIN')")
.and().formLogin().loginPage("/login").successHandler(getSuccessHandler)
.loginProcessingUrl("/login").usernameParameter("ssoId").passwordParameter("password")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/Access_Denied")
.and()
.sessionManagement().invalidSessionUrl("/login").maximumSessions(1).expiredUrl("/login").and().sessionAuthenticationErrorUrl("/login").sessionFixation().migrateSession()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS); //always, IF_REQUIRED,never ,stateless
http.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
.permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**")
.antMatchers("/images/**");
}
}
SuccessHandler class
#Component
public class SuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
HttpSession session = request.getSession();
response.sendRedirect(request.getContextPath() + "/dashboard/index");
}
}
Is basically the success handler.
You can take a look at this example:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler(new RefererAuthenticationSuccessHandler());
}
More info about it : http://www.baeldung.com/spring-security-redirect-login
For others on a similar issue, refer the link:
https://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html
Extract:There is a danger that when an application is deployed in a container which does not strip path parameters from these values, an attacker could add them to the requested URL in order to cause a pattern match to succeed or fail unexpectedly.
However this stripping is meant to firmly protect the pattern matching for login. It doesnt means the query parameters are not available from the HTTP request, they should be.

Categories