What happen if there are too many session in the spring-mvc + tomcat application - spring

I am having a project using spring-mvc + tomcat. It has a keycloak adapator and a CMS systems which use the traditional session to manage the user login,
i.e. the spring-mvc checks if the session has permission, and if not it will redirect to keycloak login page.
I need to write some new routes (RESTful, using #RestController) for their new mobile app. These api will accept the access_token and return data if they have permission/ the token is valid.
Because this backend needs to also support the old CMS system, so I can't set the spring to stateless or disable the session usage.
As I am not in controll of who is using these new RESTful API, some api users are just calling these api without passing the session cookies, so that the backend makes a new session for them every time they called (these api will be called very frequently to update the data, say 30 every mins)
So, will the server having memory usage problem if there are too many sessions? I know that the default of session timeout should be 30mins, is this timeout enough?. I have done a lot of searching but seems no one talks about this

Each session will consume some memory, to the total needed memory for sessions is number of sessions (in parallel) x size per session. - I know this is not helpful, so the help full part comes next.
If you have many (huge) sessions, Tomcat can persist them on disk, instead of hold them in memory. You just need to configure an other Manger Implementation for sessions: switch to org.apache.catalina.session.PersistentManager and you need to configure the idle parameters: https://tomcat.apache.org/tomcat-9.0-doc/config/manager.html
Important: all the stuff that is stored in your session must be Serializable!.

I finally figured out a way to actually enables sessions in routes for web cms while disabling sessions on RESTful routes, i will post it here
you can define a MultiHttpSecurityConfig
#Configuration
public class MultiHttpSecurityConfig{
#KeycloakConfiguration
#Order(1)
public class SecurityConfig1 extends KeycloakWebSecurityConfigurerAdapter
{
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
/**
* Defines the session authentication strategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http.antMatcher("/api/v1/external/**") // these routes disabled session
.authorizeRequests()
.anyRequest()
.permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
#KeycloakConfiguration
#Order(2)
public class SecurityConfig2 extends KeycloakWebSecurityConfigurerAdapter
{
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
/**
* Defines the session authentication strategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(buildSessionRegistry());
}
#Bean
protected SessionRegistry buildSessionRegistry() {
return new SessionRegistryImpl();
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.antMatcher("/")
.authorizeRequests()
.anyRequest().permitAll();
}
}
}

Related

Spring security - Specific session creation policy per matchers

I'm trying to implement SessionCreationPolicy.ALWAYS for the /testMVCController/** endpoint and SessionCreationPolicy.STATELESS for rest of endpoints (/**).
Expected scenario:
When accessing to /testMVCController/displayUsers the user logs in once and the log I have implemented in UserDetailsService logs the authorities associated to that user.
After that, all the requests to /testMVCController/displayUsers or other URL under /testMVCController/** will not log the authorities again because the session creation policy is always and the user is already logged in.
This works when I don't specify the 2nd security configuration (X509ClientSessionCreationPolicyStateless) but when I add it, all the requests become session stateless.
It is not working with the current security configuration because after I log in with my client certificate, at any request executed under /testMVCController/** endpoint (e.g. /testMVCController/displayUsers), the authenticationUserDetailsService is consulted and the list of authorities is logged for each file request the browser makes (.js file, .css files, ...), even after the initial login.
So, if there are 3 requests (/testMVCController/displayUsers, displayUsers.js, displayUsers.css) the list of authorities log present in authenticationUserDetailsService is logged 3 times.
I configured SecurityConfiguration as shown below but it is not working:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class X509ClientSessionCreationPolicyAlways extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService x509CUDService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/testMVCController/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(x509CUDService)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
}
}
#Configuration
#Order(2)
public static class X509ClientSessionCreationPolicyStateless extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService X509CUDService ;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(X509CUDService);
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
}
I've searched this issue and I found various links (e.g. Spring session creation policy per-request?, Spring Session: How to create separate session management policies for different URLs and Multiple HttpSecurity) but none of them worked.
Thanks in advance!
I was missing some details on my configuration. I was catching all the requests to /testMVCController/** and that was working, but in addition to catch the requests to any endpoint of the type /testMVCController/** (e.g.: /testMVCController/usersList), I also have to catch the requests that these pages make to get their scripts (.js files, .css files, .png files).
What was happening was: the request to /testMVCController/usersList), was configured with SessionCreationPolicy.ALWAYS, but the subsequent requests such as usersList.js, usersList.css, etc were configured with SessionCreationPolicy.STATELESS, and in these cases the X509CustomUserDetailsService was always consulted.
Example:
GET request to /testMVCController/usersList works, but there also requests in this usersList page to usersList.js, usersList.css, etc.
So, once I included these resource paths in the antMatchers all worked perfectly.

Disable multiple login with same user in spring boot and OAuth2 application

I have microservice architecture application working with zuul api-gateway added with Oauth2 security feature. Now, I can able to login with same user in multiple session(I mean multiple browser and multiple machine). So I want to restrict multiple login of same user.
I used below code to restrict same user login. This code works perfectly when I'm doing oauth logout. But I'm facing problem when user logged in and close their browser or clear their browser cookies.
static SessionRegistry sessionRegistry;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable().authorizeRequests().antMatchers("/login", "/logout").permitAll().anyRequest()
.authenticated().and().formLogin().loginPage("/login")
.failureHandler(loginAuthenticationFailureHandler).permitAll().and().logout().and().authorizeRequests()
.anyRequest().authenticated()
.and()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry);
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
Can any one help me how to implement this single user session logout when browser close and cookie clear or is there any separate procedure to develop this functionality.
I tried to make an implementation using this same approach to use the sessionManagement configuration, but it didn't work for me, in my case I was just needing to remove the multiple login, make either the new login go off or the previous login, do this with an extension of InMemoryTokenStore.
#Component
public class ResetTokenStore extends InMemoryTokenStore {
#Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
OAuth2AccessToken accessToken = super.getAccessToken(authentication);
if(accessToken != null) {
removeAccessToken(accessToken);
removeRefreshToken(accessToken.getRefreshToken());
}
return null;
}
}
Basically what I do is force the token renewal, every time a new token is generated and logged in, and the previously generated accesstoken and refreshtoken are deleted.
In the class that extends AuthorizationServerConfigurerAdapter:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.reuseRefreshTokens(false)
.userDetailsService(userDetailsService)
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new ResetTokenStore();
}

Spring Security with OAuth2 and anonymous access

I have my Spring REST API secured with Spring Security and OAuth2, I can successfully retrieve a token and access my APIs. My App defines the OAuth2 client itsself.
Now I want users to have anonymous access on some resources. The use case is really simple: I want my app to be usable without login - but if they are logged in, I want to have access to that principal.
Here is my WebSecurityConfigurerAdapter so far:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api1").anonymous().and()
.authorizeRequests().antMatchers("/ap2**").permitAll();
}
As soon as I add a second antMatcher/anonymous, it fails to work though, and it doesn't really express my intent either - e.g. I wan't to have anonymous access on api1 GETs, but authenticated on POSTs (easy to do with #PreAuthorize).
How can I make the OAuth2 authentication optional?
I dropped my #EnableWebSecurity and used a ResourceServerConfigurerAdapter like so:
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/api1", "/api/api2").permitAll()
.and().authorizeRequests()
.anyRequest().authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("my-resource-id");
}
}
/api/api1 may now be called with or without authentication.

Spring Security "Remember Me" redirection to login

I'm trying to implement remember me functionallity in my webapp with Spring Security. The cookie is established correctly (I've seen it in browser) and recognized by the server (my app finds the user in bbdd) but always I am being redirected to login page. Does Anybody can help me?
My Security configuration is:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
RememberMeAuthenticationProvider rememberMeAuthenticationProvider = new RememberMeAuthenticationProvider(
"OTRS_KEY");
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.debug(true)
.ignoring()
.antMatchers("/unsec/**", "/resources/**", "/css/**",
"/images/**", "/design/**", "/javax.faces.resource/**",
"/syntaxhighlighter/**");
}
/*
* JSF 1.2/2.0/2.1/2.2 has implicit CSRF protection when h:form is submitted
* with a POST request. This is because the javax.faces.ViewState hidden
* field contains a sufficiently random token. JSF 2.2 adds CSRF protection
* to HTTP GET by allowing the developer to specify protected-views in the
* WEB-INF/faces-config.xml descriptor. URLs that invoke the JSF lifecycle
* via HTTP GET must have the new javax.faces.Token URL parameter. For more
* information, see the tutorial titled Java EE 7: Implementing CSRF
* Protection with JSF 2.2.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().headers().frameOptions().disable().formLogin()
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginPage("/unsec/secureLogin.jsf")
.defaultSuccessUrl("/sec/home.jsf")
.failureUrl("/unsec/secureLogin.jsf").and().rememberMe()
.rememberMeServices(tokenBasedRememberMeServices()).and()
.authorizeRequests().antMatchers("/unsec/**").permitAll()
.antMatchers("/sec/**").authenticated().and().logout()
.deleteCookies("JSESSIONID", "OTRS_REMEMBER");
}
#Bean
public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
return NoOpPasswordEncoder.getInstance();
}
private TokenBasedRememberMeServices tokenBasedRememberMeServices() {
TokenBasedRememberMeServices t = new TokenBasedRememberMeServices(
"OTRS_KEY", satecAuthenticationProvider.getIdentitiesService());
t.setParameter("_spring_security_remember_me_input");
t.setAlwaysRemember(true);
t.setCookieName("OTRS_REMEMBER");
t.setTokenValiditySeconds(7200);
return t;
}
}

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

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".

Resources