Session expiration management with Spring Session for a single page app - spring-session

I use Spring Session and I am having issues with session management especially dealing with session expiration.
The idea is to return a custom Http Header to the client e.g. X-Application-Session-Is-New if the session has expired.
Here is what I came up with:
public class SessionDestroyedFilter extends OncePerRequestFilter {
//TODO: not always invoked!!
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (isAjaxRequest(request) && (isRequestedSessionInvalid(request) || isSessionNew(request))) {
response.addHeader("X-Application-Session-Is-New", "true");
}
filterChain.doFilter(request, response);
}
private boolean isRequestedSessionInvalid(HttpServletRequest request) {
return !request.isRequestedSessionIdValid();
}
private boolean isSessionNew(HttpServletRequest request) {
return request.getSession(false).isNew();
}
private boolean isAjaxRequest(HttpServletRequest request) {
return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
}
The issue is that my filter does not seem to be always invoked upon session expiration perhaps because the request is not an ajax request and a new session ID is immediately created after that.
Can anyone please point me to an appropriate strategy to deal with session expiration on single page apps?

EDIT Following is currently not possible (instead do decoration)
Here are some related github issues (comment on them to fix them faster)
https://github.com/spring-projects/spring-session/issues/243
https://github.com/spring-projects/spring-session/issues/112
To accomplish this you have to use your own HttpSessionStrategy.
Here is an example if you are using CookieHttpSessionStrategy (the default one)
public class CustomHttpSessionStrategy extends CookieHttpSessionStrategy {
#Override
public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
super.onInvalidateSession(request, response);
response.addHeader("X-Application-Session-Is-New", "true");
}
}
If you want to add your header on new sessions also consider overriding onNewSession(Session, HttpServletRequest, HttpServletResponse).

This is Wrong
Please see my other answer
You should move your logic for adding X-Application-Session-Is-New after request is processed.
Try something like
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
if (isAjaxRequest(request) && (isRequestedSessionInvalid(request) || isSessionNew(request))) {
response.addHeader("X-Application-Session-Is-New", "true");
}
}

Related

Spring Security SAML2, sending language code in <Extensions> -element

I'm currently migrating from Spring Security SAML Extension to Spring Security SAML2 and use case requires language code to be sent in Extensions -element.
With Spring Security SAML Extension this was done by:
Extending SAMLEntryPoint and storing locale as relayState to SAMLMessageContext like this:
public class CustomSAMLEntryPoint extends SAMLEntryPoint {
private String relayState;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
//read your request parameter
setRelayState(request.getParameter("locale"));
super.commence(request, response, authenticationException);
}
#Override
protected WebSSOProfileOptions getProfileOptions(SAMLMessageContext samlMessageContext, AuthenticationException authenticationException) throws MetadataProviderException {
//set the relayState to your SAML message context
samlMessageContext.setRelayState(getRelayState());
return super.getProfileOptions(samlMessageContext, authenticationException);
}
private void setRelayState(String relayState) {
this.relayState = relayState;
}
private String getRelayState() {
return relayState;
}
}
Extending WebSSOProfileImpl and using previously set relayState value to generate Extensions -element:
public class CustomWebSSOProfileImpl extends WebSSOProfileImpl {
#Override
protected AuthnRequest getAuthnRequest(SAMLMessageContext context, WebSSOProfileOptions options, AssertionConsumerService assertionConsumer, SingleSignOnService bindingService) throws SAMLException, MetadataProviderException {
AuthnRequest authnRequest = super.getAuthnRequest(context, options, assertionConsumer, bindingService);
authnRequest.setExtensions(buildExtensions(context.getRelayState()));
return authnRequest;
}
}
How could this same functionality be done with Spring Security Core SAML2? Is there some similar way than using SAMLMessageContext and relayState?
I could customize AuthenticationEntryPoint as well as authentication request creation but there seems to be no way to move locale between these two.
public AuthenticationEntryPoint authenticationEntryPoint() {
final AuthenticationEntryPoint authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(
"/saml2/authenticate/sp");
return (request, response, exception) -> {
String locale = request.getParameter("locale");
// Where shoud locale be stored???
authenticationEntryPoint.commence(request, response, exception);
};
}
#Bean
public Saml2AuthenticationRequestFactory authenticationRequestFactory() {
final OpenSamlAuthenticationRequestFactory authenticationRequestFactory = new OpenSamlAuthenticationRequestFactory();
authenticationRequestFactory.setAuthenticationRequestContextConverter(context -> {
final AuthnRequest request = new AuthnRequestBuilder().buildObject();
request.setAssertionConsumerServiceURL(context.getAssertionConsumerServiceUrl());
request.setDestination(context.getDestination());
request.setID("A" + UUID.randomUUID());
request.setIssueInstant(new DateTime());
final Issuer issuer = new IssuerBuilder().buildObject();
issuer.setValue(context.getIssuer());
request.setIssuer(issuer);
// Where can locale be read from???
request.setExtensions(buildLanguageExtensions(???);
return request;
});
return authenticationRequestFactory;
}

Grails Spring Security Shiro, how to create 2 login forms with 2 different authentication success urls?

I am working a project that leverages Spring Security Shiro. I would like to configure the application to have 2 different login forms with 2 different authentication success urls. What is the best way to perform this? I looked at the documentation but have not seen any solutions.
Thank you in advance for your help.
The easiest and the best way it's extending two separate WebSecurityConfigurerAdapter and creating there two different configuration (two login page and two authentication success urls):
#Configuration
#Order(94)
public class WebSecurityConf1 extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/first-resources**").formLogin().successForwardUrl();
}
}
and
#Configuration
#Order(95)
public class WebSecurityConf2 extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/second-resources**").formLogin().successForwardUrl();
}
}
Note that WebSecurityConf1 will be applied when .antMatcher("/first-resources**") is satisfied and the same for WebSecurityConf2. Also, configuration between WebSecurityConf1 and WebSecurityConf2 independent.
The simple way was to override the AuthenticationSuccessHandler with a custom one.
First create your success handler, my checks to see if the user is an admin user.
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler
import org.springframework.security.core.Authentication
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpSession
class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
def requestCache
boolean administrator = false
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
println administrator
if(administrator){
return "/admin"
} else {
return super.determineTargetUrl(request, response)
}
}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authentication) throws ServletException, IOException {
try {
checkSetAdministratorUser(authentication)
handle(request, response, authentication)
super.clearAuthenticationAttributes(request)
}catch(Exception e){
e.printStackTrace()
} finally {
// always remove the saved request
requestCache.removeRequest(request, response)
}
}
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String targetUrl = determineTargetUrl(request, response)
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to " + targetUrl)
return
}
redirectStrategy.sendRedirect(request, response, targetUrl)
}
def checkSetAdministratorUser(authentication){
authentication.authorities.each(){ authority ->
if(authority.authority == "ROLE_ADMIN")administrator = true
}
}
}
Then I had to define the success handler in the beans section of resources.groovy
beans = {
authenticationSuccessHandler(CustomAuthenticationSuccessHandler) {
requestCache = ref('requestCache')
redirectStrategy = ref('redirectStrategy')
}
}
Then I was good to go. It worked well for my scenario.
Thanks: https://groggyman.com/2015/04/05/custom-authentication-success-handler-with-grails-and-spring-security/

Dropwizard url rewriting

How do I enable URL rewriting in dropwizard? I need to rewrite all urls matching a certain pattern to /.
I'm using dropwizard 0.7.1 and I'm trying to run an Angular.js app in html5 mode in the front-end and this requires url rewriting (see https://docs.angularjs.org/guide/$location under the Server side section).
you can try with this: http://www.tuckey.org/urlrewrite/
it's very symilar to mod_rewrite.
You could add a Filter like this:
environment.getApplicationContext().addFilter(
new FilterHolder(new Filter() {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (shouldRedirect(request.getRequestURI())) {
response.sendRedirect("/");
} else {
chain.doFilter(req, res);
}
}
#Override
public void destroy() {}
}), "/*", EnumSet.allOf(DispatcherType.class));
For simple rules it seems to me that the best option is to use the Dropwizard PathRedirect RedirectBundle.
Add dropwizard-redirect-bundle to your dependencies
Register the bundle:
#Override
public void initialize(Bootstrap<?> bootstrap) {
bootstrap.addBundle(new RedirectBundle(
new PathRedirect("/old", "/new")
));
}
It supports regular expressions as well.
Just a note that for this to work with other bundles, such as static assets and HTTPS redirect, order matters. It should be:
AssetsBundle first
HttpsRedirect second
PathRedirect last

Grails/SpringSecurity: Getting locale in LogoutHandler

In a grails app, I'm successfully getting the language the user has selected(added to url, "...?lang=xx_XX") like this:
def locale = RequestContextUtils.getLocale(request)
Using springsecurity, and got a special logout handler set up that works fine
grails.plugins.springsecurity.logout.handlerNames = ['securityContextLogoutHandler', 'myLogoutHandler']
I need to get the user selected locale in myLogoutHandler, but the following is not working (it only shows the browser default locale, not the one selected by the user)
class MyLogoutHandler implements LogoutHandler {
void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
def locale2 = RequestContextUtils.getLocale(httpServletRequest);
}
}
I have also tried getting the session locale with:
RequestContextHolder.currentRequestAttributes().getProperties()
but that gives the same result, anyone got an idea how to get the locale from the MyLogoutHandler?
Workaround
The session appears to be cleared by spring-security but you can still send in parameters.
so in the controller for the logout page I got:
def index = {
def myParam = "bar"
redirect(uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl + "?foo=" + myParam) // '/j_spring_security_logout'
}
And I just get the parameter in the LogoutHandler:
void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
def myParam = httpServletRequest.parameterMap.get("foo")[0]
....
}
I can get the locales by using the code below:
class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
private static final ThreadLocal<Authentication> AUTH_HOLDER = new ThreadLocal<Authentication>()
void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
AUTH_HOLDER.set authentication
// reading locales...
request.locales.each {locale ->println locale.toString()}
try {
super.handle(request, response, authentication)
}
finally {
AUTH_HOLDER.remove()
}
}
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = AUTH_HOLDER.get()
String url = super.determineTargetUrl(request, response)
// do something with the url based on session data..
url
}
}

JSF page redirect

We have a JSF2.0 application deployed in weblogic-10.3.4 , we have a requirement to give a user generic url ,say (http://web/apply?7777 ) . When user access this page ,based on query string value , user will be re-directed to client specific page,which can be one of 10 different pages.
So one approach is to have a apply.jsf page ,which has got a pre-render event ,which will re-direct the user to different page based on query string,
Is there any other better approach? not to have apply.xhtml.
Note: In web.xml ,we defined pageNotFound.xhtml in case if the page is not found.
You could use a simple servlet filter for this.
#WebFilter("/apply")
public class ApplyFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String queryString = request.getQueryString();
String redirectURL = determineItBasedOnQueryString(queryString);
if (redirectURL != null) {
response.sendRedirect(redirectURL);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
// ...
}

Resources