I have used one of the several approaches for solving issue with redirecting to login page when ajax or non-ajax event occurred and session was expired: https://gist.github.com/banterCZ/5160269
During testing ajax case in debug mode, I have performed following steps:
- Open two tabs in my browser.
- Perform login.
- The first page is displayed data table with several action buttons.
- The second page is displayed log out button.
- So, when I press log out button, I am going into the JsfRedirectStrategy class into else statement:
Bean:
@Component
public class JsfRedirectStrategy implements InvalidSessionStrategy {
final static Logger logger = LoggerFactory.getLogger(JsfRedirectStrategy.class);
private static final String FACES_REQUEST_HEADER = "faces-request";
private String invalidSessionUrl = "/login.xhtml";
@Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
boolean ajaxRedirect = "partial/ajax".equals(request.getHeader(FACES_REQUEST_HEADER));
if (ajaxRedirect) {
String contextPath = request.getContextPath();
String redirectUrl = contextPath + invalidSessionUrl;
logger.debug("Session expired due to ajax request, redirecting to '{}'", redirectUrl);
String ajaxRedirectXml = createAjaxRedirectXml(redirectUrl);
logger.debug("Ajax partial response to redirect: {}", ajaxRedirectXml);
response.setContentType("text/xml");
response.getWriter().write(ajaxRedirectXml);
} else {
String requestURI = getRequestUrl(request);
logger.info(
"Session expired due to non-ajax request, starting a new session and redirect to requested url '{}'",
requestURI);
request.getSession(true);
response.sendRedirect(requestURI);
}
}
private String getRequestUrl(HttpServletRequest request) {
StringBuffer requestURL = request.getRequestURL();
String queryString = request.getQueryString();
if (StringUtils.hasText(queryString)) {
requestURL.append("?").append(queryString);
}
return requestURL.toString();
}
private String createAjaxRedirectXml(String redirectUrl) {
return new StringBuilder().append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.append("<partial-response><redirect url=\"")
.append(redirectUrl)
.append("\"></redirect></partial-response>").toString();
}
}
and the following message is printed: Session expired due to non-ajax request, starting a new session and redirect to requested url
So, new session was opened, even user was logged out.
6. Then I am going to the first page and press one of the action button to trigger ajax event, but nothing happened (no action.)
the following message is printed: o.s.b.a.audit.listener.AuditListener - AuditEvent [timestamp=Tue Nov 03 17:44:39 EET 2015, principal=anonymousUser, type=AUTHORIZATION_FAILURE, data={type=org.springframework.security.access.AccessDeniedException, message=Access is denied}]
I suppose that reason of such behavior is executing this sample of code at step 5:
request.getSession(true);
From my point of view, the correct behavior would be if result of step 6 would be redirecting to the loging page.
So, please tell me how to achieve this?
My Spring Security config is:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/invite.xhtml").permitAll()
.antMatchers("/login.xhtml").permitAll()
.antMatchers("/forgotpassword.xhtml").permitAll()
.antMatchers("/resetpwd.xhtml").permitAll()
.antMatchers("/admin/*").hasRole(Roles.ROLE_ADMIN.getSpringSecName())
.antMatchers("/**").authenticated()
.antMatchers("/actuator/*").permitAll()
.and()
.formLogin()
.loginPage("/login.xhtml").permitAll()
//.failureUrl("/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher( new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login.xhtml")
.permitAll()
.and()
.exceptionHandling().authenticationEntryPoint(new AjaxAwareAuthenticationEntryPoint("/login.xhtml"));
http.headers().frameOptions().disable();
http.addFilterBefore(sessionManagementFilter(), FilterSecurityInterceptor.class);
// @formatter:on
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/javax.faces.resource/**");
}
@Bean
public HttpSessionSecurityContextRepository httpSessionSecurityContextRepository() {
return new HttpSessionSecurityContextRepository();
}
@Bean
public JsfRedirectStrategy jsfRedirectStrategy() {
return new JsfRedirectStrategy();
}
@Bean
public SessionManagementFilter sessionManagementFilter() {
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
httpSessionSecurityContextRepository());
sessionManagementFilter.setInvalidSessionStrategy(jsfRedirectStrategy());
return sessionManagementFilter;
}
}