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

     

    Related Post

    • Alejandro Ramírez

      gracias