Configure Spring HTTP Security at Runtime - spring

All http security is applied at startup:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
}
During runtime, I am trying to add more to it ...
applicationContext.getBean(WebSecurityConfigurerAdapter).http.authorizeRequests().antMatchers("bla").hasRole("admin")
When that line is executed, it adds it to http.authorizeRequests()'s but /bla is still accessible by "non admins"
When server is restarted, this change takes effect because it is loading bla from the database.
How do I make the security take effect instantly without restarting the server?

You are trying to dynamicaly change a spring bean at runtime which is very hard to do unless you use tools like spring-loaded or JRebel.
There is a lot of SO about it :
Update spring beans dynamically. Is it possible?
dynamically change spring beans
Can I replace a Spring bean definition at runtime?
The best approach (in my opinion) for your use case is to use spring profiles.
Define a bean with authorisations for /bla and another bean without. Then use them in different profiles.
see dynamically declare beans at runtime in Spring

My solution to these case scenarios is to make a dynamic custom spring security rule to match with all the path.
http
.authorizeRequests()
.antMatchers("/**").access("#customSecurityRule.check(authentication)");
This way new endpoint will automatically be configured with our custom security rule, and in our custom security rule we can preety much do anything we want, checking their roles, validate it againts our database and etc.

Related

Use Keycloak and JWT Statelessly in Spring Boot

I need to use Keycloak and prefer to use stateless JWT tokens with my Spring Boot application. I can get it to run OK with sessions, but when converting this I need help
Forcing Spring Boot Security to check for logins
Allow a /logout URL (that goes to Keycloak)
My code "runs", but when I hit the initial page, I'm seeing log messages that seem to suggest it did not detect any sign of being logged in. When this happens, I'd like to force Spring Boot to redirect to the login page just like it would have had this been a stateful application.
org.springframework.web.servlet.FrameworkServlet: Failed to complete request: java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AbstractAuthenticationToken.getName()" because "authenticationToken" is null
org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper: Did not store anonymous SecurityContext
org.springframework.security.web.context.SecurityContextPersistenceFilter: Cleared SecurityContextHolder to complete request
org.apache.juli.logging.DirectJDKLog: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AbstractAuthenticationToken.getName()" because "authenticationToken" is null] with root cause
java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AbstractAuthenticationToken.getName()" because "authenticationToken" is null
Here's my HttpSecurity snippet:
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.csrf()
// .disable().exceptionHandling().accessDeniedPage("/access-denied")
.and()
.authorizeRequests()
.antMatchers("/sso/**").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/css/**","/contact-us","/actuator/**","/isalive/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.defaultSuccessUrl("/myfirstpage",true)
.and().exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.oauth2ResourceServer().jwt();
}
I know I'm missing something, but I thought Keycloak provided a lot of these things OOTB. The initial URL is /. I had hoped the .authenticated() would force it to authenticate against all patterns not permitted, but I'm likely wrong. What am I missing?
Please note, the internet is awash with examples of Spring Boot + Keycloak (a few are even good). It also has a lot of Spring Boot + OAuth + Stateless JWT. It does not have (that I can tell) a lot of Spring Boot + Keycloak + Stateless JWT. I got the little that I could find from this JHipster repo, but I feel like I'm missing some grand magical step.
Resource-server should return 401 (unauthorized) when authentication is missing or invalid (expired, wrong issuer, etc.) and client should handle redirection to authorization-server.
Things get messy when you try to merge different OAuth2 actors (client, resource-server and authorization-server) into a single application.
Are you sure you want a Spring client (and not an Angular / React / Vue / Flutter / whatever client-side rendering framework)?
If yes, maybe should you start by splitting client (presenting login, logout and Thymeleaf or whatever pages) and resource-server (REST API) apps. You'll better understand spring-security conf you write (and can assert it works as expected individually).
Resource-server configuration (REST API)
Be aware that Keycloak adapters for spring are deprecated.
Easiest solution is spring-addons-webmvc-jwt-resource-server (support multi-tenancy, stateless by default, CORS configuration from properties, easy Keycloak roles mapping to Spring authorities and more).
You can also work directly with spring-boot-starter-oauth2-resource-server, but it requires more java conf.
Client configuration (pages including login & logout)
if keeping Spring, refer to spring-boot documentation: it's clear and always up to date (as opposed to most tutorials)
if using a "modern" client-side framework, find a lib from certified list
I have a complete sample (Ionic-Angular UI with spring RESTful API) there, but it might be a little complicated as starter.
You will need spring-security and keycloak-adapters
This guid has full explanation how to setup and secure it
https://keepgrowing.in/java/springboot/keycloak-with-spring-boot-1-configure-spring-security-with-keycloak/

Spring StandardServletMultipartResolver

I was wondering where is located the code that automatically create a temporary file when you send a multipart request using StandardServletMultipartResolver?
Can i disable that behavior? I want to decide how its going to be stored and where. I don't want spring to do it for me.
I'm considering creating my own resolver but I cant find information on how to disable spring default behavior.
To quote from API docs StandardServletMultipartResolver does not support temporary file configuration on resolver level rather it is to be done on servlet registration level -
In order to use Servlet 3.0 based multipart parsing, you need to mark the affected servlet with a "multipart-config" section in web.xml, or with a MultipartConfigElement in programmatic servlet registration, or (in case of a custom servlet class) possibly with a MultipartConfig annotation on your servlet class.
Configuration settings such as maximum sizes or storage locations need to be applied at that servlet registration level; Servlet 3.0 does not allow for them to be set at the MultipartResolver level.
So either you can configure it on servlet or switch to CommonsMultipartResolver which has the support to set the temp directory out-of-the-box as it inherits it from CommonsFileUploadSupport.setUploadTempDir (see respective docs here and here)

Spring security - implement oauth with existing FilterChainProxy

we have existing web application built with Spring security 3.1 ,Wink(for rest)
we now need to add oauth2 (client_credentials flow) for several resources, i looked into many examples and all of them using the Http namespace configuration along with spring dispatcher servlet (which we didn't have till now)
problem is that http namespace is creating a springSecurityFilterChain which we already had in the application , so first thing i renamed the existing filter so the default could co-exist with the old one.
but this does not work, its either the existing chain working for requests or the new one.
i have tried the following already
1. disabled dispatcher servlet context by giving empty config location (in web.xml)
2. tried to have the oauth configuration in application-context.xml (right to the existing FilterChainProxy)
3. Allow the /oauth/token in existing chain by setting its filter to none (so the new can take over)
4. tried to declare the oauth filters in the existing chain but there was a problem with its not getting the right clientAuthentication
i really don't know what else to try - so the question is : is it possible to have both declared in the same webapp ? or is it possible to declare oauth2 configuration in the old fashion.
thanks
Shlomi
I managed to do that eventually, having the API (protected with oauth) completey separated url from the rest of the application.
so the Http namespace is creating the springSecurityFilterChain bean and the others just have different bean names. everyone is delegated through the DelegatingProxy in web.xml
i needed to puth the API URL prefix in other chains and allow all requests through , leaving the oauth security chanin to deal with security.
(i.e filter-chain pattern="/api/**" filters="none)
regarding the spring oauth2 bounded to spring MVC so tight i think is not a good implementation.
the mapping of the dispatcher servlet cannot be for /* but have to be something like /auth/*
so a special filter inherit from ClientCredentialsTokenEndpointFilter with special path like super("/auth/oauth/token") was needed.
it also cannot be /api/* since this is the real API URI mapped by our rest framework (wink RestServlet)
so we have something like this
http://server:port/context/auth/oauth/token
http://server:port/context/api/someresource (protected with oauth2)
http://server:port/context/rest/someresource (old rest for application)
Shlomi

JSF2 BackingBean method not being called after Spring Security configuration done

I integrated an webapp that uses JSF 2 with Spring Security 3.2 and Spring 4.0 (compatible, see documentation, and this thread), using annotations, and I have this configuation:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/my-account", "**/myAccount.**").authenticated()
.and()
.formLogin().loginPage("/login").permitAll();
The login page is being showed correctly, but when I submit the username and password, JSF BakcingBean method is never called. I want to process some validations (required fileds, etc) on this method and throw exceptions (required field messages).
If I comment the line that setup my custom login page, the desired method is called.
This article, and this other, are examples of what I'm trying to do. Notice that the methods declared on the managed beans, apparently, are being called.
The question are: am I forgetting some configuration? How to do to Spring let JSF perform my validations, display required fields messages, etc?
People, after searching all this day on the internet I haven't found any example of this being doing using annotations.
I just migrated to XML files and now everything works.
I'm not an expert of Spring, but based in just what I tested, I think that using annotations Spring created that automatic filters that, for some reason, intercepted all the requests coming from the configured custom login form, blocking the JSF from handling the requests. This can be happening because some undocumented incompatibility between "Spring 4" and Spring "Security 3.2". This incompatibility doesn't occur when using XMLs.
If you create the security filters on the classic manual way on the web.xml, and configure your custom login form on Spring XML files, you can use the JSF features on the login form again.
Same security configuration of annotation migrated to XML and it worked.
PS: Sorry, I can't share the detailed files because this time it's not open source.
I answered this in the Spring Security JIRA at SEC-2761, but I'm posting here to help anyone else that stumbles across this issue.
The problem is that Java Configuration defaults the login processing URL to be a POST to the value of the login form. This means since the login page is configured to be loginPage("/login") a POST to /login will be intercepted by Spring Security.
To avoid this problem, you can either
perform a POST to a different URL and have the LoginController process that URL.
configure Spring Security to intercept a different URL using .loginProcessingUrl("/j_spring_security_check")
An example configuration for option 2 can be seen below:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/j_spring_security_check")
...
}
I attached a working sample application to the previously mentioned JIRA. You can download it here.

Storing Form Login Sessions in a Database

A security context on my Spring server uses Spring Security's built-in form login implementation. Currently, login sessions are being stored locally, in memory, by the servlet container. I'd like to replace the way HttpSessions are stored and retrieved with one of my Spring Data Mongo repositories. I looked for one of those "slots" in the Java configuration for session management, but didn't find anything. To be clear, I'm looking for the equivalent of a UserDetailsService, but for sessions.
Here's the relevant snippet of Java configuration in my security configuration class:
...
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.formlogin()
.loginProcessingUrl("/authentication/login")
.successHandler(successHandler)
.usernameParameter("username")
.passwordParameter("password")
.failureUrl("/login?error")
.loginPage("/login")
.and()
.logout()
.logoutUrl("/authentication/logout")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutSuccessUrl("/login")
.and()
...
As far as I can tell, I'm not doing anything particularly strange. It may not be relevant, but I wanted to show precisely what I'm looking at when I say I couldn't find the correct configuration slot.
I looked closely at the source code for the SecurityContextPersistenceFilter, which seems to be the filter in Spring Security's filter chain responsible for retrieving and storing HttpSessions. It delegates session retrieval to a call to getSession() in HttpServletRequest, and session storage to saveContext() in an injected SecurityContextRepository.
In order to correctly replace the default, server-local session storage mechanism, I see three approaches.
Plug in a Spring Data-backed security context repository into the persistence filter. Then, wrap incoming requests to implement custom getSession() behavior that queries the custom security context repository rather than local storage. The built-in persistence filter will then "do the right thing".
Set up an ObjectPostProcessor to replace the default filter with a custom SecurityContextPersistenceFilter that uses my Spring Data repository directly rather than calling getSession() or a using security context repository. I've actually never used an object post processor before, so if that's not what they're meant for, please tell me.
The last option is not one I'm considering, but it's worth mentioning. I think that underneath all the magic, Spring Security really delegates to the servlet container's implementation of session storage. So one way to change the backing store to Mongo would be to use something like Tomcat's Manager interface to customize the session persistence behavior. This is not something I want to do because it becomes quite separate from Spring, I lose the ability to use my services via dependency injection, and it depends completely on the container, making it difficult to change at whim.
I'm sure that gutting out the session storage and replacing it with a database is a fairly common requirement for Spring servers. How is it usually done? If I'm just missing a configuration option, I'd love to see where it's located. Otherwise, suggestions about which route to take (and why) are what I'm looking for.
In Spring 3 the place is in SessionManagement.
Basically you define the session filter and specialise either the session strategy or the session registry.
The session registry is in charge of dealing with session invalidation and creation. In that point you could persist whatever is that you need to persist.
The downside of this approach is that it requires either that you declare the session event publisher in web.xml file or that you handle everything.
An example would be to implement SessionRegistry and SessionAuthenticationStrategy. From there when a user authenticates or a getSession(true) (or invalidate it) is executed it will reach the code and there you can act upon it. Your strategy would have your session registry injected. If a user authenticates through the authentication chain it would reach your strategy, which would pass the session to your registry.
An alternative approach is to add a custom filter of your own. A class extending GenericFilterBean. And then register it:
<security:custom-filter ref="customSessionFilter" after="LAST" />
In this example it would be executed last. This is useful since you could check for an active session or a successfully authenticated user.
An approach similar to your option #3 without relying on container-specific interfaces would be to use Spring Session with a MongoDB-backed SessionRepository implementation.
This would handle persisting all HTTP Session data rather than only the bits specific to Spring-security.

Resources