Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

Spring Security in Java

4.44/5 (9 votes)
2 Apr 2015CPOL11 min read 42.9K  
In this article, I'm going to discuss what is a Security in the application, what is Spring Security, and how to integrate Spring Security in your application.

Contents

Introduction

This article is all about implementing Spring Security in your Spring MVC web application. After reading this article, you will get an idea about how Spring Security works and how you can integrate Spring security in your spring application easily.

Background

Anyone reading this article should have knowledge of Core Java, J2EE and Spring MVC.

What Is a Security in the Application?

It is all about securing the resources of your application by providing authentication to the privileged users of the application.

What is Spring Security?

It's a Java based security solution. It is mostly applicable for the Java based web applications that are built upon Spring framework. It provides comprehensive security services for J2EE based enterprise software applications. It's very powerful and has a lot more flexibility. You can plug-in the spring security service to your application very easily.

Authentication and Authorization are two main operations that are included in Spring Security:

  1. Authentication is something that asks the user to provide valid credentials to login to the application.
  2. Authorization means validating what role(s) or privilege(s) the login user has for this application based on which the user will be allowed to access the different controls or functionality in the application.

So on a whole, Authentication validates the identity of a user, if the identity is authenticated, then it is used to decide the authorization of the user.

Spring Security Integration in Your Application

Before going into this, let's have a brief on how a web application works.

In web applications, normally the execution process is as follows:

  • Client hits a URL in the browser to open a web application.
  • It then reaches a Servlet Container (e.g Tomcat).
  • Where the Servlet Container checks the web.xml to check some web configurations and finds where I need to go.
  • The Servlet then sends Request to the Servlet.
  • And Servlet processes the Request and sends the Response to the Servlet Container.
  • And Servlet Container then sends that response to the client Web Browser.

Afterwards, the concept of Interceptor is introduced in J2EE. You can intercept or filter the Request between the Servlet Container and Servlet, such that before the request reaches the Servlet, you can do some of the pre processing and post processing jobs.

For example, you can do some security stuff, logging, or you can perform any logic to redirect the request to a specific URI based on the type of request or type of client from which the request has come in.

Now, let's see how to integrate Spring Security in your solution to build a User Login portal.

Let's take an example of a web application where as soon as user will enter and execute a URL of a web application in the web browser, it will first open a Login page where user has to enter his/her login credentials and submit them for authentication process. And if authenticated successfully, then the user will be taken to the application Home page. If authentication failed, then user will be taken back to the Login page.

Note: Here, I will focus on authentication via hard coded user name defined in the Spring Security configuration. And then, I will show you how to develop authentication via database using Spring Security.

Now let's see how we can implement this application logic using Spring Security.

Spring Security Set Up

First, you need to download the spring security dependency jars. Below are the 3 basic jars that need to be downloaded, which can perform spring-security for basic authentication and web application authentication.

There are other jars in spring-security which provide other advanced authentication techniques, such as: LDAP auth, OpenID auth, implement Remember me option in authentication, etc. But here, I will focus on web application authentication.

In the servlet configuration XML file or your application context XML, you need to specify the below URLs in the xsi:schemaLocation of the <beans .. > tag.

XML
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.1.xsd">
....
....
....
</beans>

To enable the Spring Security, you need to add the following filters to the /WEB-INF/web.xml.

XML
<!-- Enable Spring Security -->
<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>

So here in the <filter> tag, you need to define the filter-name and filter-class, like you define the servlet-name and servlet-class for your application. But here, the filter-name and filter-class are fixed or predefined as given above.

In the <filter-mapping> tag, you define the path that should pass through the spring security checks.

The springSecurityFilterChain filter, i.e., the DelegatingFilterProxy servlet delegates the request to a set of filters and interceptors defined in your spring security configuration in the application context. Such as authentication, authorization via the defined interceptors.

Spring Security Configuration

You need to define the following tags under the <beans...>....</beans> tag in your application context XML file (or servlet config XML file).

XML
<security:http auto-config="true" use-expressions="true">
    <!-- Interceptor urls -->
    <security:intercept-url pattern="/login**" access="permitAll" />
    <security:intercept-url pattern="/resources/**" access="permitAll" />
    <security:intercept-url pattern="/**" access="hasRole('USER')" />
    <security:intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
    
    <security:form-login />
            
    <!-- Logout -->
    <security:logout logout-success-url="/logout" />
</security:http>

Inside the <security:http ... > tag, you need to mention what resources you want to secure. As you can see, I have defined four interceptors using <security:intercept-url> tags.

Here, I have mentioned what URLs or URL patterns are permitted to all users including anonymous users and what URLs or areas are restricted to accessible by certain User Roles only.

Such as in the above code snippets, I have mentioned the “login” page, any pages inside “/resources/” path are accessible to everyone including anonymous users.

As every user should go to the Login Form page and the reason why I gave the “/resources/**” path access to anonymous users is because I have kept all my application CSS, JavaScripts and images inside “/resources/” path.

Also you can notice, I have restricted the “/admin” paths, i.e., any page inside /admin path can be accessed by the users having ADMIN role, and any page inside root path i.e., “/” is accessible to the authenticated users having USER privilege/role.

Spring-security provides a default built-in login page, which has two text boxes to enter the Username and Password, and a Submit button to submit the credentials for validation. You don't have to design a login form for your application. As soon as the user opens the application URL on his browser, spring-security checks if the user has not logged in, then it redirects the user to the default login form provided by spring-security.

If you want to use a custom login page for your application, then you can configure spring-security to use your custom login page instead. You can use the <security:form-login> tag to define your custom login form page within the <security:http> … </security:http> tag. Below is the configuration example.

XML
<security:http auto-config="true" use-expressions="true">

    <security:intercept-url pattern="/login**" access="permitAll" />
    <security:intercept-url pattern="/resources/**" access="permitAll" />
    <security:intercept-url pattern="/**" access="hasRole('USER')" />
    <security:intercept-url pattern="/admin**" access="hasRole('ADMIN')" />

    <!-- Login Form -->
    <security:form-login 
        login-page="/login"
        default-target-url="/"
        authentication-failure-url="/login?error=1" 
        username-parameter="username"
        password-parameter="password" />
         
    <!-- Logout -->
    <security:logout logout-success-url="/logout" />
        
</security:http>

In the <security:form-login ...> tag,

  • In “login-page”, you can specify the custom login page URL path.
  • In “default-target-url”, you can specify the URL where the user should navigate after successful login.
  • In “authentication-failure-url”, you can specify the URL where the user should navigate after a login failure.
  • username-parameter” and “password-parameter” - These two are optional. By default, spring-security accepts the parameter names “j_username” and “j_password” as username and password in the login form. If you want to give any other name to the username and password input fields in the login form, then you can specify the custom parameter names in these two attributes in <security:form-login ...> tag.

In the <security:logout ...> tag:

  • In “logout-success-url”, you can specify the page URL path which should execute when user uses spring-security logout process.

After the end of </security:http> tag, you need to add the below tag which is <security:authentication-manager>. This is meant for Authentication manager.

XML
<security:authentication-manager>
    <security:authentication-provider>
        <security:user-service>
            <security:user name="stitis" password="mindfire" authorities="USER" />
        </security:user-service>
    </security:authentication-provider>
</security:authentication-manager>

As I mentioned earlier in this article that before the user request reaches the Servlet, i.e., in case of applications built on spring framework, then we can say that before the request reaches the Spring Dispatcher Servlet, it goes through the spring security filter chain and during the filter process, it does certain pre processing and post processing. And these pre and post processing are nothing but the Authentication and Authorization (or we can call it as Security checks) processes.

In the above code snippets, the <security:authentication-manager>, <security:authentication-provider> and <security:user-service> are meant for Authentication. The authentication-manager delegates the user credentials to UserDetailServices, i.e., the User Repository to check and get the user identity and granted authorities. And <security:http> is meant for the security checks or authorization.

That's all on the Spring Security setup and configuration part.

Tips on Advanced Setup Inside <security:authentication-manager>

In the above example spring security setup, I have set up the authentication-manager to check the login user credential with the plain text user defined in the <user-service> tag. You can define multiple users for your application here like shown below:

XML
<security:user-service>
    <security:user name="stiti" password="mindfire" authorities="USER" />
    <security:user name="ram" password="pass1234" authorities="ADMIN" />
    .
    .
    and so on...
</security:user-service>

If you want to do the authentication against a Users table from the Database, then you can replace the <security:user-service> ... </security:user-service> tag with the <security:jdbc-user-service> as follows:

XML
<security:authentication-manager>
    <security:authentication-provider>
        <security:jdbc-user-service 
            data-source-ref="dataSource"  
            users-by-username-query=
                "SELECT username, password FROM users WHERE username=? AND active=1" 
            authorities-by-username-query=
                "SELECT US.username, UR.authority 
                    FROM users US, user_roles UR 
                    WHERE US.user_id = UR.user_id and US.username =?" 
            />
    </security:authentication-provider>
</security:authentication-manager>

Here, you are executing SQL queries to get the username and password from "users" table in the database.

Similarly, the granted authorities for the username is also fetched from the "user_roles" database table.

Here, you can notice that I have mentioned the datasource reference in the "data-source-ref" property. Which is "dataSource".

So you need to define a Bean with id="dataSource" in your application Context XML file as follows:

XML
<beans ....>
...
...
...
    <!-- Datasource Config -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.databaseurl}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
...
...
</beans>

I have provided placeholders in the "value" of the above database property tags. You can replace them with actual values.

If you want to do the authentication against a Users table from the Database via the Data Access Object Layer (DAO) @Service, then you can do the configuration as follows:

XML
<security:authentication-manager>
    <security:authentication-provider user-service-ref="loginService">
    </security:authentication-provider>
</security:authentication-manager>

Here, you are executing SQL queries to get the username and password from "users" table in the database.

Similarly, the granted authorities for the username is also fetched from the "user_roles" database table.

Here, you can notice that I have mentioned user-service-ref="loginService" in the <security:authentication-provider> tag.

The spring security will get the authentication using a repository service which should be named as "loginService".

We can create Data access object interface and implementation for our Login Service. Let's create an interface Java class called "LoginDAO.java":

Java
package com.stiti.dao;

import com.stiti.model.AppUser;

public interface LoginDAO {

    Object findUserByUsername(String username);
}

Let's create an implementation Java class called "LoginDAOImpl.java" and define it as Repository. Annotate the class as @Transactional so that all the methods inside this class can be called by the Service class.

com.stiti.model.AppUser and AppUserRole are the Model classes. I have used Hibernate to do the DB operations, you can use your own way to fetch the DB Users and User Roles tables and define the findUserByUsername( String username ) function body.

findUserByUsername( String username ) returns an AppUser type object.

Java
package com.stiti.dao.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.HibernateException;

import org.springframework.stereotype.Repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.stiti.dao.LoginDAO;
import com.stiti.model.AppUser;
import com.stiti.model.AppUserRole;

/**
 * @author Stiti Samantray
 */
@Repository("loginDao")
@Transactional
public class LoginDAOImpl implements LoginDAO {

    @Autowired
    SessionFactory sessionFactory;

    /**
     * Finds the AppUser which has a matching username
     * @param username
     * @return
     */
    @Override
    public AppUser findUserByUsername( String username )
    {
        Session session = sessionFactory.getCurrentSession();

        List<AppUser> users = new ArrayList<AppUser>();
        List<Object> userData = new ArrayList<Object>();
        Set<AppUserRole> userRoles = new HashSet<AppUserRole>(0);

        try {
            String hql = "FROM AppUser U WHERE U.username = :username";
            org.hibernate.Query query = session.createQuery(hql)
                    .setParameter("username", username);
            users = query.list();
        } catch (HibernateException e) {
            System.err.println("ERROR: "+ e.getMessage());
        }

        AppUser user = null;

        if(users.size() > 0) {
            user = (AppUser) users.get(0);

            // Get the user roles
            try {
                String hql = "FROM AppUserRole R WHERE R.username = :username";

                org.hibernate.Query query = session.createQuery(hql)
                        .setParameter("username", username);

                userRoles = new HashSet<AppUserRole>(query.list());
            } catch (HibernateException e) {
                // You can log the error here. Or print to console
            }

            user.setUserRole(userRoles);
        }

        return user;
    }
}

findUserByUsername( String username ) returns an AppUser type object.

Java
package com.stiti.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import org.springframework.stereotype.Service;

import com.stiti.model.AppUser;
import com.stiti.model.AppUserRole;
import com.stiti.dao.LoginDAO;

/**
 * This class gets the appuser information from the database and 
 * populates the "org.springframework.security.core.userdetails.User" type object for appuser.
 *
 * @author Stiti Samantray
 */
@Service("loginService")
public class LoginServiceImpl implements UserDetailsService {

    @Autowired
    private LoginDAO loginDao;

    /**
     * @see UserDetailsService#loadUserByUsername(String)
     */
    @Override
    public UserDetails loadUserByUsername( String username ) throws UsernameNotFoundException
    {
        AppUser user = (AppUser) loginDao.findUserByUsername(username);        
        List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
        return buildUserForAuthentication(user, authorities);
    }

    private List<GrantedAuthority> buildUserAuthority(Set<AppUserRole> appUserRole) {
        Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
        // Build user's authorities
        for (AppUserRole userRole : appUserRole) {
            System.out.println("****" + userRole.getUserRole());
            setAuths.add(new SimpleGrantedAuthority(userRole.getUserRole()));
        }
        List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
        return Result;
    }

    private User buildUserForAuthentication(AppUser user, List<GrantedAuthority> authorities) {
        return new User(user.getUsername(), user.getPassword(), 
                        true, true, true, true, authorities);
    }
}

Write the Controller of the Spring MVC Application

Now we need to write the Controller of the Spring MVC application.

We need to define a RequestMapping method in our Controller class for the home path of the application which is in my example “/”. When user opens the application URL, e.g., “http://www.example.com/”, the below method “loadHomePage()” defined for this request mapping is executed. In this method, it first gets the user authentication and authorization to this URL.

Spring-security will first go and check the <security:intercept_url>s in the spring configuration to find what role is allowed to access this URL path. In this example, it finds the users with role “USER” are allowed to access this URL path. If the user is having a role = USER, then it will load the Home page.

Otherwise, if it is an Anonymous user, then spring security will redirect them to the login page.

Java
package com.stiti.controller;

import java.util.HashSet;
import java.util.Set;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;

@Controller
@SessionAttributes(value={"accountname"}, types={String.class})
public class HomeController {

    @SuppressWarnings("unchecked")
    @RequestMapping(value="/", method = RequestMethod.GET)
    public String executeSecurityAndLoadHomePage(ModelMap model) {

        String name = null;
        Set<GrantedAuthority> role = new HashSet<GrantedAuthority>();

        //check if user is login
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (!(auth instanceof AnonymousAuthenticationToken)) {
            // Get the user details
            UserDetails userDetail = (UserDetails) auth.getPrincipal();
            name = userDetail.getUsername();
            role = (Set<GrantedAuthority>) userDetail.getAuthorities();
        }

        // set the model attributes
        model.addAttribute("accountname", name);
        model.addAttribute("userRole", role);

        // go to Home page
        return "Home";
    }
}

Define a RequestMapping method in our Controller class to load the custom “Login Page”, provided if you have mentioned the custom login page URL in your Spring <security:http><security:form-login … > tag . Otherwise, there is no need to define any controller method for Login page, spring will automatically take the user to spring-security's default login form page, which is a simple JSP page written by spring-security itself.

Java
// Show Login Form
@RequestMapping(value="/login", method = RequestMethod.GET)
public String login(ModelMap model, 
        @RequestParam(value="error",defaultValue="") String error) {

    // If fails to login
    if (!error.isEmpty()){
        model.addAttribute("error", "true");
    }

    return "Login";
}

Define a method in our Controller class for “logout-success-url” defined in the <security:logout> tag in spring-security config. For this example, I have defined the “logout-success-url” as “/logout”.

Java
// Logout page
@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logout(ModelMap model) {

    return "Login";

}

Write the Login Form in the Login.jsp

Now let's see what the Login.jsp should contain in the login form.

HTML
<form name='login_form' action="<c:url value='j_spring_security_check' />" method='POST'>
    <table>
        <tr>
            <td>User:</td>
            <td><input type='text' name='username' value=''></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type='password' name='password' /></td>
        </tr>
        <tr>
            <td colspan='2'><input name="submit" type="submit" value="submit" /></td>
        </tr>
        <tr>
            <td colspan='2'><input name="reset" type="reset" /></td>
        </tr>
    </table>
</form>

Here, the Form action is submitted to "/j_spring_security_check".

The "j_spring_security_check" - is a Servlet where the actual authentication is made and you must map the action of your login form to this Servlet.

/j_spring_security_check” URL must be processed by springSecurityFilterChain filter in web.xml.

Conclusion

To implement security in the application, a developer has to do a lot of things in his application. Spring security substitutes all these overheads by simplifying the methods. It's easy to be plugged in to the application and spring security itself handles all the security aspects of your application and provides a tight security to your application.

History

  • 2nd April, 2015: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)