Using Spring-security in Java (2)

In the previous article, we talked about the basic use of spring security. But for some complex permission scenarios, we need more advanced functions.

We continue to show its advanced part.

<security: authentication-manager> internal advanced settings

In the previous Spring security setup example, I set up authentication-manager to check the login user credentials and use the plain text user defined in the label. As shown below, you can define multiple users for your application here.

<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>

The above method is simple verification. For more complex things, such as authentication to the Users table in the database, you can use <security:jdbc-user-service> to replace the <security:user-service>… </security:user-service> tag. as follows.

<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 execute a SQL query to get the username and password from the "users" table in the database.

Similarly, the authorization permissions of the user name are also obtained from the "user_roles" database table.

Here you can notice that I mentioned the data source reference in the "data-source-ref" attribute. This is "dataSource".

Therefore, you need to define a bean with id="dataSource" in the application context xml file. as follows.

<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 provided a placeholder in the "value" of the database attribute tag above. You can replace them with actual values.

If you want to authenticate the Users table in the database through the data access object layer (DAO) @Service, you can configure it as follows.

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

Here, you execute a SQL query to get the username and password from the "users" table in the database.

Similarly, the authorization permissions of the user name are also obtained from the "user_roles" database table.

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

Spring Security will use a repository service named "loginService" to obtain authentication.

We can create a data access object interface and implementation for our login service.

For example: we create an interface java class named "LoginDAO.java"

package com.stiti.dao;

import com.stiti.model.AppUser;

public interface LoginDAO {

    Object findUserByUsername(String username);

}

com.stiti.model.AppUser and AppUserRole are Model classes.

You can use your own way to obtain the database user and user role table and define the *findUserByUsername(String username)* function body.

*findUserByUsername(String username)* returns AppUser type object.

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 AppUser type object.

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 for the main path of the application in the Controller class, which is in my example "/". When the user opens the application URL such as " http://www.example.com/ ", the following method "loadHomePage()" defined for the request mapping is executed. In this method, it first obtains user authentication and authorization for this URL.

Spring-security will first check the <security:intercept_url> in the spring configuration to find the roles that are allowed to access this url path. In this example, it looks to allow users with the role "USER" to access this URL path. If the user has a role = USER, then it will load the home page.

Otherwise, if it is an anonymous user, Spring Security Verification will redirect it to the login page.

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 the RequestMapping method in our Controller class to load a custom "login page", provided that you mentioned the custom login page URL in the Spring <security: http> <security: form-login …> tag. Otherwise, there is no need to define any controller method for the Login page. Spring will automatically take the user to the default login form page of spring-security, which is a simple JSP page written by spring-security itself.

// 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 the "logout-success-url" defined in the <security:logout> tag in the spring-security config. For this example, I defined "logout-success-url" as "/logout".

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

    return "Login";

}

##Write the Login form in Login.jsp
Now let's take a look at what Login.jsp should contain in the login form.

<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>

in conclusion

In order to implement security in an application, a developer must perform many operations in his application. Spring security replaces all these overheads by simplifying the method. It is easy to plug into the application, and Spring Security itself can handle all the security aspects of the application and provide strict security for your application.

Thinking:

How many ways does authentication-manager verify username/password?

answer:

There are at least three,

security: user configures a fixed username/password method, the simplest;

jdbc-user-service configures the sql statement verification method