Chapter 6. Authenticating users

published book

This chapter covers

  • Implementing user authentication with Spring Security
  • Customizing a login page via Hibernate
  • Using password hashing, salting, and auto-authentication

Many applications need a way to allow users to authenticate—that is, to say who they are and prove it. In this chapter, you’ll learn how to support this common requirement using Spring Security 3. The first three recipes look at approaches to implementing a login form. The five remaining recipes look at sourcing user data from a persistent store.

join today to enjoy all our content. all the time.
 

6.1. Implementing login and logout with remember-me authentication

Prerequisites

None

Key technologies

Spring Security 3 (including tag libraries)

Background

Spring Security 3, although a large framework, makes it easy to get started with basic authentication. This recipe shows what you can do with a fairly minimal configuration.

Problem

Support basic logins and logouts, including remember-me authentication.

Solution

You’ll use Spring Security 3 to add logins and logouts to a simple web app. You’ll do this entirely through configuration; that is, you don’t need to write any Java code to make it work.

The app is a simple university portal with nothing more than a home page and a login page (figure 6.1). To implement it, you’ll need to configure Spring Security, configure web.xml, and add login and logout links to the app.

Figure 6.1. The default login page, complete with Submit Query (or Submit, depending on the browser) button. The page is styled with CSS.

There’s also a beans-web.xml configuration, but we won’t address that here because it doesn’t contain anything specific to this recipe. See the code download for more information.

Let’s start by configuring Spring Security. You’ll do the configuration itself first, and after that we’ll dive into some of the behind-the-scenes details.

Configure Spring Security

The following listing shows a simple Spring Security 3 configuration that enables web-based authentication and creates an authentication source.

Listing 6.1. beans-security.xml, your Spring Security configuration

The first thing to notice is that beans-security.xml isolates your Spring Security configuration into its own configuration file. Because you’ve done this, it makes a lot of sense to declare the Spring Security namespace as the default namespace, which you do at . That way you don’t have to keep specifying a namespace prefix with each element.

At you enable web security using the <http> element. Although this element is responsible for web-based security generally (authentication, authorization, HTTPS, and so on), the focus in this chapter is authentication. (Chapter 7 covers web authorization in some detail.) The <http> element works by creating a chain of servlet filters to handle different aspects of web security.

By setting auto-config="true", you enable the filters for form-based logins (using the form from figure 6.1), HTTP basic authentication, and logouts. If you prefer, you can set auto-config="false" (that’s the default) and specify the desired filters manually.

You also set use-expressions="true" to enable the SpEL, which you’ll need when you create your JSPs.

You override the auto-config defaults by placing the desired configuration inside the <http> configuration. In the case of <form-login>, you choose your own value for default-target-url to indicate where you want the user to land after they successfully log in.[1]

1 Note that this is a default URL. When an unauthenticated user tries to access a protected resource, the login page intercepts the attempt, and the target URL is the originally requested page unless always-use-default-target="true" is set on the <form-login> element. See the Spring Security Reference Documentation for details.

At you enable remember-me authentication. Spring Security 2 automatically included remember-me authentication with its auto-config, but that’s no longer the case with Spring Security 3. You must add it yourself.

The logout configuration at is similar to what you did with <form-login>, but this time you’re specifying a target URL for successful logouts.

That takes care of your web authentication configuration. But you still need an authentication source, and that’s what <authentication-manager> helps you establish . This element allows you to identify a list of authentication providers that the manager will consult during authentication; authentication succeeds as long as at least one provider successfully authenticates the user. <authentication-provider> sets up a single DaoAuthenticationProvider backed by a DAO . In general the DAO can be any UserDetailsService implementation; here <user-service> implicitly selects the InMemoryDaoImpl implementation .[2] You use the in-memory DAO to create two users, one with username juan, password p@ssword, and roles called user and admin, and the other for username elvira with password p@ssword who only has the user role. (Note that the specific interfaces and classes are hidden by the namespace configuration, and that’s the point of using namespace configuration in the first place.)

2 Once again, we’re glossing over many configuration options and details. Please consult the Spring Security Reference Documentation.

That’s the Spring Security configuration. Even though it’s small, it’s pretty dense; it will help to examine some technical details before moving on to web.xml configuration, especially because you’ll need them again in recipes 6.5 and 6.6.

Authentication managers, providers, and user details services

We stated that an authentication manager manages a list of providers. More exactly, AuthenticationManager is an interface with a single authenticate() method to process authentication requests. It doesn’t care whether implementations use providers, although it’s hard to imagine what else they would reasonably do. At any rate, from the AuthenticationManager’s point of view, providers are an implementation detail.

The default AuthenticationManager implementation is called ProviderManager. You’re telling Spring Security to create a ProviderManager instance when you include the <authentication-manager> element. ProviderManager maintains a list of AuthenticationProviders corresponding to the authentication sources you want to include. See figure 6.2.

Figure 6.2. A class diagram for AuthenticationManager and ProviderManager

Where things get interesting is with the providers themselves. Spring Security offers many provider options. You happen to be using the DaoAuthenticationProvider, but that’s certainly not the only one there is—not by a long shot. See the class diagram in figure 6.3.

Figure 6.3. The AuthenticationProvider hierarchy, which includes tons of provider options

As figure 6.3 shows, Spring Security provides rich support for authentication. Besides the DAO provider, there are providers for CAS, JAAS, LDAP, OpenID, and more.

Let’s drill down one more level. When you use a DaoAuthenticationProvider, you have to specify a DAO: that is, a UserDetailsService implementation. In essence, DaoAuthenticationProvider is an adapter that allows you to use any UserDetailsService implementation as an AuthenticationProvider. There are multiple UserDetailsService implementations; see figure 6.4.

Figure 6.4. UserDetailsService hierarchy, containing DAOs used by the DaoAuthenticationProvider

UserDetailsService is a read-only interface with a single loadUserByUsername() method. This is how DaoAuthenticationProvider provides authentication services to AuthenticationManager. UserDetailsManager extends UserDetailsService to add write operations as well, although AuthenticationManager doesn’t use that.

Now you’re in a better position to understand what’s happening with listing 6.1. See the bean dependency diagram in figure 6.5. The <authentication-manager> element creates an AuthenticationManager bean—specifically, a ProviderManager—called org.springframework.security.AuthenticationManager at .

Figure 6.5. Bean dependency diagram for listing 6.1

At , <authentication-provider> creates a single AuthenticationProvider bean—a DaoAuthenticationProvider—and adds it to the ProviderManager. It also creates an InMemoryDaoImpl at . The net result is an in-memory authentication provider, which is fine for development purposes.

For the sake of completeness, <authentication-manager> also creates a DefaultAuthenticationEventPublisher at . The ProviderManager uses this to publish authentication events (successes and failures) so that listeners can respond as needed (for example, redirecting the user to the correct target URL).

That’s enough behind-the-scenes for now. Let’s configure web.xml.

Configuring web.xml for web security

Spring Security uses a special servlet filter to secure web resources. We’ll examine this in more detail momentarily, but first let’s look at the following listing.

Listing 6.2. Configuring web.xml with the Spring Security filter

At you reference the beans-security.xml security configuration from listing 6.1. At you enable Spring Security web security by defining a DelegatingFilterProxy filter, which is part of the core Spring distribution rather than being part of Spring Security itself.[3] DelegatingFilterProxy is essentially a trick for injecting servlet filters. You can point it at any filter on the application context you like by giving the DelegatingFilterProxy a name that matches the target filter’s bean ID. The target filter, being a bean, is injectable like any other bean.

3 DelegatingFilterProxy was inspired by the FilterToBeanProxy class, which originated with Acegi Security—the precursor to Spring Security.

Although it would certainly be possible to define one DelegatingFilterProxy for each filter you want to use, that would be a hassle. Instead you define a single DelegatingFilterProxy filter on the web.xml side and a single FilterChainProxy filter on the beans-security.xml side. Then you create the filters and filter chains you want to use entirely within the Spring Security configuration rather than in web.xml. Fortunately, using <http auto-config="true" /> sets up the FilterChainProxy, filters, and filter chains for you, using the bean ID springSecurityFilterChain. See figure 6.6 for a visual overview of what we just described.

Figure 6.6. Filter proxying for injectable servlet filters

Figure 6.7 shows the same thing as a sequence diagram.

Figure 6.7. DelegatingFilterProxy sequence diagram. This is a partial view; we’ve suppressed the full filter chain. Filter 1, Filter 2, and so on refer to filters in the chain.

Finally, you indicate that you want all requests to pass through the filter . This includes all requests, because login submissions and logouts aren’t associated with any DispatcherServlet.

You’re done with configuration. Now let’s make sure your JSPs are equipped to display the login and logout links appropriately.

Quick tip: the filter mapping order matters

Note that you place the Spring Security filter mapping before the Sitemesh filter mapping for a reason: you want the request to pass through the Spring Security filter first so authentication information will be available on the request when the Sitemesh filter kicks in. This allows you to display, for example, the user’s name as part of the template.

Creating the appropriate links in your JSPs

So far your only special JSP is subhead.jspf.[4] The next listing shows a simplified (CSS suppressed) version of subhead.jspf. See the code download for the full version.

4 A .jspf file is a JSP fragment (or JSP segment in JSP 2.0). JSPF pages are JSPs that you want to include in other JSPs.

Listing 6.3. User information and shared navigation in subhead.jspf

You begin by declaring the Spring Security tag library , because you’re going to use it both to present/suppress links according to role and to display user information. Next you create a couple of variables to store the default login and logout URLs. The login URL here returns a login form, not the form submission URL.

At you display the login link to anonymous users using <security:authorize> and using SpEL for the access rule. This is why you had to set use-expressions="true" in listing 6.1. You’ll learn more about <security:authorize> in recipe 7.1.

At you display a personalized welcome message and the logout link to authenticated users, once again using <security:authorize> and SpEL to perform the test. The personalized welcome message uses the <security:authentication> tag , which exposes the user’s authentication information to the JSP. The property attribute refers to a property on an underlying org.springframework.security.core.Authentication object; see the Javadoc for that class for more information on what’s available.[5] By default, the principal is an org.springframework.security .core.userdetails.User, and its properties are available for use as well. You’ll see how to use a custom principal object in recipe 6.5.

5 Bear in mind that you’re using Spring Security 3 here, not Spring Security 2. Some classes moved around in Spring Security 3, including Authentication.

You now have fully functional login and logout capabilities. Try it at http://localhost:8080/sip/home.html.

Discussion

The main benefit of the default login form is that it’s easy to set up. But it’s merely serviceable; it’s not necessarily what you’d want to use for a more polished app:

  • The login button uses the awkward language Submit Query (or Submit on some browsers, which is somewhat better). We’d prefer something like Log in or Sign in.
  • The form includes a reset button, which most users would consider superfluous.
  • You may want a different page layout, form layout, or styling.

In the following recipe you’ll learn how to customize the login form.

Get Spring in Practice
add to cart

6.2. Customizing the login page

Prerequisite

Recipe 6.1 Implementing login and logout with remember-me authentication

Key technologies

Spring Security

Background

In recipe 6.1, you learned the mechanics of using Spring Security to set up form-based logins, but in most real-life applications you’ll need (or at least want) to modify the form’s appearance. This recipe shows how to replace the purely functional default login form with one that addresses not only the functional requirements but also those that are more visual or interactive in nature.

Problem

Create a custom login page.

Solution

To create a custom login page, you must

  • Create a custom login JSP
  • Add a <mvc:view-controller> element to beans-web.xml (part of the code download)
  • Modify the <form-login> element in beans-security.xml
  • Update subhead.jspf to use the new form

As in recipe 6.1, you don’t have to write any Java language code to pull this off. You’ll start with the JSP change.

Creating the login form JSP

Figure 6.8 shows a custom login page. It isn’t hugely different in appearance than the default form, but there is a key difference: you now control what’s on the page. Notice that there’s a nav bar at the top of the login form (although all we’ve put there is a Home link). That nav bar wasn’t part of the default login form from figure 6.1. We’ve also changed the old Submit Query button to a new Log in button, and we’ve gotten rid of the unnecessary Reset button.

Figure 6.8. A custom login page

The following listing shows how to implement a custom form. For clarity, we’ve suppressed most of the actual layout and CSS because it’s the form itself that matters.

Listing 6.4. Custom login form, login.jsp, whose appearance you control

You use <c:url> to store the form-submission URL at . This allows you to avoid hard-coding the context path into the URL, because <c:url> provides it automatically. The specific URL you’re using is the Spring Security default for login form submissions.

If there’s a login failure, you need a way to say so. That’s what is about. The JSP checks to see whether there is an HTTP parameter failed=true. If so, it displays the error message.

The form at uses the form-submission URL you created as its action. You also use specific parameter names for the username, password, and remember-me check box.

That’s all there is to the login form. But you still need to make it reachable by updating beans-web.xml.

Adding a view controller to beans-web.xml

The beans-web.xml configuration (see the code download) already declares the mvc namespace, so all you need to do is add the following view controller:

<mvc:view-controller path="/login.html" />

This maps requests for /login.html to the logical view name login, thanks to the DispatcherServlet’s DefaultRequestToViewNameTranslator. (You can also pick a view name explicitly by using the view-name attribute on the <mvc:view-controller>.) Then of course the InternalResourceViewResolver carries the view name to an actual JSP. See figure 6.9.

Figure 6.9. From request path to view with a view controller

Now that you have a login page, the next step is to tell Spring Security about it.

Modifying the <form-login> element in beans-security.xml

There isn’t much to do to beans-security.xml. You need to tell Spring Security where to find your new login page, and you need to ensure that it uses the error-message capability you created in the JSP. Modify the <form-login> element as follows:

<form-login
    login-page="/login.html"
    authentication-failure-url="/login.html?failed=true"
    default-target-url="/home.html" />

The login-page attribute tells Spring Security where the custom login page is. It needs this so it can redirect unauthenticated users to the login page when they attempt to access a protected resource. You don’t currently have any protected pages, so you can’t yet see this in action, but you’ll return to this in chapter 7.

The authentication-failure-url attribute, as you might guess, tells Spring Security where to direct the user if the login attempt fails. You send the user right back to your custom login page, but you include a failed=true HTTP parameter. That’s how you tell the JSP to display the error message, as you saw in listing 6.4.

  • Now that you’ve handled configuration, let’s update the navigation JSP fragment.
Updating the login link in the navigation

All you have to do in subhead.jspf is replace the /spring_security_login path with /login.html. Simple! Admire your handiwork at http://localhost:8080/sip/home.html.

Discussion

This recipe showed how to improve upon the default login page that Spring Security provides by creating a custom login page. In the next recipe, you’ll consider a third way to handle login forms. This time, instead of using a login link that points to a separate login page, you’ll build the login form right into the page navigation.

Sign in for more free preview time

6.3. Implementing an always-resident login form

Prerequisites

Recipe 6.2 Customizing the login page

Key technologies

Spring Security, Spring Security tag library

Background

In recipes 6.1 and 6.2, you looked at two standard ways to present login forms: you can present a login link as part of the site navigation, or you can present a login form when an unauthenticated user attempts to access a protected resource.

Here we’ll consider a third way: the always-resident login form. It allows the user to log in with one less click.

Problem

Display a login form that appears as part of the page template (and thus on every page) until the user logs in. See figure 6.10.

Figure 6.10. A login form that displays on every page until the user logs in

Solution

Normally an unauthenticated user either clicks a login link or attempts to access a protected resource, after which Spring Security redirects them to a login page. Here you don’t have that; every page has a login form, and there isn’t any login page.

Fortunately, Spring Security is sufficiently flexible that you can pull it off. The main thing you care about is having somewhere to post the form data, no matter where the form lives. You’ll start by modifying the subhead.jspf navigation file.

Modifying subhead.jspf to include the login form

The next listing updates the subhead.jspf file from listing 6.3. As before, we’ve simplified the layout and CSS for clarity’s sake; see the code download for the full version.

Listing 6.5. subhead.jspf updated to include an always-resident login form

You specify the login submission URL at and use it to create a login form at . That’s all there is to it. This is a special case of the custom login form from recipe 6.2.

You need two more JSPs. The first is a login-required page that you present to unauthenticated users when they attempt to access protected resources. The second is a login-failed page to display when (yup) a login attempt failed.

Creating a login-required page

Normally, when an unauthenticated user attempts to access a protected page, you send the user to a login page, perhaps with some verbiage to the effect that they need to log in. Here you don’t have a dedicated login page, so you need to do something different. You’ll use loginRequired.jsp for this. It’s simple:

<html>
    <head><title>Login required</title></head>
    <body>
        <%@ include file="includes/subhead.jspf" %>
        <h1>Login required</h1>
        <p>Please log in to access the requested page.</p>
    </body>
</html>

The subhead.jspf include has a built-in login form, so the user can log in from this page. Now let’s create the login-failed page.

Creating a login-failed page

In recipes 6.1 and 6.2, the login pages displayed login-failed messages as appropriate; but now there’s no login page, so you need some other way to communicate that message. Once again you’ll create a simple page to do that, this time called loginFailed.jsp:

<html>
    <head><title>Login failed</title></head>
    <body>
        <%@ include file="includes/subhead.jspf" %>
        <h1>Login failed</h1>
        <div class="warning">
            Your login attempt failed. Please try again, or contact
            technical support for further assistance.
        </div>
    </body>
</html>

Those are the JSPs you’ll need. Let’s add them to beans-web.xml because you’ll need to reference them from beans-security.xml.

Spring Web MVC configuration

Here all you need do is add a couple of view controllers to beans-web.xml, like so:

<mvc:view-controller
    path="/login-required.html" view-name="loginRequired" />
<mvc:view-controller
    path="/login-failed.html" view-name="loginFailed" />

Notice that this time around you’re using the view-name attribute to specify view names explicitly, because, for example, /login-required.html wouldn’t map to /WEB-INF/jsp/loginRequired.jsp under the implicit mapping. You could have named the JSPs login-required.jsp and login-failed.jsp, but you happened not to do that. Either way works.

With the JSPs and MVC configuration complete, let’s update the Spring Security configuration.

Spring security configuration

All you need are a couple of tweaks to your existing beans-security.xml configuration. Once again it’s the <form-login> element you need to change:

<form-login
    login-page="/login-required.html"
    authentication-failure-url="/login-failed.html"
    default-target-url="/home.html" />

Same attributes, different values. This time login-page points to the login-required page and authentication-failure points to the login-failed page.

There you have it: an always-resident login form. Spring Security makes it easy. You can give it a test drive using the same URL as before. Bear in mind that you don’t yet have any way to exercise the login-required page, because the sample app doesn’t include any access controls. We’ll treat authorization in chapter 7.

Discussion

The always-resident login form described in this recipe is an alternative to the more typical login link. It works well if you have enough screen real estate to present it and if you want to avoid an unnecessary click. The Spring Community Forums (http://forum.springsource.org/), for example, use this login style. Always-resident login forms are good for highlighting the fact that a given website supports user registrations and logins. They’re often located near links for registration and resetting forgotten passwords.

Once the user logs in, it’s common to replace the login form with account settings information or links as well as a logout link. This helps to establish the piece of screen real estate as containing account/session information and options.

You now have a nice login front end. It’s time to attend to back-end issues. Specifically you’re ready to connect the login form to a backing database.

join today to enjoy all our content. all the time.
 

6.4. Sourcing user data from a database

Prerequisite

Recipe 6.1 Implementing login and logout with remember-me authentication

Key technologies

Spring Security, database

Background

In real applications, you want to source authentication data from a persistent store, and a database is a common choice. In this recipe you’ll see how to replace your in-memory user service with one that’s backed by a database.

Problem

Source user authentication data from a database.

Solution

We covered both the DaoAuthenticationProvider class and UserDetailsService interfaces in recipe 6.1, so we won’t rehash that here. Instead we’ll jump right into the changes you need to make. These recipes require that your Maven configuration be set up as described under “Building and Configuration” at http://springinpractice.com/code-and-setup to enable Jetty startup to find the jetty-env.xml configuration file.

The most straightforward approach to using a database back end is to replace the InMemoryDaoImpl user service with a JdbcDaoImpl user service. To do this, you’ll need to perform the following steps:

1.  Create user-related schema and tables in a database.

2.  Grant appropriate permissions to whichever user you’re using. (The sample scripts and configuration assume user sip/sip and database sip06. It’s fine to grant all permissions to that user on the sip06 database.)

3.  Expose the database through JNDI in your servlet container environment.

4.  Use Spring to do a JNDI DataSource lookup.

5.  Update the Spring Security authentication provider configuration.

When all is said and done, you’re targeting the bean graph shown in figure 6.11.

Figure 6.11. Bean dependency graph for a JDBC-backed authentication manager

You’ll see more details as you work through the recipe. Let’s get started by creating the database tables.

Creating the database tables

Although it’s possible to use a custom database schema, in this recipe you’ll use the default Spring Security user schema. In recipe 6.5, you’ll customize the schema.

The default user schema has only two tables: users and authorities, for user credentials and roles, respectively. Figure 6.12 shows the entity-relationship diagram (ERD) for the user schema.

Figure 6.12. ERD for the default user schema. Each user has zero or more roles.

Here’s the MySQL DDL for the schema in figure 6.12:

create table users (
    username varchar(50) not null primary key,
    password varchar(50) not null,
    enabled boolean not null
) engine = InnoDb;

create table authorities (
    username varchar(50) not null,
    authority varchar(50) not null,
    foreign key (username) references users (username),
    unique index authorities_idx_1 (username, authority)
) engine = InnoDb;

Create the tables, and add users and authorities. You can use, for example, the following:

insert into users values ('juan', 'p@assword', 1);
insert into authorities values ('juan', 'user');

You can find the schema and data SQL scripts in the sample code in the src/main/sql folder.

The next step is to use JNDI to expose the database in the servlet container environment.

Exposing the database using JNDI

The specifics of this step are strongly dependent on the servlet container you’re using. If you haven’t already done so, consult your container documentation for instructions on how to do this.

For the sake of this exercise, assume that you’ve bound the DataSource to the jdbc/Sip06DS JNDI name. The code download contains a sample_conf/jetty-env.xml configuration for Jetty with the driver, URL, username, and password set to match some of the assumptions we’ve made (for example, database name is sip06, username is sip).

Next you’ll make the DataSource available to Spring so you can use it as an authentication source.

Looking up the DataSource from Spring

For this step, you’ll create a new application context configuration file. Although it would be possible to place the DataSource lookup directly in beans-security.xml, in general you’d expect to use the DataSource for general persistence needs and not merely security needs. Therefore the DataSource lookup doesn’t belong in beans-security.xml.

Create a new configuration file called beans-data.xml. All it has is a lookup:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

    <jee:jndi-lookup id="dataSource"
        jndi-name="jdbc/Sip06DS" resource-ref="true"/>
</beans>

You also need to update web.xml to point to the new Spring configuration. Change the contextConfigLocation parameter definition to look like this:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:/spring/beans-data.xml
        classpath:/spring/beans-security.xml
    </param-value>
</context-param>

Now you have a DataSource. Next you need to update the Spring Security configuration to use it to source authentication data.

Working with JdbcDaoImpl

By now you may be noticing the common theme that you can make fairly major changes with minimal effort. Swapping a JDBC-backed user service for the in-memory user service is another case in point. Change the <authentication-manager> configuration so it looks like this:

<authentication-manager>
    <authentication-provider>
        <jdbc-user-service data-source-ref="dataSource" />
    </authentication-provider>
</authentication-manager>

Behind the scenes, the <jdbc-user-service> element creates a JdbcDaoImpl (implements UserDetailsService) to inject into the DaoAuthenticationProvider created by <authentication-provider>. Refer to recipe 6.1 for figures and details.

Table 6.1 shows the default SQL queries that JdbcDaoImpl uses for looking up users and roles. Note that these match the tables you created.

Table 6.1. Default SQL queries that JdbcDaoImpl uses to retrieve user data

Description

Query

Gets users by username SELECT username, password, enabled FROM users WHERE username = ?
Gets roles by username SELECT username, authority FROM authorities WHERE username = ?

That concludes the solution part of the recipe. Now you have a database back end for your authentication source. Try it at http://localhost:8080/sip/home.html.

Discussion

In this recipe, you saw how to source authentication data from a database. Although there was a little setup to do with respect to the database and DataSource configuration, presumably most of your apps have to do that anyway. The Spring Security part was trivial.

The default user schema that JdbcDaoImpl expects may or may not serve your needs in any given case. It’s simple and minimalistic, so it won’t be long before you’re looking for ways to customize, expand, or replace it. We’ll cover that topic in recipes 6.5 and 6.6.

Sign in for more free preview time

6.5. Customizing the user database schema

Prerequisites

Recipe 6.4 Sourcing user data from a database

Key technologies

Spring Security, database

Background

In most instances, you’ll want to use something a little more beefy than the default JdbcDaoImpl user schema. The good news is that it’s easy to do.

Problem

Use a custom database schema for authentication.

Solution

As an example, suppose you want to use the schema from recipe 4.4—that is, the three-table schema shown in figure 6.13.

Figure 6.13. ERD for a custom user schema. Compare with figure 6.12.

You can handle this by configuring <jdbc-user-service> to use custom queries, as follows:

<authentication-provider>
    <jdbc-user-service data-source-ref="dataSource"
        users-by-username-query=
            "select username, password, enabled
             from account where username = ?"
        authorities-by-username-query=
            "select a.username, r.name
             from account a, role r, account_role ar
             where ar.account_id = a.id and ar.role_id = r.id
             and a.username = ?" />
</authentication-provider>

This configuration uses the same JdbcDaoImpl user service you’ve been using, but you replace the two lookup queries with custom queries that reflect the underlying schema.

Be sure to run the SQL scripts in the src/main/sql directory of the sample code before starting up the app. Then point the browser at http://localhost:8080/sip/home.html, and you should be running successfully against the new database schema.

Discussion

Being able to customize the database schema is certainly a useful thing to do, but sometimes the customizations you desire are more involved. Recall from the discussion following listing 6.3 that the default principal object for Spring Security user services is an org.springframework.security.core.userdetails.User. Even when you customize the database schema as just described, you’re still stuck with the default User object, which is once again minimalistic. Ideally you’d like to be able to query the principal object for the user’s first and last names, email, address, and so forth.

The next recipe shows how to overcome this limitation by sourcing your user data from the account service you implemented in recipe 4.4.

Tour livebook

Take our tour and find out more about liveBook's features:

  • Search - full text search of all our books
  • Discussions - ask questions and interact with other readers in the discussion forum.
  • Highlight, annotate, or bookmark.
take the tour

6.6. Using a custom user service and user principal

Prerequisites

Recipe 4.4 Saving form data

Recipe 6.5 Customizing the user database schema

Key technologies

Spring Security

Background

In general, user representations include important information beyond credentials and account flags. This might include demographic data (first name, last name, email address), preferences, and more. Because Spring Security makes the authenticated user principal available in the security context, it would be nice to use a more full-featured user instead of the default org.springframework.security.core.userdetails.User. In this recipe, you’ll learn how to do that with a custom user service.

Problem

Enhance the user principal with first name, last name, email address, and so forth.

Solution

For this recipe, you’ll connect the account service you created in recipe 4.4 with Spring Security. You did most of the heavy lifting in that recipe, but there’s still a bit more work to do. Here are the steps you’ll need to carry out:

1.  Adapt the Account domain object from recipe 4.4 to the Spring Security UserDetails interface.

2.  Create a DAO to get the password for the new UserDetails object, because neither Account nor AccountDao exposes the password.

3.  Adapt the AccountService interface from recipe 4.4 to the Spring Security UserDetailsService interface.

4.  Update the Spring configuration to use the new UserDetailsService. (You’ll inject your custom UserDetailsService into the DaoAuthenticationProvider.)

5.  Update subhead.jspf to take advantage of the new UserDetails object. You’ll show the user’s full name instead of only their username.

Figure 6.14 is a class diagram that shows several of the key pieces for this recipe.

Figure 6.14. Class diagram for the DaoAuthenticationProvider. In this recipe, you’ll implement the adapters that allow you to use the account service from recipe 4.4 as a UserDetailsService.

You’ll begin by implementing the UserDetailsAdapter.

Adapting the Account domain object to the UserDetails interface

To perform authentication, Spring Security needs your user principal to implement the UserDetails interface. Although you could add implements UserDetails to the Account object from recipe 4.4, this would be invasive. Instead you’ll create an adapter to make the Account object conform to the UserDetails interface, as shown in the following listing.

Listing 6.6. UserDetailsAdapter.java, which adapts Account to UserDetails

The listing is an adapter to make Account act like a UserDetails. Obviously that involves implementing the UserDetails interface and accepting a backing Account . You expose special Account properties like firstName , lastName, fullName, and email because you want these to be available to the app for rendering or other purposes.

You must also implement the methods that UserDetails expects, such as username, password, various flag properties, and authorities. In the case of username, it’s a pass-through . The password property is different because Account doesn’t include a password property (recall that you left it out for security reasons), so you implement it here and include a setter because the database has a password column. The flag properties other than enabled, on the other hand, have neither corresponding Account properties nor corresponding database columns, so you return true for all of them . Finally, you support the authorities property by mapping roles to authorities .

You now have a custom user principal class, but you still need a way to get the password out of the database. You’ll create a DAO for that.

Creating a DAO for retrieving passwords

This DAO exists solely for the purpose of obtaining user passwords from the database. Although you could go back and modify the account service, that would again be invasive. We’ll continue to show how to adapt the account service without changing it.

The interface for your DAO has a single method:

package com.springinpractice.ch06.dao;

public interface UserDetailsDao {
    String findPasswordByUsername(String username);
}

The next listing is a JDBC-based implementation of the UserDetailsDao interface.

Listing 6.7. JdbcUserDetailsDao.java for password lookups

This DAO uses JDBC instead of Hibernate because you’re not doing any ORM. You inject the JdbcTemplate at and define a simple query at . You query for the password at by specifying the SQL, the username, and the String class.

With that, you have a user principal and a means to populate all of its properties. But what you don’t have is an implementation of the UserDetailsService to inject into the DaoAuthenticationProvider. You’ll take care of that in the next subsection.

Adapting the AccountService interface to the UserDetailsService interface

In the same way that you adapted the Account class to the UserDetails interface, you need to adapt the AccountService interface to the UserDetailsService interface. The following listing is an adapter to do this.

Listing 6.8. UserDetailsServiceAdapter.java, backed by AccountService

The adapter is a service bean. You give it a name so you can reference it from the Spring Security <authentication-provider> configuration. As an adapter, it implements UserDetailsService and accepts a backing AccountService . It also accepts a UserDetailsDao so you have a way to look up the user’s password.

The single method you need to implement is loadUserByUsername() . To do this, you get the Account from the backing AccountService and then validate the account , throwing UsernameNotFoundException as per the UserDetailsService contract. Then you create the UserDetailsAdapter , inject the password , and return the user principal.

The Java part of your effort is now complete, so let’s look at the configuration changes you need to make.

Updating the Spring configuration to use the new UserDetailsService

Now that you’ve added the custom user service from recipe 4.4, you need to update the configuration to support it. There’s also some other configuration to handle, such as the configuration for the JdbcTemplate. Here’s what you need to do for the data tier.

Listing 6.9. beans-data.xml, updated to support the custom user service

As with recipes 6.4 and 6.5, you still have a DataSource . But now you add a JdbcTemplate to help with the password lookup. You also add Hibernate and scan for DAOs because your user service will need those.

In addition to configuring your data tier, you must configure your service tier.

Listing 6.10. beans-service.xml for service configuration

The previous listing is of course entirely standard, and there’s not much to say about it.

The third Spring configuration file you need to update is beans-security.xml. Here, the change is easy. Replace the old <authentication-provider> definition with a new one, as shown:

<authentication-provider user-service-ref="userDetailsService" />

You no longer need the <jdbc-user-service> from the previous recipe, because UserDetailsServiceAdapter (whose ID is userDetailsService; see listing 6.8) is your new user service.

Figure 6.15 shows the bean dependency graph for the DaoAuthenticationProvider bean and its supporting infrastructure. This is a visual summary of the work you’ve already done. The authentication provider at the top is something that Spring Security provides, and the account service at the bottom is what you did in recipe 4.4. The adapter layer, which you just implemented, is the glue that holds these two things together.

Figure 6.15. Bean dependency graph for DaoAuthenticationProvider and its dependencies

You don’t need to change anything for beans-web.xml. But you do need to add the entry for beans-service.xml to web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:/spring/beans-data.xml
        classpath:/spring/beans-service.xml
        classpath:/spring/beans-security.xml
    </param-value>
</context-param>

You also need to modify one of the JSPs slightly, so let’s do that next.

Updating subhead.jspf to display the user’s full name

In previous recipes, you displayed the user’s username in the navigation area because you didn’t have access to user properties beyond those provided by the default Spring Security UserDetails implementation. But now you’re using the UserDetailsAdapter class from listing 6.6, which gives you access to nonstandard properties such as the user’s first name, last name, full name, and email address. You can address the user by their full name by changing subhead.jspf as follows:

Hi, <security:authentication property="principal.fullName" />.

When you run this app, it will display “Hi, Juan Cazares” instead of “Hi, Juan.” Go ahead and try it now: http://localhost:8080/sip/home.html.

Discussion

Recipe 6.6 ties together the custom authentication source you created in recipe 4.4 with Spring Security logins. This gives you a great deal of flexibility as regards your back-end authentication source, and it allows you to use rich user principals in your code and JSPs.

One issue we’ve neglected is storing passwords securely. All the examples so far have involved storing passwords as plain text. Recipe 6.7 remediates this issue.

join today to enjoy all our content. all the time.
 

6.7. Secure user passwords in the database

Prerequisites

Recipe 6.6 Using a custom user service and user principal (code dependency only)

Key technologies

Spring Security, cryptography

Background

When storing passwords, you need to take steps to ensure that nobody can see them, yourself included. This recipe shows how to use password salting and hashing to prevent anybody from viewing user passwords in the database. Although this recipe doesn’t inherently depend on using a custom user service or user principal, you do build on the sample code from recipe 6.6.

Problem

Prevent people (including you) from viewing user passwords stored in the database.

Solution

You’ll use cryptography to secure user passwords in the database, or at rest. The specific technique you’ll use is hashing, which performs a one-way encryption of a user password; that is, hashing doesn’t provide a corresponding decrypt operation. The way it works is as follows. When a user creates an account, you hash the user’s password before storing it in the database. That way, nobody except the user knows what it is, even if the database is compromised. When the same user wants to log in, you take the submitted password, hash it, and compare that hash to the one stored in the database. The authentication succeeds if and only if the hashes are the same. This works because different messages (here, passwords) are extremely unlikely to hash to the same value.

Figure 6.16 illustrates the process of hashing a plaintext message.

Figure 6.16. Hashing a plaintext password into a digest for improved security at rest

Spring Security makes it easy to add password hashing to your app, but to learn how, you need to treat both the initial password creation (as embodied, for example, in a user registration process) and subsequent authentications. The code download for this recipe combines the login work you’ve done in this chapter with the user registration code from recipe 4.4.

First you’ll update the registration process to store password hashes instead of plain text.

Hashing passwords before storing them in the database

The change here is fairly trivial. You need to inject the HbnAccountDao (from recipe 4.4, listing 4.13) with a Spring Security PasswordEncoder and then use the encoder to hash the password before saving it:

package com.springinpractice.ch06.dao;

import org.springframework.security.authentication.encoding.
        PasswordEncoder;

... other imports ...

@Repository
public class HbnAccountDao extends AbstractHbnDao<Account>
    implements AccountDao {

    @Inject private PasswordEncoder passwordEncoder;

    public void create(Account account, String password) {
        create(account);
        String encPassword =
            passwordEncoder.encodePassword(password, null);
        jdbcTemplate.update(UPDATE_PASSWORD_SQL,
            encPassword, account.getUsername());
    }

    ... other members ...

}

The encodePassword() method’s second argument is an optional salt, which you’re not using yet. You’ll see that shortly. For now, you pass in null.

The only other thing to do is add a PasswordEncoder in the app context. You’ll use it both for the initial password creation as well as for logins. To do this, you need to choose an appropriate hashing algorithm. MD5 and SHA-1, although popular choices, have known vulnerabilities. MD5 in particular is no longer considered a secure hash algorithm. Instead, you’ll use SHA-256. Here’s what you need to do to beans-security.xml:

<beans:bean id="passwordEncoder"
    class="org.springframework.security.authentication.encoding.
        ShaPasswordEncoder">
    <beans:constructor-arg value="256" />
</beans:bean>

<authentication-manager>
    <authentication-provider user-service-ref="userDetailsService">
        <password-encoder ref="passwordEncoder" />
    </authentication-provider>
</authentication-manager>

You use the ShaPasswordEncoder and its constructor to specify SHA-256. (Other choices include 1, 384, and 512.) Spring automatically injects this into the HbnAccountDao. This therefore takes care of the registration use case.

For logins, you use the <password-encoder> configuration to endow the DaoAuthenticationProvider with the ShaPasswordEncoder. The <password-encoder> element has an optional hash attribute that you can use to specify a hash algorithm (for example, hash="sha-256"), but you reference an external bean because you need to inject the bean into the HbnAccountDao as noted.

One other item is that the password column in the account database table supports 64 characters because that’s how many characters SHA-256 requires. (Each hexadecimal digit represents 4 bits, and 256/4 = 64.) Because any accounts you created in previous recipes will no longer work (the passwords aren’t hashed), you may as well rebuild the database with the database scripts. (Be sure to run both the schema and the data script.) See the src/main/sql in the code download. If for whatever reason you don’t want to do that, you can as an alternative use this:

ALTER TABLE account MODIFY COLUMN password varchar(64);

At this point, if you run the app, new registrations will create hashed passwords, and logins will hash tendered passwords before comparing with the database hashes. You’ll need to create new accounts (with hashed passwords) through the registration process to log in.

The new password-storage scheme offers considerably better security than plain text, which is completely insecure. But it has some important weaknesses. The most significant is that despite the fact that hash algorithms don’t support a decrypt operation, you (wearing your black hat) can do something that’s almost as good: precompute hashes for all the words in a dictionary and use the result as a reverse-lookup table. Although this won’t necessarily allow you to recover every password in the database, it will allow you to recover any password that’s a dictionary word, and plenty of users use dictionary words for passwords. You need something to defend against so-called dictionary attacks, and that’s where salt comes in.

Defending against dictionary attacks using salt

The idea behind salt is to add extra bits to user passwords to ensure that they aren’t dictionary words, thus making dictionary attacks harder to execute. There are various approaches to salt. One best practice is to associate a large, random set of bits with each user and append it to each password before hashing it. This makes it much more costly to precompute reverse-lookup tables because a table must be created for each possible combination of bits.

Here you’ll do something that’s weaker but still an improvement over simple hashing: you’ll use the user’s ID as a salt. We can best explain how Spring Security will use this by way of example. Suppose you have a user with ID 27 and password maxmax. Spring Security will incorporate the chosen salt (here, the ID) into a nondictionary password as follows: maxmax{27}. This is weaker because you can easily imagine somebody precomputing a catalog of reverse-lookup tables to attack apps that use Spring Security and this particular salt scheme. But it would require a table for each ID, which means more effort than a single table of dictionary word hashes. But if your security needs are more stringent, you would be well advised to consider a stronger salt scheme, such as the large random bitset we described.

Figure 6.17 shows how you’ll encode passwords using salting and hashing.

Figure 6.17. Improving security at rest by incorporating a variable salt

To add salt, once again you need to add it to both the password-creation and authentication processes. Here’s what HbnAccountDao looks like with salt added:

package com.springinpractice.ch06.dao;

import org.springframework.security.authentication.dao.SaltSource;

... other imports ...

@Repository
public class HbnAccountDao extends AbstractHbnDao<Account>
    implements AccountDao {
    @Inject private SaltSource saltSource;

    public void create(Account account, String password) {
        create(account);
        Object salt = saltSource.getSalt(new UserDetailsAdapter(account));
        String encPassword =
            passwordEncoder.encodePassword(password, salt);
        jdbcTemplate.update(UPDATE_PASSWORD_SQL, encPassword,
            account.getUsername());
    }

    ... other members ...

}

What you’re doing is similar to what you did with the PasswordEncoder. You inject a SaltSource and then use it to generate a salt from the account in a way you’ll see. Note though that you need to wrap the account with a UserDetailsAdapter because the getSalt() method expects a UserDetails argument. You update the call to encodePassword() by passing in the salt instead of null.

The new beans-security.xml configuration looks like this:

<beans:bean id="saltSource"
    class="org.springframework.security.authentication.dao.
            ReflectionSaltSource"
    p:userPropertyToUse="id" />

<authentication-manager>
    <authentication-provider user-service-ref="userDetailsService">
        <password-encoder ref="passwordEncoder">
            <salt-source ref="saltSource" />
        </password-encoder>
    </authentication-provider>
</authentication-manager>

You’ve added a ReflectionSaltSource bean to generate salts from the user’s id property, and as with the PasswordEncoder, Spring automatically injects this into the HbnAccountDao so you can use it during account creation. You also configure the salt source into the DaoAuthenticationProvider so you can use it during logins.

Once again, this configuration effectively invalidates any existing accounts, because their passwords won’t be recognizable anymore. But see the “Quick tip” sidebar.

Quick tip: preserving legacy passwords

If you want to upgrade a plaintext password-storage scheme to a hashed storage scheme, that’s easy enough: you replace the plaintext passwords with hashed versions. But what if you want to upgrade a legacy hashed password-storage scheme to a salted, hashed scheme? Here you don’t have the original passwords, so you can’t recompute the passwords.

The answer is to use two authentication providers: the one with the salt source and the one without. Put them both inside the <authentication-manager> element, because it accepts a list of <authentication-provider> children. Problem solved!

Your password-storage scheme is now significantly more secure than the plaintext scheme you’ve been using up to this point.

Discussion

This recipe illustrated the link between the account-creation process and the authentication process. In general, the two processes need to be coordinated so that created accounts can serve in authentication contexts.

Something you’ll notice if you run the sample code for this recipe is that you aren’t automatically logged in after you register an account. That’s an annoyance and another example of where it makes sense to coordinate account creation with authentication. The final recipe in this chapter shows how to fix that.

6.8. Auto-authenticating the user after a successful registration

Prerequisites

Recipe 4.4 Saving form data

Recipe 6.4 Sourcing user data from a database

Recipe 6.7 Securing user passwords in the database (code dependency only)

Key technologies

Spring Security

Background

Users expect applications to authenticate them automatically after creating an account. This recipe shows how to do this with Spring Security.

This recipe builds on the code from recipe 6.7, but it doesn’t depend on hashing or salting. The sample code for recipe 6.7 is convenient in that it includes both the registration component and the login component.

Problem

Automatically authenticate the user after a successful new user registration.

Solution

This requires a couple of minor modifications to the sample code from recipe 6.7:

  • Update the AccountController to perform the auto-authentication immediately following a successful user registration.
  • Update beans-security.xml to support injecting your authentication manager into the controller.

Let’s handle the controller first. You add an AuthenticationManager dependency to the AccountController class:

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
...
public class AccountController {

    @Inject
    @Qualifier("authenticationManager")
    private AuthenticationManager authMgr;

    ...

}

You use Spring’s @Qualifier annotation because it turns out there are two AuthenticationManager beans on the app context—one created as part of the <http> element and one created as part of the <authentication-manager> element—and you need a way to specify the one you want to inject. (Note that javax.inject also has an @Qualifier annotation, but it has different semantics. You’re using the Spring version.)

You also have to modify AccountController to use the authentication manager to perform the auto-authentication:

import org.springframework.security.authentication.
        UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

...

public class AccountController {

    @RequestMapping(value = "", method = RequestMethod.POST)
    public String postRegistrationForm(
            @ModelAttribute("account") @Valid AccountForm form,
            BindingResult result) {

        convertPasswordError(result);
        String password = form.getPassword();
        accountService.registerAccount(toAccount(form), password, result);

        Authentication authRequest =
            new UsernamePasswordAuthenticationToken(
                form.getUsername(), password);
        Authentication authResult = authMgr.authenticate(authRequest);
        SecurityContextHolder.getContext().setAuthentication(authResult);

        return (result.hasErrors() ? VN_REG_FORM : VN_REG_OK);
    }
    ...
}

The part you care about starts where you create the authentication request. You feed it the username and password. To authenticate the token, you need to pass it into the authenticate() method on the AuthenticationManager. The authentication should succeed because you’ve just created the associated account. After you have the authenticated token, you place it on the SecurityContext. The user is now authenticated.

There’s one small detail you need to handle in the beans-security.xml configuration. Because there are (as we mentioned) two AuthenticationManagers, you can’t rely on type-based injection. You used @Qualifier to identify the bean you want to inject, which is authenticationManager. The problem is that the bean’s actual ID is org.springframework.security.authenticationManager, and that’s an internal ID that Spring Security gives it rather than a published ID. To reference this bean, you need to give it an explicit alias. You do this as follows:

<authentication-manager alias="authenticationManager">
    ...
</authentication-manager>

With those changes, you now have a registration process that automatically authenticates the user after a successful registration.

Discussion

This final authentication recipe showed how to handle an important detail when integrating registration with authentication: auto-authenticating the user on a successful registration. You were able to accomplish this using the AuthenticationManager in a programmatic fashion.

Sign in for more free preview time

6.9. Summary

You should now have a reasonable understanding of how to implement logins and logouts using Spring Security. We began by looking at different login UI options, then moved on to back-end options as well. Toward the end, you saw how to integrate different aspects of account creation with authentication, such as hashing, salting, and auto-authentication.

In chapter 7, we’ll consider one of the major use cases for authentication: authorization. Authorization is the process of determining for a given user whether they’re allowed to access a given resource. Once again, Spring Security provides a rich set of tools to handle the job.

sitemap

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage