Spring Security Redirect based on User Role

I want to implement the different navigation from the login page based on the roles of users.

Admin should be redirected to Admin Dashboard from login

User should be redirected to User Dashboard from login 

1. Implement the hasRole() method in ResidentUser class (Domain & Security)

If you are following my previous tutorial, I didn't send the role flag(admin) to database, so It always set to 0. I don't have many-to-many relationship for the class. I have a single class as "ResidentUser" which also has the role flag(admin) as Integer.


#ResidentUser.java
package shangrila.council.app.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/* Resident_user is a Domain class or JPA entity */
@Entity
@Table(name = "resident_user")
public class ResidentUser {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name="UserID")
	private int userID;
	
	@Column(name="FullName", nullable=false)
	private String fullName;

	@Column(name="DoB")
    private String dateOfBirth;
	
	@Column(name="Address", nullable=false)
    private String homeAddress;
	
	@Column(name="Email", unique=true, nullable=false)
	private String email;
	
	@Column(name="PasswordHash", nullable=false)
	private String password;
	
	@Column(name="SNI", unique=true, nullable=false)
	private String sniNumber;
	
	@Column(name="admin")
	private int admin;
	public ResidentUser() { System.out.println("ResidentUser Default constructor"); }
	
	public ResidentUser(String fullName, String dateOfBirth, String homeAddress,
			            String email, String password, String sniNumber, int admin) {
		super();
		this.fullName = fullName;
		this.dateOfBirth = dateOfBirth;
		this.homeAddress = homeAddress;
		this.email = email;
		this.password = password;
		this.sniNumber = sniNumber;
		this.admin = admin;
		
		System.out.println("Model: ResidentUser\n");
	}

	public int getUserID() {
		return userID;
	}

	public void setUserID(int userID) {
		this.userID = userID;
	}

	public String getFullName() {
		return fullName;
	}

	public void setFullName(String fullName) {
		this.fullName = fullName;
	}

	public String getDateOfBirth() {
		return dateOfBirth;
	}

	public void setDateOfBirth(String dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}

	public String getHomeAddress() {
		return homeAddress;
	}

	public void setHomeAddress(String homeAddress) {
		this.homeAddress = homeAddress;
	}
	
	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getSniNumber() {
		return sniNumber;
	}

	public void setSniNumber(String sniNumber) {
		this.sniNumber = sniNumber;
	}

	public int getAdmin() {
		return admin;
	}

	public void setAdmin(int admin) {
		this.admin = admin;
	}
	
    public boolean hasRole(int admin) {
    	System.out.println("User: hasRole\n");
		

        if (this.getAdmin() == admin) {
        	System.out.println("Model: Compare \n" + this.getEmail() + " " + 
        this.getAdmin() + " Admin " + 	admin);
                return true;
        }
         
        return false;
    }
	
}

When DTO transfers the data from client, it will be invoking a constructor to save in the database. So it is essential to update the constructor with role flag(admin).

The below code was added as part of security to navigate to different URL based on Role. So, every user is tied up with some flag. 


I also created a new custom UserSurveyService which will also have the hasRole() method.  

package shangrila.council.app.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

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

import shangrila.council.app.model.ResidentUser;


public class UserSurveyService implements UserDetails{
	private static final long serialVersionUID=1L	;
    private ResidentUser user;
    
    public UserSurveyService(ResidentUser user) {
    	System.out.println("UserSurveyService: Constructor\n");
        this.user = user;
    }

	
	public boolean hasRole(int roleName) {

		System.out.println("UserSurveyService: hasRole\n");
		return user.hasRole(roleName);
    }

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
	    List<SimpleGrantedAuthority> authorities = new ArrayList<>();
	    System.out.println("UserSurveyService: getAuthorities\n");
	    /* All users will have only one Role (either Admin or USER) */
	    if (this.hasRole(0)) {
	    	 authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        } else {
            authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        }
        
	    return authorities;
	}


	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return user.getPassword();
	}


	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return user.getFullName();
	}


	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}


	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}


	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}


	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return true;
	}

}


2. Code Authentication Success Handler (Security)

Now, I implemented a class "LoginSuccessHandler" which extends SavedRequestAwareAuthenticationSuccessHandler.

package shangrila.council.app.config;

import java.io.IOException;
import java.util.Collection;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;


 
@Component
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
 
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws ServletException, IOException {
 
    	System.out.println("LoginSuccessHandler :onAuthenticationSuccess ");
    	UserSurveyService userDetails = (UserSurveyService) authentication.getPrincipal();
    	System.out.println("username" + userDetails.getUsername());
        
    	String redirectURL = request.getContextPath();
         
      	/*
 	     The below code was used to verify the authorisation of the logged in user. 
 	      Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
 	      authorities.forEach(auth-> System.out.println(auth.getAuthority())); 
 	 
 	      super.onAuthenticationSuccess(request, response, authentication);
 	 
 	    */	
        if (userDetails.hasRole(1)) {
        	System.out.println("username" + userDetails.getUsername() );
            redirectURL = "admin";
        } else if (userDetails.hasRole(0)) {
            redirectURL = "user";
        }
         
        response.sendRedirect(redirectURL);
         
    }
 
}

onAuthenticationSuccess method will be invoked upon user's successful login. Hence, technically it is correct to add the redirection code here. 

Note: There is no change is the repository class.

3. Custom Class Invocation (Service)

Previously I had invoked the following from loadUserByUsername(), but now I had to invoke my custom class constructor UserSurveyService.

return new org.springframework.security.core.userdetails.User(residentUser.getEmail(),
				residentUser.getPassword(), getAuthorities());


package shangrila.council.app.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

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.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import shangrila.council.app.config.UserSurveyService;
import shangrila.council.app.model.ResidentUser;
import shangrila.council.app.repository.ResidentUserRepository;
import shangrila.council.app.web.dto.ResidentUserRegDTO;

@Service
public class ResidentUserServiceImpl implements ResidentUserService{

	private ResidentUserRepository residentUserRepository;	
	
	/* SHA-1 algorithm will be used internally to encode the password. */
	@Autowired
	private BCryptPasswordEncoder passwordEncoder;
	
	public ResidentUserServiceImpl(ResidentUserRepository residentUserRepository) {
		super();
		System.out.println("Service: ResidentUserServiceImpl\n");
		this.residentUserRepository = residentUserRepository;
	}

	@Override
	public ResidentUser save(ResidentUserRegDTO registrationDTO) {
		System.out.println("Service: save\n");
		ResidentUser residentUser = new ResidentUser(registrationDTO.getFullName(),
				registrationDTO.getDateOfBirth(), registrationDTO.getHomeAddress(),
				registrationDTO.getEmail(), passwordEncoder.encode(registrationDTO.getPassword()), 
				registrationDTO.getSniNumber(),registrationDTO.getAdmin());

		return residentUserRepository.save(residentUser);
	}

	/* Backend Implementation for Login */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		
		System.out.println(username);
		ResidentUser residentUser = residentUserRepository.findByEmail(username);
	
		if (residentUser == null) {
			throw new UsernameNotFoundException("Invalid Username or Password");
		}
		
		System.out.println("Service 1: loadUserByUsername\n" + residentUser.getAdmin() + " " +
				residentUser.getEmail());
		/* For the interface UserDetails, return the User object */
		//return new org.springframework.security.core.userdetails.User(residentUser.getEmail(),
			//	residentUser.getPassword(), getAuthorities());
		return new UserSurveyService(residentUser);
	} 
	
	/* Adding/Setting the authorities for the Resident Users*/
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {

	    List<SimpleGrantedAuthority> authorities = new ArrayList<>();
	    ResidentUser residentUser = residentUserRepository.findByEmail("admin@shangrila.gov.uk");
	    System.out.println("Service: getAuthorities\n");
	    
	    /* All users will have only one Role (either Admin or USER) */
	    if (residentUser == null) {
	    	 authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        } else {
            authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        }
        
	    return authorities;
	}
	

}

There is no change in the interface ResidentUserService.


4. Controller Changes for creating two URLs (Controller/ModelView)

The two URLs user and admin page will be rendered after these changes.

package shangrila.council.app.web;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;


@Controller
public class HomeController {
	
	@GetMapping(value = {"/login"})
	public String login() {
		return "login";
	}
	
	
	@GetMapping(value = {"/","/home"}) 
	public String shangriLaHome() {
		//model.addAttribute("title", "E-Survey Shangri-La City Council"); 
		return "index"; 
	}
	
	@GetMapping(value = {"/user"}) 
	public String userDashboard(Model model) {
		//model.addAttribute("title", "E-Survey Shangri-La City Council"); 
		//List <ResidentUser> listUsers = residentUserRepository.findAll();
	    //model.addAttribute("listUsers", listUsers);
		return "user"; 
	}
	
	@GetMapping(value = {"/admin"}) 
	public String adminDashboard() {
		//model.addAttribute("title", "E-Survey Shangri-La City Council"); 
		return "admin"; 
	}
	
}

My registration controller remained same. Anyway here is the code. 

package shangrila.council.app.web;

import java.util.List;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import shangrila.council.app.service.ResidentUserService;
import shangrila.council.app.web.dto	.ResidentUserRegDTO;

@Controller
//@RequestMapping(value = { "/registration" })
public class ResidentUserRegController {
	private ResidentUserService residentUserService;

	public ResidentUserRegController(ResidentUserService residentUserService) {
		super();
		System.out.println("Controller: ResidentUserRegController\n");
		this.residentUserService = residentUserService;
	}

	@ModelAttribute("residentUser")
	public ResidentUserRegDTO residentUserRegDTO() {
		System.out.println("Controller: residentUserRegDTO\n");
		return new ResidentUserRegDTO();
	}
	
	@GetMapping ("/registration")
	public String displayRegistrationForm(Model model) {
	    model.addAttribute("residentUser", new ResidentUserRegDTO()); 
	    /* Display the "ResidentUserRegistration.html" page */
	    return "ResidentUserRegistration"; 
	}
	 
 
	//@PostMapping("/process")  
	@RequestMapping(value="/process", method=RequestMethod.POST)
	public String registerResidentUserAcc(@Valid @ModelAttribute("residentUser") 
                                    ResidentUserRegDTO residentUserRegDTO,
                                    BindingResult result, Model model) {
		System.out.println("Controller: registerResidentUserAcc\n");
		if(residentUserRegDTO.getEmail().equals("admin@shangrila.gov.uk")) {
			residentUserRegDTO.setAdmin(1);
			System.out.println("pricss: ResidentUser admin 1\n" + residentUserRegDTO.getEmail() 
			                    +  " " +residentUserRegDTO.getAdmin());
		} else {
			residentUserRegDTO.setAdmin(0);
			System.out.println("Process: ResidentUser\n" + residentUserRegDTO.getEmail() 
			                   + " " + residentUserRegDTO.getAdmin());	
		}
		try 
		{
         
         if (result.hasErrors()) {
             List<FieldError> err=result.getFieldErrors();

             for(FieldError e:err){
                  System.out.println("Error on object ---> "+ 
                   e.getObjectName()+ " on field ---> "
                  + e.getField()+". Message ---> "+ e.getDefaultMessage());
             }
             return "ResidentUserRegistration";
         }
         
         /* This will save the Client(URL) entered details 
          * to the server (MySQL) by transfering via DTO Object */
         residentUserService.save(residentUserRegDTO);
         System.out.println("Success");
         
         /*
          * Returning a view which will be executed
          * by the thymeleaf attribute param.acccreationsuccess
          */
         return "redirect:/registration?acccreationsuccess";
		} 
		
		catch (Exception e) {
            List<FieldError> err=result.getFieldErrors();

            for(FieldError er:err){
                 System.out.println("Error on object ---> "+ 
                                    er.getObjectName()+" on field ---> " + er.getField()
                                    + ". Message ---> "+ er.getDefaultMessage());
            }
			
			return "ResidentUserRegistration";
		}
    }

	
}


5. Configure Spring Security to use Success Handler (Security)

Line no 77 was removed which was used to navigate all users to /user page. Line 76 was added where the loginSuccessHandler will decide the navigation page based on roles.



package shangrila.council.app.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import shangrila.council.app.service.ResidentUserService;

/*
 * Enables Spring Security Web security support and 
 * provides MVC Integration.
 */
@Configuration
@EnableWebSecurity
public class ResidentUserConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
	private ResidentUserService residentUserService;
	
	/* New code for different navigation */
//	@Bean
//	public UserSurveyService userService() {
//		System.out.println("UserSurveyService invoked as Null ");
//		return new UserSurveyService(null);
//	}
//	
	
	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		System.out.println("Config: passwordEncoder \n");
		return new BCryptPasswordEncoder();
	}

	@Bean
	public DaoAuthenticationProvider authenticationProvider() {
		System.out.println("Config: DaoAuthenticationProvider \n");
		DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
		auth.setUserDetailsService(residentUserService);
		auth.setPasswordEncoder(passwordEncoder());
		return auth;		
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		System.out.println("Config: AuthenticationManagerBuilder \n");
		auth.authenticationProvider(authenticationProvider());
	}


    @Override
    protected void configure(HttpSecurity security) throws Exception {
       System.out.println("onfig: HttpSecurity \n");
       /* Default Configurations Disabled. */
       security.httpBasic().disable();
       
       
       /* Provide Access to the different URLs, 
        * by starting more restricted to less restricted.
        */
       
       security.csrf().disable()
           .authorizeRequests()
           .antMatchers("/registration").permitAll()
           .antMatchers("/process").permitAll()
           .antMatchers("/").permitAll()
           .antMatchers("/user").hasAuthority("ROLE_USER")
           .antMatchers("/admin", "/questions/create", "/questions/listallquestions")
           .hasAuthority("ROLE_ADMIN")
          .and().formLogin()
               .loginPage("/login")
               .usernameParameter("email")
               .successHandler(loginSuccessHandler)
     //        .defaultSuccessUrl("/user")
               .permitAll()
          .and().logout()
               .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
               .logoutSuccessUrl("/login?logout")
               .permitAll();
       
    }
    
    @Autowired private LoginSuccessHandler loginSuccessHandler;
}

The last few code restricts the user to access only user pages and admin to access only admin pages.

HTML pages

My login page remained the same.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

         <!-- Bootstrap CSS -->
         <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" 
               integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" 
               crossorigin="anonymous">     
		<title> Login E-Survey ShangriLa City Council</title>
    </head>
    <body>
        <!-- Create a Navigation Bar -->
        <nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark">
            <!-- Navbar content -->
            <button class="navbar-toggler" type="button" data-toggle="collapse" 
                data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup"
                aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarText">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                       <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/registration">Registration</a>
                    </li>
                </ul>
                <span class="navbar-text">
                   E-Survey for Energy Consumption
                </span>
        </nav>
        
        <div class = "container">
            <div class = "row">
                <!-- To align the page in the center, we use col-md-offset 
                     col-md-6 is the bootstrap CSS grade system -->
                <div class="col-md-6 col-md-offset-3">
                    <h1> User Login </h1>
                        <!-- Success Message -->
<!--                        <div th:if="${param.loginsuccess}">
                            <div class="alert alert-info">Logged into E-Survey Shangri La account. 
                            <a href="/" th:href="@{/logout}">Logout</a>
                            </div>
                        </div> -->
                                               
                        <!-- Logout Message -->
                        <div th:if="${param.logout}">
                            <div class="alert alert-info"> You have been logged out. </div>
                        </div> 
                         
                        
                    <form th:action="@{/login}" th:object="${residentUser}"  method="post"> 

                        <!-- Error Message -->
                            <div th:if="${param.error}">
                            <div class="alert alert-danger"> Invalid Username or Password. </div>
                        </div>
  
                        
                        <!-- Email Address -->
                        <div class="form-group">
                            <label for ="email"> Email </label> :
                            <input type="email" class="form-control"
                                   id="email" name="email" placeholder="Email Address" 
                                   autofocus="autofocus">
                        </div>
                        
                        <!-- Password -->
                        <div class="form-group">
                            <label for ="password"> Password </label> :
                            <input type="password" class="form-control"
                                   id="password" name="password" placeholder="Password" 
                                   autofocus="autofocus">
                        </div>
                        
                        <!-- Submit Button -->
                        <div class="form-group">
                            <div class="row">
                                <div class="col-sm-6 col-sm-offset-3">
                                    <input type="submit" name="login-submit" id="login-submit"
                                    class="form-control btn btn-primary" value="Log in"/>
                                   
                                </div>
                            </div>
                        </div>
                </div>
            </div>
        </div>
        
        
        
    </body>
</html>

admin.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

         <!-- Bootstrap CSS -->
         <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" 
               integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" 
               crossorigin="anonymous">     
		<title> Admin Dashboard - E-Survey ShangriLa City Council</title>
    </head>
    <body>
        <!-- Create a Navigation Bar -->
        <nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark">
            <!-- Navbar content -->
            <button class="navbar-toggler" type="button" data-toggle="collapse" 
                data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup"
                aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarText">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                       <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
                    </li>
                    <!-- This thymeleaf atribute sec:authorize
                          will be handled in secutity handler 
                         -->
                    <li class="nav-item" sec:authorize="isAuthenticated()">
                        <a class="nav-link" href="/logout">Logout</a>
                    </li>
                    <li class="nav-item" sec:authorize="isAuthenticated()">
                        <a class="nav-link" href="/questions/create">Add Questions</a>
                    </li>
                    <li class="nav-item" sec:authorize="isAuthenticated()">
                        <a class="nav-link" href="/questions/listallquestions">View/Edit Questions</a>
                    </li>
                </ul>
                <span class="navbar-text">
                   E-Survey for Energy Consumption
                </span>
        </nav>
        <br> <br> <br>
        <form th:action="@{/logout}" method=post>   
             <input type="submit" value="Signout"/>
        </form >
        
    </body>
</html>

user.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

         <!-- Bootstrap CSS -->
         <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" 
               integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" 
               crossorigin="anonymous">     
		<title> User DashBoard E-Survey ShangriLa City Council</title>
    </head>
    <body>
        <!-- Create a Navigation Bar -->
        <nav class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark">
            <!-- Navbar content -->
            <button class="navbar-toggler" type="button" data-toggle="collapse" 
                data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup"
                aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarText">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                       <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
                    </li>
                    <!-- This thymeleaf atribute sec:authorize
                          will be handled in secutity handler 
                         -->
                    <li class="nav-item" sec:authorize="isAuthenticated()">
                        <a class="nav-link" href="/logout">Logout</a>
                    </li>
                </ul>
                <span class="navbar-text">
                   E-Survey for Energy Consumption
                </span>
        </nav>
        <br> <br> <br>
         <!--<div class="container">
              Welcome <span sec:authentication="principal.email"> ResidentUser</span >
              </div> -->
        <form th:action="@{/logout}" method=post>   
             <input type="submit" value="Signout"/>
        </form >
        
    </body>
</html>


Project Structure




Admin logs in 



Admin DashBoard


User login 


User Dashboard





Console logs 

2022-01-04 10:18:35.957  INFO 1173 --- [  restartedMain] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@61881d1, org.springframework.security.web.context.SecurityContextPersistenceFilter@36a7c52f, org.springframework.security.web.header.HeaderWriterFilter@15bf64bc, org.springframework.security.web.authentication.logout.LogoutFilter@6aff619d, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@d93b7b2, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7176515b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@6140c6b7, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2fcd0c39, org.springframework.security.web.session.SessionManagementFilter@60a70740, org.springframework.security.web.access.ExceptionTranslationFilter@18f15737, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@6b0a3acd]
2022-01-04 10:18:36.152  INFO 1173 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2022-01-04 10:18:36.330  INFO 1173 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2022-01-04 10:18:36.371  INFO 1173 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-01-04 10:18:36.382  INFO 1173 --- [  restartedMain] s.c.app.ShangriLaESuveyToolApplication   : Started ShangriLaESuveyToolApplication in 4.677 seconds (JVM running for 5.62)
2022-01-04 10:18:47.915  INFO 1173 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-01-04 10:18:47.915  INFO 1173 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2022-01-04 10:18:47.917  INFO 1173 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
Controller: residentUserRegDTO

admin@shangrila.gov.uk
2022-01-04 10:20:10.805 DEBUG 1173 --- [nio-8080-exec-8] org.hibernate.SQL                        : select residentus0_.userid as userid1_2_, residentus0_.admin as admin2_2_, residentus0_.dob as dob3_2_, residentus0_.email as email4_2_, residentus0_.full_name as full_nam5_2_, residentus0_.address as address6_2_, residentus0_.password_hash as password7_2_, residentus0_.sni as sni8_2_ from resident_user residentus0_ where residentus0_.email=?
2022-01-04 10:20:10.811 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [admin@shangrila.gov.uk]
2022-01-04 10:20:10.816 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([userid1_2_] : [INTEGER]) - [1]
ResidentUser Default constructor
2022-01-04 10:20:10.821 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([admin2_2_] : [INTEGER]) - [1]
2022-01-04 10:20:10.821 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([dob3_2_] : [VARCHAR]) - [01/01/2022]
2022-01-04 10:20:10.821 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([email4_2_] : [VARCHAR]) - [admin@shangrila.gov.uk]
2022-01-04 10:20:10.821 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([full_nam5_2_] : [VARCHAR]) - [admin]
2022-01-04 10:20:10.821 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([address6_2_] : [VARCHAR]) - [C9, 12 Brookland Roa]
2022-01-04 10:20:10.821 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([password7_2_] : [VARCHAR]) - [$2a$10$Kh.WWheBogXuq//8lVOtG.wTHoHDEVEjVa2eXxLjocMwkn8G5KygK]
2022-01-04 10:20:10.821 TRACE 1173 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([sni8_2_] : [VARCHAR]) - [1234]
Service 1: loadUserByUsername
1 admin@shangrila.gov.uk
UserSurveyService: Constructor

UserSurveyService: getAuthorities

UserSurveyService: hasRole

User: hasRole

LoginSuccessHandler :onAuthenticationSuccess 
usernameadmin
UserSurveyService: hasRole

User: hasRole

Model: Compare 
admin@shangrila.gov.uk 1 Admin 1
usernameadmin

For User,

lorena@gmail.com
2022-01-04 10:21:17.708 DEBUG 1173 --- [nio-8080-exec-5] org.hibernate.SQL                        : select residentus0_.userid as userid1_2_, residentus0_.admin as admin2_2_, residentus0_.dob as dob3_2_, residentus0_.email as email4_2_, residentus0_.full_name as full_nam5_2_, residentus0_.address as address6_2_, residentus0_.password_hash as password7_2_, residentus0_.sni as sni8_2_ from resident_user residentus0_ where residentus0_.email=?
2022-01-04 10:21:17.718 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [lorena@gmail.com]
2022-01-04 10:21:17.725 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([userid1_2_] : [INTEGER]) - [2]
ResidentUser Default constructor
2022-01-04 10:21:17.726 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([admin2_2_] : [INTEGER]) - [0]
2022-01-04 10:21:17.727 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([dob3_2_] : [VARCHAR]) - [02/03/2020]
2022-01-04 10:21:17.727 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([email4_2_] : [VARCHAR]) - [lorena@gmail.com]
2022-01-04 10:21:17.727 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([full_nam5_2_] : [VARCHAR]) - [Lorena]
2022-01-04 10:21:17.727 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([address6_2_] : [VARCHAR]) - [C9, 12 Brookland Roa]
2022-01-04 10:21:17.727 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([password7_2_] : [VARCHAR]) - [$2a$10$vPqE0/DZrPK8DBKM98ri9eydB.uMz/PfSMpPGn978R6V4uPexHQOC]
2022-01-04 10:21:17.728 TRACE 1173 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([sni8_2_] : [VARCHAR]) - [1289]
Service 1: loadUserByUsername
0 lorena@gmail.com
UserSurveyService: Constructor

UserSurveyService: getAuthorities

UserSurveyService: hasRole

User: hasRole

Model: Compare 
lorena@gmail.com 0 Admin 0
LoginSuccessHandler :onAuthenticationSuccess 
usernameLorena
UserSurveyService: hasRole

User: hasRole

UserSurveyService: hasRole

User: hasRole

Model: Compare 
lorena@gmail.com 0 Admin 0

Reference:

https://www.youtube.com/watch?v=sYGTdvq-CP0

https://www.codejava.net/frameworks/spring-boot/redirect-users-after-login-based-on-roles



Comments