Spring Custom UserDetailsService Example

In this post, we will be create a spring custom userdetailsservice example. When using Spring Framework, you may want to create Custom UserDetailsService to handle retrieval of user information when logging in as part of Spring Security. This can also be use if you want to create your custom login in spring.

1.Create Spring MVC Project

First, create your spring mvc project. You can visit this link on how to create spring mvc using maven. Next modify your pom.xml and add Spring Security Dependencies:


    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    ${spring.security.version}



    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    ${spring.security.version}



    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    ${spring.security.version}

and add spring security version in your properties section


     3.2.1.RELEASE

2. Create your Custom UserDetailsService Class

Create a class that will implements UserDetailsService.

CustomUserDetailsService.java

package com.javapointers.custom;

import com.javapointers.service.UserService;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserService userService;

    static final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Map<String, Object> userMap = userService.getUserByUsername(s);

        //check if this user with this username exist, if not, throw an exception
        // and stop the login process
        if (userMap == null) {
            throw new UsernameNotFoundException("User details not found with this username: " + s);
        }

        String username = (String) userMap.get("username");
        String password = (String) userMap.get("password");
        String role = (String) userMap.get("role");

        List<SimpleGrantedAuthority> authList = getAuthorities(role);

        //get the encoded password
        String encodedPassword = passwordEncoder.encode(password);

        User user = new User(username, encodedPassword, authList);

        return user;
    }

    private List<SimpleGrantedAuthority> getAuthorities(String role) {
        List<SimpleGrantedAuthority> authList = new ArrayList<>();
        authList.add(new SimpleGrantedAuthority("ROLE_USER"));

        //you can also add different roles here
        //for example, the user is also an admin of the site, then you can add ROLE_ADMIN
        //so that he can view pages that are ROLE_ADMIN specific
        if (role != null && role.trim().length() > 0) {
            if (role.equals("admin")) {
                authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
            }
        }

        return authList;
    }
}


The method loadUserByUsername is the method that Spring uses to retrieve user information. Here in this method, you can add your own logic how you will get your user information for login process.

The getAuthorities method is a method that we have created to return a list of authorities that the user has. For example, if the user is also an admin of the site, in addition to ROLE_USER, we can also add ROLE_ADMIN so that the user can also access pages that are admin specific.

Next, we create UserService class that handles the retrieval of user information.

UserService.java

package com.javapointers.service;

import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service("userService")
public class UserService {

    public Map<String, Object> getUserByUsername(String username) {
        Map<String, Object> userMap = null;
        //logic here to get your user from the database
        if (username.equals("admin") || username.equals("user")) {
            userMap = new HashMap<>();
            userMap.put("username", "admin");
            userMap.put("password", "password");
            //if username is admin, role will be admin, else role is user only
            userMap.put("role", (username.equals("admin")) ? "admin" : "user");
            //return the usermap
            return userMap;
        }
        //if username is not equal to admin, return null
        return null;
    }
}

In this class, we can add the logic of getting the user information from the database. For our example, the user information are just hard coded for simplicity. It only contains the user admin and user.

Next, We create a simple Controller that handles the landing page when the user successfully login. The /home is called if the user is successfully login.

HomeController.java

package com.javapointers.controller;

import com.javapointers.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/home", method = RequestMethod.GET)
    public String viewHome() {
        return "home";
    }

    @RequestMapping(value = "/admin", method = RequestMethod.GET)
    public String viewAdmin() {
        return "admin";
    }
}

3.Configure XML Files

First, configure your web.xml file.





    
    
        contextConfigLocation
        
            /WEB-INF/applicationContext.xml
        
    

    
    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        1
    
    
        dispatcher
        /
    

    
    
        org.springframework.web.context.ContextLoaderListener
    

    
    
        springSecurityFilterChain
        org.springframework.web.filter.DelegatingFilterProxy
    
    
        springSecurityFilterChain
        /*
    


The Spring Security Filter filters the request so that any request from unauthorized or not logged in users will be denied.

Next, in your dispatcher-servlet.xml, add the view resolver for jsp.




    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />


Next, configure applicationContext.xml and create a bean for our custom userdetailsservice. Then, create our own authentication-manager and add the custom userdetailsservice bean that we have created. Next, we have also configured the http interceptor that intercepts specific url pattern and check if the user has access to this page. For example,

<intercept-url pattern="/home" access="hasRole('ROLE_USER')" />

means that /home url can only be access by login users. and the

<intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />

can only be access by users that has admin role. Lastly, we have also added the form-login and define the login page, the page that will be return when successfully login, the  error page when login fails and also the logout url. Here is the full applicationContext.xml



    <mvc:annotation-driven />
    <context:component-scan base-package="com.javapointers" />

    
        
        <intercept-url pattern="/home" access="hasRole('ROLE_USER')" />
        <intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />

        <form-login login-page="/login" default-target-url="/home" authentication-failure-url="/login?error=" />
        <logout invalidate-session="true" logout-success-url="/logout" />
    

    
        
            <password-encoder ref="encoder" />
        
    

    <beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
    <beans:bean id="customUserDetailsService" class="com.javapointers.custom.CustomUserDetailsService" />

    
    <mvc:view-controller path="/login" view-name="login" />
    <mvc:view-controller path="/logout" view-name="logout" />

4.JSP and Testing

These are our jsp files:

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
    <style>
        #error{
            display: none;
            color: red;
        }
    </style>
</head>
<body>
<h2>Please Login!</h2>
<form action="j_spring_security_check" method='POST'>
    Username: <input type="text" name="j_username" /><br />
    Password: <input type="password" name="j_password" /><br />
    <div id="error">
        Invalid username and/or password!
    </div>
    <br/>
    <input type="submit" value="Login" />
    <script>
        var field = 'error';
        var url = window.location.href;
        if(url.indexOf('?' + field + '=') != -1){
            document.getElementById("error").style.display="block";
        }
    </script>
</form>
</body>
</html>

home.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
</head>
<body>
    <h2>Welcome to JavaPointers!</h2>
</body>
</html>

admin.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
</head>
<body>
    <h2>ADMIN PAGE</h2>
</body>
</html>

logout.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
</head>
<body>
    <h2>You have successfully logout</h2>
</body>
</html>

The login page,
login

When the user with username admin logs in, he can view all pages:

home

admin

 

but when the user with username user logs in, trying to access the admin page gives an error:

admin-error

the logout page

logout

 

Download source code here!

 

Share this tutorial!