Getting Started with Spring Security

Exclusive offer: get 50% off this eBook here
Spring Security 3.1

Spring Security 3.1 — Save 50%

Secure your web applications from hackers with this step-by-step guide with this book and ebook.

$29.99    $15.00
by Peter Mularien Robert Winch | March 2013 | Java Open Source

In this article, we'll apply a minimal Spring Security configuration to start addressing the inadvertent privilege escalation due to lack of URL protection and general authentication. We will then build on the basic configuration to provide a customized experience for our users.

This article by Robert Winch, author of Spring Security 3.1 , will get you up and running with Spring Security and will provide you with a foundation for any other security-related tasks you will need to perform.

During the course of this article, we will:

  • Implement a basic level of security on the JBCP Calendar application, using Spring Security's automatic configuration option

  • Learn how to customize both the login and logout experience

  • Configure Spring Security to restrict access differently, depending upon the URL

  • Leverage Spring Security's expression-based access control

  • Conditionally display basic information about the logged-in user using Spring Security's JSP library

  • Determine the user's default location after login, based upon role

(For more resources related to this topic, see here.)

Hello Spring Security

Although Spring Security can be extremely difficult to configure, the creators of the product have been thoughtful and have provided us with a very simple mechanism to enable much of the software's functionality with a strong baseline. From this baseline, additional configuration will allow a fine level of detailed control over the security behavior of our application.

We'll start with an unsecured calendar application, and turn it into a site that's secured with rudimentary username and password authentication. This authentication serves merely to illustrate the steps involved in enabling Spring Security for our web application; you'll see that there are some obvious flaws in this approach that will lead us to make further configuration refinements.

Updating your dependencies

The first step is to update the project's dependencies to include the necessary Spring Security .jar files. Update the Maven pom.xml file from the sample application you imported previously, to include the Spring Security .jar files that we will use in the following few sections.

 Remember that Maven will download the transitive dependencies for each listed dependency. So, if you are using another mechanism to manage dependencies, ensure that you also include the transitive dependencies. When managing the dependencies manually, it is useful to know that the Spring Security reference includes a list of its transitive dependencies.

pom.xml <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>3.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.1.0.RELEASE</version> </dependency>

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com.If you purchased this book elsewhere, you can visit http://www.PacktPub. com/support. and register to have the files e-mailed directly to you.

Using Spring 3.1 and Spring Security 3.1

It is important to ensure that all of the Spring dependency versions match and all the Spring Security versions match; this includes transitive versions. Since Spring Security 3.1 builds with Spring 3.0, Maven will attempt to bring in Spring 3.0 dependencies. This means, in order to use Spring 3.1, you must ensure to explicitly list the Spring 3.1 dependencies or use Maven's dependency management features, to ensure that Spring 3.1 is used consistently. Our sample applications provide an example of the former option, which means that no additional work is required by you.

In the following code, we present an example fragment of what is added to the Maven pom.xml file to utilize Maven's dependency management feature, to ensure that Spring 3.1 is used throughout the entire application:

<project ...> ... <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.1.0.RELEASE</version> </dependency> … list all Spring dependencies (a list can be found in our sample application's pom.xml ... <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.1.0.RELEASE</version> </dependency> </dependencies> </dependencyManagement> </project>

If you are using Spring Tool Suite, any time you update the pom.xml file, ensure you right-click on the project and navigate to Maven | Update Project…, and select OK, to update all the dependencies.

Implementing a Spring Security XML configuration file

The next step in the configuration process is to create an XML configuration file, representing all Spring Security components required to cover standard web requests.Create a new XML file in the src/main/webapp/WEB-INF/spring/ directory with the name security.xml and the following contents. Among other things, the following file demonstrates how to require a user to log in for every page in our application, provide a login page, authenticate the user, and require the logged-in user to be associated to ROLE_USER for every URL:URL element:

src/main/webapp/WEB-INF/spring/security.xml <?xml version="1.0" encoding="UTF-8"?> <bean:beans xmlns:bean="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security- 3.1.xsd"> <http auto-config="true"> <intercept-url pattern="/**" access="ROLE_USER"/> </http> <authentication-manager> <authentication-provider> <user-service> <user name="user1@example.com" password="user1" authorities="ROLE_USER"/> </user-service> </authentication-provider> </authentication-manager> </bean:beans>

If you are using Spring Tool Suite, you can easily create Spring configuration files by using File | New Spring Bean Configuration File. This wizard allows you to select the XML namespaces you wish to use, making configuration easier by not requiring the developer to remember the namespace locations and helping prevent typographical errors. You will need to manually change the schema definitions as illustrated in the preceding code.

This is the only Spring Security configuration required to get our web application secured with a minimal standard configuration. This style of configuration, using a Spring Security-specific XML dialect, is known as the security namespace style, named after the XML namespace (http://www.springframework.org/schema/security) associated with the XML configuration elements.

Let's take a minute to break this configuration apart, so we can get a high-level idea of what is happening. The <http> element creates a servlet filter, which ensures that the currently logged-in user is associated to the appropriate role. In this instance, the filter will ensure that the user is associated with ROLE_USER. It is important to understand that the name of the role is arbitrary. Later, we will create a user with ROLE_ADMIN and will allow this user to have access to additional URLs that our current user does not have access to.

The <authentication-manager> element is how Spring Security authenticates the user. In this instance, we utilize an in-memory data store to compare a username and password.

Our example and explanation of what is happening are a bit contrived. An inmemory authentication store would not work for a production environment. However, it allows us to get up and running quickly. We will incrementally improve our understanding of Spring Security as we update our application to use production quality security .

Users who dislike Spring's XML configuration will be disappointed to learn that there isn't an alternative annotation-based or Java-based configuration mechanism for Spring Security, as there is with Spring Framework.

There is an experimental approach that uses Scala to configure Spring Security, but at the time of this writing, there are no known plans to release it. If you like, you can learn more about it at https://github.com/tekul/scalasec/. Still, perhaps in the future, we'll see the ability to easily configure Spring Security in other ways.

Although annotations are not prevalent in Spring Security, certain aspects of Spring Security that apply security elements to classes or methods are, as you'd expect, available via annotations.

Updating your web.xml file

The next steps involve a series of updates to the web.xml file. Some of the steps have already been performed because the application was already using Spring MVC. However, we will go over these requirements to ensure that these more fundamental Spring requirements are understood, in the event that you are using Spring Security in an application that is not Spring-enabled.

ContextLoaderListener

The first step of updating the web.xml file is to ensure that it contains the o.s.w.context.ContextLoaderListener listener, which is in charge of starting and stopping the Spring root ApplicationContext interface. ContextLoaderListener determines which configurations are to be used, by looking at the <context-param> tag for contextConfigLocation. It is also important to specify where to read the Spring configurations from. Our application already has ContextLoaderListener added, so we only need to add the newly created security.xml configuration file, as shown in the following code snippet:

src/main/webapp/WEB-INF/web.xml <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/services.xml /WEB-INF/spring/i18n.xml /WEB-INF/spring/security.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>

The updated configuration will now load the security.xml file from the /WEB-INF/spring/ directory of the WAR. As an alternative, we could have used /WEB-INF/spring/*.xml to load all the XML files found in /WEB-INF/spring/. We choose not to use the *.xml notation to have more control over which files are loaded.

ContextLoaderListener versus DispatcherServlet

You may have noticed that o.s.web.servlet.DispatcherServlet specifies a contextConfigLocation component of its own.

src/main/webapp/WEB-INF/web.xml <servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/mvc-config.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>

DispatcherServlet creates o.s.context.ApplicationContext, which is a child of the root ApplicationContext interface. Typically, Spring MVC-specific components are initialized in the ApplicationContext interface of DispatcherServlet, while the rest are loaded by ContextLoaderListener. It is important to know that beans in a child ApplicationContext (such as those created by DispatcherServlet) can reference beans of its parent ApplicationContext (such as those created by ContextLoaderListener). However, the parent ApplicationContext cannot refer to beans of the child ApplicationContext. This is illustrated in the following diagram where childBean can refer to rootBean, but rootBean cannot refer to childBean.

As with most usage of Spring Security, we do not need Spring Security to refer to any of the MVC-declared beans. Therefore, we have decided to have ContextLoaderListener initialize all of Spring Security's configuration.

springSecurityFilterChain

The next step is to configure springSecurityFilterChain to intercept all requests by updating web.xml. Servlet <filter-mapping> elements are considered in the order that they are declared. Therefore, it is critical for springSecurityFilterChain to be declared first, to ensure the request is secured prior to any other logic being invoked. Update your web.xml file with the following configuration:

src/main/webapp/WEB-INF/web.xml </listener> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class> org.springframework.web.filter.DelegatingFilterProxy </filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet>

Not only is it important for Spring Security to be declared as the first <filter-mapping> element, but we should also be aware that, with the example configuration, Spring Security will not intercept forwards, includes, or errors. Often, it is not necessary to intercept other types of requests, but if you need to do this, the dispatcher element for each type of request should be included in <filter-mapping>. We will not perform these steps for our application, but you can see an example, as shown in the following code snippet:

src/main/webapp/WEB-INF/web.xml <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> ... </filter-mapping>

DelegatingFilterProxy

The o.s.web.filter.DelegatingFilterProxy class is a servlet filter provided by Spring Web that will delegate all work to a Spring bean from the root ApplicationContext that must implement javax.servlet.Filter. Since, by default, the bean is looked up by name, using the value of <filter-name>, we must ensure we use springSecurityFilterChain as the value of <filter-name>. Pseudo-code for how o.s.web.filter.DelegatingFilterProxy works for our web.xml file can be found in the following code snippet:

public class DelegatingFilterProxy implements Filter { void doFilter(request, response, filterChain) { Filter delegate = applicationContet.getBean("springSecurityFilterChain") delegate.doFilter(request,response,filterChain); } }

FilterChainProxy

When working in conjunction with Spring Security, o.s.web.filter. DelegatingFilterProxy will delegate to Spring Security's o.s.s.web. FilterChainProxy, which was created in our minimal security.xml file. FilterChainProxy allows Spring Security to conditionally apply any number of servlet filters to the servlet request. We will learn more about each of the Spring Security filters and their role in ensuring that our application is properly secured, throughout the rest of the book. The pseudo-code for how FilterChainProxy works is as follows:

public class FilterChainProxy implements Filter { void doFilter(request, response, filterChain) { // lookup all the Filters for this request List<Filter> delegates = lookupDelegates(request,response) // invoke each filter unless the delegate decided to stop for delegate in delegates { if continue processing delegate.doFilter(request,response,filterChain) } // if all the filters decide it is ok allow the // rest of the application to run if continue processing filterChain.doFilter(request,response) } }

Due to the fact that both DelegatingFilterProxy and FilterChainProxy are the front door to Spring Security, when used in a web application, it is here that you would add a debug point when trying to figure out what is happening.

Running a secured application

If you have not already done so, restart the application and visit http://localhost:8080/calendar/, and you will be presented with the following screen:

Great job! We've implemented a basic layer of security in our application, using Spring Security. At this point, you should be able to log in using user1@example.com as the User and user1 as the Password (user1@example.com/user1). You'll see the calendar welcome page, which describes at a high level what to expect from the application in terms of security.

Common problems

Many users have trouble with the initial implementation of Spring Security in their application. A few common issues and suggestions are listed next. We want to ensure that you can run the example application and follow along!

  • Make sure you can build and deploy the application before putting Spring Security in place.

  • Review some introductory samples and documentation on your servlet container if needed.

  • It's usually easiest to use an IDE, such as Eclipse, to run your servlet container. Not only is deployment typically seamless, but the console log is also readily available to review for errors. You can also set breakpoints at strategic locations, to be triggered on exceptions to better diagnose errors.

  • If your XML configuration file is incorrect, you will get this (or something similar to this): org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'. It's quite common for users to get confused with the various XML namespace references required to properly configure Spring Security. Review the samples again, paying attention to avoid line wrapping in the schema declarations, and use an XML validator to verify that you don't have any malformed XML.

  • If you get an error stating "BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/ schema/security] ...", ensure that the spring-security-config- 3.1.0.RELEASE.jar file is on your classpath. Also ensure the version matches the other Spring Security JARs and the XML declaration in your Spring configuration file.

  • Make sure the versions of Spring and Spring Security that you're using match and that there aren't any unexpected Spring JARs remaining as part of your application. As previously mentioned, when using Maven, it can be a good idea to declare the Spring dependencies in the dependency management section.

Spring Security 3.1 Secure your web applications from hackers with this step-by-step guide with this book and ebook.
Published: December 2012
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

A little bit of polish

Stop at this point and think about what we've just built. You may have noticed some obvious issues that will require some additional work and knowledge of the Spring Security product before we are production-ready. Try to make a list of the changes that you think are required, before this security implementation is ready to roll out to the public-facing website.

Applying the "Hello World" Spring Security implementation was blindingly fast and has provided us with a login page, username, and password-based authentication, as well as automatic interception of URLs in our calendar application. However, there are gaps between what the automatic configuration setup provides and what our end goal is, which are listed as follows:

  • While the login page is helpful, it's completely generic and doesn't look like the rest of our JBCP Calendar application. We should add a login form that's integrated with our application's look and feel.

  • There is no obvious way for a user to log out.

  • We've locked down all pages in the application, including the Welcome page, which a potential user may want to browse anonymously. We'll need to refine the roles required to accommodate anonymous, authenticated, and administrative users.

  • We do not display any contextual information to indicate to the user that they are authenticated. It would be nice to display a greeting similar to Welcome user1@example.com.

  • We've had to hardcode the username, password, and role information of the user in the XML configuration file. Recall this section of XML we added:

    <user-service> <user name="user1@example.com" password="user1" authorities="ROLE_USER"/> </user-service>

You can see that the username and password are right there in the file. It would be unlikely that we'd want to add a new XML declaration to the file for every user of the system! To address this, we'll need to update the configuration with another type of authentication.

Customizing login

We've seen how Spring Security makes it very easy to get started. Now let's see how we can customize the login experience. In the following code snippet, we demonstrate the usage of some of the more common ways to customize login, but we encourage you to refer to Spring Security's reference documentation, which includes an Appendix with all of the supported attributes. First, update your security.xml file as follows:

src/main/webapp/WEB-INF/spring/security.xml <http ...> … <form-login login-page="/login/form" login-processing-url="/login" username-parameter="username" password-parameter="password" authentication-failure-url="/login/form?error"/> </http>

The login-page attribute specifies where Spring Security will redirect the browser if a protected page is accessed and the user is not authenticated. If a login page is not specified, Spring Security will redirect the user to / spring_security_login. Then o.s.s.web.filter.FilterChainProxy will choose o.s.s.web.authentication.ui.DefaultLoginPageGeneratin gFilter, which renders the default login page, as one of the delegates since DefaultLoginPageGeneratingFilter is configured to process /spring_security_ login by default. Since we have chosen to override the default URL, we are in charge of rendering the login page when the URL /login/form is requested.

The login-processing-url attribute defaults to /j_spring_security_check, and specifies the URL that the login form (which should include the username and password) should be submitted to, using an HTTP post. When Spring Security processes this request, it will attempt to authenticate the user.

The username-parameter and the password-parameter attributes default to j_username and j_password respectively and specify the HTTP parameters that Spring Security will use to authenticate the user when processing loginprocessing- url.

The authentication-failure-url attribute specifies the page that Spring Security will redirect to if the username and password submitted to login-processing-url are invalid.

It may be obvious, but if we only wanted to add a custom login page, we would only need to specify the login-page attribute. We would then create our login form using the default values for the remaining attributes. However, it is often a good practice to override the values of anything visible to users, to prevent exposing that we are using Spring Security. Revealing what frameworks we are using is a type of "information leakage", making it easier for attackers to determine potential holes in our security.

The next step is to create a login page. We can use any technology we want to render the login page, as long as the login form produces the HTTP request that we specified with our Spring Security configuration, when submitted. By ensuring the HTTP request conforms to our configuration, Spring Security can authenticate the request for us. Create the following login.jsp file:

src/main/webapp/WEB-INF/views/login.jsp <?xml version="1.0" encoding="ISO-8859-1" ?> <%@ 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" %> <c:set var="pageTitle" value="Please Login" scope="request"/> <jsp:include page="./includes/header.jsp"/> <c:url value="/login" var="loginUrl"/> <form action="${loginUrl}" method="post"> <c:if test="${param.error != null}"> <div class="alert alert-error"> Failed to login. <c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}"> Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION. message}" /> </c:if> </div> </c:if> <c:if test="${param.logout != null}"> <div class="alert alert-success"> You have been logged out. </div> </c:if> <label for="username">Username</label> <input type="text" id="username" name="username"/> <label for="password">Password</label> <input type="password" id="password" name="password"/> <div class="form-actions"> <input id="submit" class="btn" name="submit" type="submit" value="Login"/> </div> </form> <jsp:include page="./includes/footer.jsp"/>

There are a number of items that are worth highlighting in login.jsp.

  • The form action should be /login, to match the value provided for the login-processing-url attribute we specified.

  • For security reasons, Spring Security only attempts to authenticate when using post, by default.

  • We can use param.error to see if there was a problem logging in, since the value of our authentication-failure-url attribute, /login/form?error, contains the HTTP parameter error.

  • The session attribute, SPRING_SECURITY_LAST_EXCEPTION, contains the last o.s.s.core.AuthenticationException exception, which can be used to display the reason for a failed login. The error messa

  • The input names for the username and password inputs are chosen to correspond to the values we specified for the username-parameter and password-parameter attributes in our security.xml configuration.

The last step is to make Spring MVC aware of our new URL. This can be done by adding the following method to WebMvcConfig:

src/main/java/com/packtpub/springsecurity/web/config/WebMvcConfig. java import org.springframework.web.servlet.config.annotation. ViewControllerRegistry; ... public class WebMvcConfig extends WebMvcConfigurationSupport { public void addViewControllers(ViewControllerRegistry registry){ registry.addViewController("/login/form") .setViewName("login"); } ... }

Configuring logout

Spring Security's <http> configuration automatically adds support for logging the user out. All that is needed is to create a link that points to /j_spring_security_ logout. However, we will demonstrate how to customize the URL used to log the user out. The first step is to update the Spring Security configuration.

src/main/webapp/WEB-INF/spring/security.xml <http ...> ... <logout logout-url="/logout" logout-success-url="/login/form?logout"/> </http>

The next step is to provide a link for the user to click that will log them out. We will update header.jsp, so that the Logout link appears on every page.

src/main/webapp/WEB-INF/views/includes/header.jsp <div id="nav-account" ..> <ul class="nav"> <c:url var="logoutUrl" value="/logout"/> <li> <a href="${logoutUrl}">Logout</a> </li> </ul> </div>

The last step is to update login.jsp to display a message indicating logout was successful when the parameter logout is present.

src/main/webapp/WEB-INF/views/login.jsp </c:if> <c:if test="${param.logout != null}"> <div class="alert alert-success"> You have been logged out. </div> </c:if> <p> <label for="username">Username</label> ...

The page isn't redirecting properly

If you have not already, restart the application and visit http://localhost:8080/calendar/ in FireFox; you will see an error similar to the following:

What went wrong? The problem is that, since Spring Security is no longer rendering the login page, we must allow everyone (not just ROLE_USER) access to the login page. Without granting access to the login page, the following happens:

  1. We request the Welcome page in the browser.

  2. Spring Security sees that the Welcome page requires ROLE_USER and that we are not authenticated, so it redirects the browser to the Login page

  3. The browser requests the Login page.

  4. Spring Security sees that the Login page requires ROLE_USER and that we are still not authenticated, so it redirects the browser to the Login page again.

  5. The browser requests the Login page again.

  6. Spring Security sees that the Login page requires ROLE_USER.

The process could just keep repeating indefinitely. Fortunately for us, Firefox realizes that there are too many redirects occurring, stops performing the redirect, and displays a very informative error message. In the next section, we will learn how to fix this error by configuring URLs differently, depending on the access that they require.

Basic role-based authorization

We can expand on the Spring Security configuration from Hello Spring Security to vary the access control by URL. In this section, you will find a configuration that allows more granular control over how resources can be accessed. In the following configuration, Spring Security will:

  • Completely ignore any request that starts with /resources/. This is beneficial, since our images, CSS, and JavaScript do not need to use Spring Security.

  • Allow anonymous users to access the Welcome, Login, and Logout pages.

  • Only allow administrators access to the All Events page.

  • Add an administrator that can access the All Events page.

    src/main/webapp/WEB-INF/spring/security.xml <http pattern="/resources/**" security="none"/> <http auto-config="true"> <intercept-url pattern="/" access="ROLE_ANONYMOUS,ROLE_USER"/> <intercept-url pattern="/login/*" access="ROLE_ANONYMOUS,ROLE_USER"/> <intercept-url pattern="/logout" access="ROLE_ANONYMOUS,ROLE_USER"/> <intercept-url pattern="/events/" access="ROLE_ADMIN"/> <intercept-url pattern="/**" access="ROLE_USER"/> ... </http> <authentication-manager> <authentication-provider> <user-service> <user name="user1@example.com" password="user1" authorities="ROLE_USER"/> <user name="admin1@example.com" password="admin1" authorities="ROLE_USER,ROLE_ADMIN"/> </user-service> </authentication-provider> </authentication-manager>

Note that we do not include /calendar, the application's context root, in the Spring Security configuration, because Spring Security takes care of the context root transparently for us. In this way, we will not need to update our configuration if we decide to deploy to a different context root.

In Spring Security 3.1, you can specify multiple <http> elements that allow you to have greater control over how security is applied to different portions of your application. The first <http> element states that Spring Security should ignore any URL that starts with /resources/, and the second <http> element states that any other request will be processed by it. There are a few important things to note about using multiple <http> elements:

  • If no path attribute is specified, it is the equivalent of using a path of /**, which matches all requests.

  • Each <http> element is considered in order, and only the first match is applied. So, the order in which they appear in your configuration file is important. The implication is that only the last <http> tag can use a path that matches every request. If you do not follow this rule, Spring Security will produce an error. The following example illustrates the error:

    <!-- matches every request --> <http auto-config="true"> ... </http> <!-- never considered since previous http matches everything. Spring Security will report an error to prevent this. To fix add path attribute to first http element --> <http auto-config="true"> ... </http>

  • The default pattern is backed by o.s.s.web.util.AntPathRequestMatcher, which will compare the specified pattern as an Ant pattern to determine wheter it matches the servletPath and pathInfo of the HttpServletRequest. Note that query strings are ignored when determining whether a request is a match. Internally, Spring Security uses o.s.u.AntPathMatcher to do all the work. A summary of the rules is listed as follows:

    • ? matches a single character.

    • * matches zero or more characters, excluding /.

    • ** matches zero or more directories in a path.

    • The pattern "/events/**" matches "/events", "/events/", "/ events/1", and "/events/1/form?test=1"; it does not match "/ events123".

    • The pattern "/events*" matches "/events", and "/events123"; it does not match "/events/" or "/events/1".

    • The pattern "/events*/**" matches "/events", "/events/", "/ events/1","/events123", "/events123/456", and "/events/1/ form?test=1".

  • A more advanced option is to use the optional request-matcher-ref attribute. This method provides the ultimate flexibility in how a request maps to an <http> element by using the o.s.s.web.util.RequestMatcher interface.

The path attribute on the <intercept-url> elements further refines the filtering on the request and allows access control to be applied. You can see that the updated configuration allows different types of access, depending on the URL pattern. ROLE_ANONYMOUS is of particular interest since we have not defined it anywhere in security.xml. This is the default authority assigned to a user that is not logged in. The following line from the updates to our security.xml file is what allows anonymous (unauthenticated) users and users with the ROLE_USER authority to access the Login page.

<intercept-url pattern="/login/*" access="ROLE_ANONYMOUS,ROLE_USER"/>

When defining <intercept-url> elements, there are a number of things to keep in mind:

  • Just as each <http> element is considered from top to bottom, so are <intercept-url> elements. This means it is important to specify the most specific elements first. The following example illustrates a configuration that does not specify the more specific pattern first, which will result in warnings from Spring Security at startup:

    <http ...> <!-- matches every request, so it will not continue --> <intercept-url pattern="/**" access="ROLE_USER"/> <!-- below will never match --> <intercept-url pattern="/login/form" access="ROLE_ANONYMOUS,ROLE_USER"/> ... </http>

  • It is important to note that if <http> is marked as security="none", there can be no child <intercept-url> elements defined. This is because security="none" states that Spring Security should ignore all requests that match this <http> tag. Defining a child <intercept-url> element with security="none" contradicts any <intercept-url> declaration. An example is as follows:

    <http pattern="/http/**" security="none"> <!-- below will produce an error since it would never be executed --> <intercept-url pattern="/**" access="ROLE_USER"/> ... </http>

  • The path attribute of the <intercept-url> element is independent and is not aware of the path attribute of the <http> element. For example, the following would never match a request since a request cannot start with both /http and /intercept-url at the same time (these two patterns are mutually exclusive):

    <http pattern="/http/**" ...> <!-- below will never match --> <intercept-url pattern="/intercept-url/**" access="ROLE_USER"/> ... </http>

If you have not done so already, restart the application and visit http://localhost:8080/calendar/.. Experiment with the application to see all the updates you have made.

  • Select a link that requires authentication and observe the new login page.

  • Try typing an invalid username/password and view the error message.

  • Try logging in as an admin (admin1@example.com/admin1), and view all of the events. Note that we are able to view all the events.

  • Try logging out and view our logout success message.

  • Try logging in as a regular user (user1@example.com/user1), and view all of the events. Note that we get an access denied page.

Expression-based authorization

You may have noticed that granting access to everyone was not nearly as concise as we may have liked. Fortunately, Spring Security can leverage Spring Expression Language (SpEL) to determine whether a user has authorization. In the following code snippet, you can see the updates when using SpEL with Spring Security:

src/main/webapp/WEB-INF/spring/security.xml <http auto-config="true" use-expressions="true"> <intercept-url pattern="/" access="permitAll"/> <intercept-url pattern="/login/*" access="permitAll"/> <intercept-url pattern="/logout" access="permitAll"/> <intercept-url pattern="/events/" access="hasRole('ROLE_ADMIN')"/> <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/> <form-login ../> ... </http>

You may notice that the /events/ security constraint is brittle. For example, the URL /events is not protected by Spring Security to restrict ROLE_ADMIN. This demonstrates the need to ensure that we provide multiple layers of security.

Changing the access attribute from ROLE_ANONYMOUS,ROLE_USER to permitAll might not seem like much, but this only scratches the surface of the power of Spring Security's expressions. We will go into much greater detail about access control and Spring Expressions in the second half of the book. Go ahead and verify that the updates work by running the application.

Conditionally displaying authentication information

Currently, our application has no indication whether we are logged in or not. In fact, it appears as though we are always logged in, since the Logout link is always displayed. In this section, we will demonstrate how to display the authenticated user's username and conditionally display portions of the page using Spring Security's JSP tag library.

The first step is to update your dependencies to include the spring-securitytaglibs- 3.1.0.RELEASE.jar file. Since we are using Maven, we will add a new dependency declaration in our pom.xml file, as follows:

pom.xml <dependencies> ... <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>3.1.0.RELEASE</version> </dependency> </dependencies>

The next step is to update header.jsp to leverage the Spring Security tag library. You can find the updates as follows:

src/main/webapp/WEB-INF/views/includes/header.jsp <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/ tags" %> <!DOCTYPE html> ... <div id="nav-account" class="nav-collapse pull-right"> <ul class="nav"> <sec:authorize access="authenticated" var="authenticated"/> <c:choose> <c:when test="${authenticated}"> <li id="greeting"> <div> Welcome <sec:authentication property="name" /> </div> </li> <c:url var="logoutUrl" value="/logout"/> <li> <a id="navLogoutLink" href="${logoutUrl}">Logout</a> </li> </c:when> <c:otherwise> <c:url var="loginUrl" value="/login/form"/> <li> <a id="navLoginLink" href="${loginUrl}">Login</a> </li> </c:otherwise> </c:choose> </ul> </div> ...

The <sec:authorize /> tag determines whether the user is authenticated or not and assigns it to the variable authenticated. The access attribute should be rather familiar from the <intercept-url /> element. In fact, both components leverage the same SpEL support. In order for the tag to be able to use SpEL support, ensure that you specify <http use-expressions="true"> in your Spring Security configuration as we have already done, otherwise Spring Security will throw an exception stating it cannot find o.s.s.web.access.expression. WebSecurityExpressionHandler. If you choose, there are attributes on the JSP tag libraries that do not use expressions. However, using SpEL is typically the preferred method since it is more powerful.

The <sec:authentication /> tag will look up the current o.s.s.core. Authentication object. The property attribute will find the principal attribute on o.s.s.core.Authentication, which in this case is o.s.s.core.userdetails. UserDetails. It then obtains the UserDetails username property and renders it to the page. Don't worry if the details of this are confusing.

If you haven't done so already, restart the application to see the updates we have made. At this point, you may realize that we are still displaying links we do not have access to. For example, user1@example.com should not see a link to the All Events page.

Customizing the behavior after login

We have already discussed how to customize a user's experience during login, but sometimes it is necessary to customize the behavior after login. In this section, we will discuss how Spring Security behaves after login and will provide a simple mechanism to customize this behavior.

In the default configuration, Spring Security has two different flows after successful authentication. The first scenario occurs if a user never visits a resource that requires authentication. In this instance, after a successful login attempt, the user will be sent to the default-target-url attribute of the <form-login> element. If left undefined, default-target-url will be the context root of the application.

If a user requests a protected page before being authenticated, Spring Security will remember the last protected page that was accessed prior to authenticating using o.s.s.web.savedrequest.RequestCache. Upon successful authentication, Spring Security will send the user to the last protected page that was accessed prior to authentication. For example, if an unauthenticated user requests the My Events page, they will be sent to the login page.

After successfully authenticating, they will be sent to the previously requested My Events page.

A common requirement is to customize Spring Security to send the user to a different default-target-url attribute, depending on the user's role. Let's take a look at how this can be accomplished.

The first step is to configure the default-target-url attribute of the
element. Go ahead and update security.xml to use /default instead of the context root.

src/main/webapp/WEB-INF/spring/security.xml <http ...> ... <form-login login-page="/login/form" login-processing-url="/login" username-parameter="username" password-parameter="password" authentication-failure-url="/login/form?error" default-target-url="/default"/> ... </http>

The next step is to create a controller that processes /default. In the following code, you will find a sample Spring MVC controller, DefaultController, which demonstrates how to redirect administrators to the All Events page and other users to the Welcome page. Create a new file in the following location:

src/main/java/com/packtpub/springsecurity/web/controllers/ DefaultController.java // imports omitted @Controller public class DefaultController { @RequestMapping("/default") public String defaultAfterLogin(HttpServletRequest request) { if (request.isUserInRole("ROLE_ADMIN")) { return "redirect:/events/"; } return "redirect:/"; } }

In Spring Tool Suite you can use Shift + CTRL + O to automatically add the missing imports.

There are a few things to point out about DefaultController and how it works. The first is that Spring Security makes the HttpServletRequest parameter aware of the currently logged-in user. In this instance, we are able to inspect which role the user belongs to, without relying on any of Spring Security's APIs. This is good because if Spring Security's APIs change or we decide we want to switch our security implementation, we have less code that needs to be updated. It should also be noted that while we implement this controller with a Spring MVC controller, our default-target- url attribute could be handled by any controller implementation (for example, Struts, a standard Servlet, and so on) we desire.

If you wish to always go to default-target-url, you can leverage the always-use- default-target attribute. We will not do this in our configuration, but you can see an example of this, as follows:

<form-login ... always-use-default-target="true"/>

You are now ready to give it a try. Restart the application and go directly to the My Events page, then log in; you will see that you are at the My Events page. Next, log out and try logging in as user1@example.com You should go to the Welcome page. Log out and log in as admin1@example.com, and you will be sent to the All Events page.

Summary

In this article we have applied a very basic Spring Security configuration, shown how to customize the user's login and logout experience, and demonstrated how to display basic information such as a username in our web application.

Resources for Article :


Further resources on this subject:


Spring Security 3.1 Secure your web applications from hackers with this step-by-step guide with this book and ebook.
Published: December 2012
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

About the Author :


Peter Mularien

Peter Mularien is an experienced software architect and engineer, and the author of the book Spring Security 3, Packt Publishing. Peter currently works for a large financial services company and has over 12 years consulting and product experience in Java, Spring, Oracle, and many other enterprise technologies. He is also the reviewer of this book.

Robert Winch

Robert Winch is currently a Senior Software Engineer at VMware and is the project lead of the Spring Security framework. In the past he has worked as a Software Architect at Cerner, the largest provider of electronic medical systems in the US. Throughout his career he has developed hands on experience in integrating Spring Security with an array of security standards (i.e. LDAP, SAML, CAS, OAuth, etc). Before he was employed at Cerner, he worked as an independent web contractor in proteomics research at Loyola University, Chicago, and on the Globus Toolkit at Argonne National Laboratory.

Books From Packt


Instant Spring for Android Starter [Instant]
Instant Spring for Android Starter [Instant]

Spring Roo 1.1 Cookbook
Spring Roo 1.1 Cookbook

Spring Persistence with Hibernate
Spring Persistence with Hibernate

Spring Python 1.1
Spring Python 1.1

Spring 2.5 Aspect Oriented Programming
Spring 2.5 Aspect Oriented Programming

Spring Data
Spring Data

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

Spring Web Services 2 Cookbook
Spring Web Services 2 Cookbook


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
x
u
2
3
i
A
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software