Spring Security 4 Logout Example
Generally, In your views,
you should be providing a simple logout link to logout a user, something like shown
below:
<%@ page
language="java" contentType="text/html;
charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1"> |
Dear
${user}, Welcome to Admin Page.
Nothing fancy about it. Now , We just need to map this /logout link in our controller. Create a new method
like shown below:
@RequestMapping(value="/logout",
method = RequestMethod.GET)public String
logoutPage (HttpServletRequest request, HttpServletResponse response) { Authentication
auth = SecurityContextHolder.getContext().getAuthentication(); if (auth
!= null){ new SecurityContextLogoutHandler().logout(request,
response, auth); } return "redirect:/login?logout";//You
can redirect wherever you want, but generally it's a good practice to show
login screen again.} |
Here firstly we identified if user was authenticated before
usingSecurityContextHolder.getContext().getAuthentication(). If
he/she was, then we calledSecurityContextLogoutHandler().logout(request,
response, auth) to logout
user properly.
This logout call
performs following:
- Invalidates HTTP
Session ,then unbinds any objects bound to it.
- Removes the
Authentication from the SecurityContext to prevent issues with concurrent
requests.
- Explicitly clears
the context value from the current thread.
That’s
it. You don’t need anything else anywhere in your application to handle logout.
Notice that you don’t even need to do anything special in your spring
configuration(xml or annotation based), shown below just for information:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration@EnableWebSecuritypublic class SecurityConfiguration
extends WebSecurityConfigurerAdapter
{ @Autowired public void configureGlobalSecurity(AuthenticationManagerBuilder
auth) throws Exception { auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("USER"); auth.inMemoryAuthentication().withUser("admin").password("root123").roles("ADMIN"); auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DBA"); } @Override protected void configure(HttpSecurity
http) throws Exception { http.authorizeRequests() .antMatchers("/",
"/home").permitAll() .antMatchers("/admin/**").access("hasRole('ADMIN')") .antMatchers("/db/**").access("hasRole('ADMIN')
and hasRole('DBA')") .and().formLogin().loginPage("/login") .usernameParameter("ssoId").passwordParameter("password") .and().exceptionHandling().accessDeniedPage("/Access_Denied"); }} |
There is
no special logout handling mentioned in above configuration.
Above security configuration in
XML configuration format would be:
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd"> auto-config="true" > pattern="/" access="hasRole('USER')" /> pattern="/home" access="hasRole('USER')" /> pattern="/admin**" access="hasRole('ADMIN')" /> pattern="/dba**" access="hasRole('ADMIN')
and hasRole('DBA')" /> login-page="/login" username-parameter="ssoId" password-parameter="password" authentication-failure-url="/Access_Denied" /> |
> name="bill" password="abc123" authorities="ROLE_USER" /> name="admin" password="root123" authorities="ROLE_ADMIN" /> name="dba" password="root123" authorities="ROLE_ADMIN,ROLE_DBA" />
Rest of
application code is same as mentioned in every post in this series
Following
technologies being used:
- Spring 4.1.6.RELEASE
- Spring Security 4.0.1.RELEASE
- Maven 3
- JDK 1.7
- Tomcat 7
- STS
Let’s begin.
1.
Create a Spring MVC
project using STS
2.
Delete the web.xml as we will be doing java Configuration
3.
Delete the spring folder from WEB-INF
4.
Update pom.xml
·
First thing to notice here is the
First thing to notice here is the maven-war-plugin declaration. As we are using full
annotation configuration, we
don’t even use web.xml, so we will need to configure this plugin in order to
avoid maven failure to build war package. We are using latest versions(at time
of writing) of Spring and Spring Security.
·
Along with that, we have also included JSP/Servlet/Jstl
dependencies which we will be needing as we are going to use servlet api’s and
jstl view in our code. In general, containers might already contains these
libraries, so we can set the scope as ‘provided’ for them in pom.xml.
5.
Following is the project structure

6.
Let’s now add the content mentioned in above structure
explaining each in detail.
7.
Create com.sbk.logout.configuratio.SecurityConfiguration class
8. package
com.sbk.logout.configuration;
9.
10. import
org.springframework.beans.factory.annotation.Autowired;
11. import
org.springframework.context.annotation.Configuration;
12. import
org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
13. import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
14. import
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
15. import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
16.
17. @Configuration
18. @EnableWebSecurity
19. public
class SecurityConfiguration extends WebSecurityConfigurerAdapter {
20.
21.
22. @Autowired
23. public void
configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
24. auth.inMemoryAuthentication().withUser("smita").password("password").roles("USER");
25. auth.inMemoryAuthentication().withUser("admin").password("password").roles("ADMIN");
26. auth.inMemoryAuthentication().withUser("dba").password("password").roles("ADMIN","DBA");
27. }
28.
29. @Override
30. protected void configure(HttpSecurity http)
throws Exception {
31.
32. http.authorizeRequests()
33. .antMatchers("/",
"/home").permitAll()
34. .antMatchers("/admin/**").access("hasRole('ADMIN')")
35. .antMatchers("/db/**").access("hasRole('ADMIN')
and hasRole('DBA')")
36. .and().formLogin().loginPage("/login")
37. .usernameParameter("ssoId").passwordParameter("password")
38. .and().exceptionHandling().accessDeniedPage("/Access_Denied");
39. }
40. }
41.
42.
43. Register the
springSecurityFilter with war
com.sbk.logout.configuration.SecurityWebApplicationInitializer
Below specified initializer class registers the springSecurityFilter [created in previous step] with application
war.
package com.sbk.logout.configuration;
import
org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class
SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
}
|
package com.websystique.springsecurity.configuration;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
|
Above setup in XML
configuration format would be(In web.xml):
|
|
44.
Add Controller com.sbk.logout.controller
package
com.sbk.logout.controller;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.springframework.security.core.Authentication;
import
org.springframework.security.core.context.SecurityContextHolder;
import
org.springframework.security.core.userdetails.UserDetails;
import
org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import
org.springframework.stereotype.Controller;
import
org.springframework.ui.ModelMap;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HelloWorldController
{
@RequestMapping(value = { "/",
"/home" }, method = RequestMethod.GET)
public String homePage(ModelMap model) {
model.addAttribute("greeting",
"Hi, Welcome to mysite");
return "welcome";
}
@RequestMapping(value = "/admin",
method = RequestMethod.GET)
public String adminPage(ModelMap model) {
model.addAttribute("user",
getPrincipal());
return "admin";
}
@RequestMapping(value = "/db",
method = RequestMethod.GET)
public String dbaPage(ModelMap model) {
model.addAttribute("user",
getPrincipal());
return "dba";
}
@RequestMapping(value =
"/Access_Denied", method = RequestMethod.GET)
public String accessDeniedPage(ModelMap
model) {
model.addAttribute("user",
getPrincipal());
return "accessDenied";
}
@RequestMapping(value = "/login",
method = RequestMethod.GET)
public String loginPage() {
return "login";
}
@RequestMapping(value="/logout",
method = RequestMethod.GET)
public String logoutPage
(HttpServletRequest request, HttpServletResponse response) {
Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new
SecurityContextLogoutHandler().logout(request, response, auth);
}
return
"redirect:/login?logout";//You can redirect wherever you want, but
generally it's a good idea to show login screen again.
}
private String getPrincipal(){
String userName = null;
Object principal =
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails)
{
userName =
((UserDetails)principal).getUsername();
} else {
userName = principal.toString();
}
return userName;
}
}
45. Add
SpringMVC Configuration Class
com.sbk.logout.configuration
package
com.sbk.logout.configuration;
import
org.springframework.context.annotation.Bean;
import
org.springframework.context.annotation.ComponentScan;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.web.servlet.ViewResolver;
import
org.springframework.web.servlet.config.annotation.EnableWebMvc;
import
org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import
org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import
org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages
= "com.sbk.logout")
public class
HelloWorldConfiguration extends WebMvcConfigurerAdapter {
@Bean(name="HelloWorld")
public ViewResolver viewResolver() {
InternalResourceViewResolver
viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/*
* Configure ResourceHandlers to serve
static resources like CSS/ Javascript etc...
*/
@Override
public void
addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
46. Add
Initializer class(This class is
exactly same as in previous post.)
com.sbk.logout.configuration
package
com.sbk.logout.configuration;
import
org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class
SpringMvcInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class[]
getRootConfigClasses() {
return new Class[] {
HelloWorldConfiguration.class };
}
@Override
protected Class[] getServletConfigClasses()
{
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/"
};
}
}
Notice that above
initializer class extends
AbstractAnnotationConfigDispatcherServletInitializerwhich is the base class for all WebApplicationInitializer implementations. Implementations of
WebApplicationInitializer configures ServletContext programatically, for
Servlet 3.0 environments. It means we won’t be using web.xml and we will deploy
the app on Servlet 3.0 container.
47. Add Views

<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> AccessDenied
page Dear
${user}, You are not authorized to access this
page
<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%><%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core"%> Admin
page Dear
${user}, Welcome to Admin Page.
<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%><%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core"%> DBA
page Dear
${user}, Welcome to DBA Page.
<%@ page language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%> Welcome
page Greeting
: ${greeting} This
is a welcome page.login.jsp
<%@ page
language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib
prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
Invalid
username and password.
You have been
logged out successfully.
class="btn
btn-block btn-primary btn-default" value="Log in">

login.jsp additionally
contains CSS for login panel layout.
Notice the CSRF (Cross
Site Request Forgery)related
line in above jsp:
type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> |
48. Build and
Deploy the application
NOTE : Before deploying, check the
java build path(1.7), Check java
compiler(1.7), java facets(jdk1.7,web module version 3.0), targeted runtime(tom
cat7) and Add Maven dependency to build path
49. Run the
application(http://localhost:8087/logout/)

Now try to access admin page
on http://localhost:8087/logout/admin, you will be prompted for login.

Provide credentials of a ‘USER’ role.enter
[smita,password]

Submit, you will see AccessDenied Page

50.
Logout. http://localhost:8087/logout/login?logout

click on browser back button, you will
remain at login screen.

No comments:
Post a Comment