Spring Boot

Spring Security using Spring Boot Example

Hello Friends!!! In this tutorial we will discuss the Spring Security with Spring Boot and also will see an example based on Spring security with Spring Boot.

 1. Spring security Overview

Spring security is the highly customizable authentication and access-control framework. This is the security module for securing spring applications. But, this can also be used for non-spring based application with few extra configurations to enable the security features. Spring Security is a framework that focuses on providing both authentication and authorization to Java applications.
  • Authentication” is the process of establishing a principal is who they claim to be (a “principal” generally means a user, device or some other system which can perform an action in your application).
  • Authorization” refers to the process of deciding whether a principal is allowed to perform an action within your application. To arrive at the point where an authorization decision is needed, the identity of the principal has already been established by the authentication process. These concepts are common, and not at all specific to Spring Security.

 2. Spring Security Modules

In Spring Security 3.0, the codebase has been sub-divided into separate jars which more clearly separate different functionality areas and third-party dependencies. The following are the list of modules currently shipped by spring security framework.
  • Core (spring-security-core.jar) – This module contains the APIs for basic authentication and access-control related mechanism. This is mandatory for ant spring security applications.
  • Remoting (spring-security-remoting.jar) – This module provides integration to the Spring Remoting. You don’t need to include this module unless you are writing remote client applications.
  • Web (spring-security-web.jar) – This module contains APIs for servlet filters and any web based authentication like access restriction for URLs. Any web application would require this module.
  • Config (spring-security-config.jar) – Contains the security namespace parsing code & Java configuration code. You need it if you are using the Spring Security XML namespace for configuration. If you are not using XML configurations, you can ignore this module.
  • LDAP (spring-security-ldap.jar)– Required if you need to use LDAP authentication or manage LDAP user entries.
  • ACL (spring-security-acl.jar) – Specialized domain object ACL implementation. Used to apply security to specific domain object instances within your application.
  • CAS (spring-security-cas.jar) – Spring Security’s CAS client integration. If you want to use Spring Security web authentication with a CAS single sign-on server.
  • OpenID (pring-security-openid.jar) – OpenID web authentication support. Used to authenticate users against an external OpenID server.
  • Test (spring-security-test.jar)– Support for testing with Spring Security.

3. Getting Spring Security

You can get hold of Spring Security in several ways.
3.1. Usage with Maven
A minimal Spring Security Maven set of dependencies typically looks like the following:
Pom.xml
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-web</artifactId>
 <version>4.1.2.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-config</artifactId>
 <version>4.1.2.RELEASE</version>
</dependency>
</dependencies>
3.2. Usage with Gradle
A minimal Spring Security Gradle set of dependencies typically looks like the following:
build.gradle
dependencies {
 compile 'org.springframework.security:spring-security-web:4.1.2.RELEASE'
 compile 'org.springframework.security:spring-security-config:4.1.2.RELEASE'
}

4. Core Components

Core Components represent the building blocks of spring security and what are the core components that are actually used while user is authenticating to your application. So if you ever need to go beyond a simple namespace configuration then it’s important that you understand what they are, even if you don’t actually need to interact with them directly.
4.1 SecurityContext
As the name implies, this interface is the corner stone of storing all the security related details for your application. When you enable spring security for your application, a SecurityContext will enable for each application and stores the details of authenticated user, etc. It uses Authentication object for storing the details related to authentications.
4.2 SecurityContextHolder
The most fundamental object is SecurityContextHolder. This class is important for accessing any value from the SecurityContext. This is where we store details of the present security context of the application, which includes details of the principal currently using the application. By default the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution, even if the security context is not explicitly passed around as an argument to those methods.
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
4.3 Authentication
Let’s consider a standard authentication scenario that everyone is familiar with.
  1. A user is prompted to log in with a username and password.
  2. The system verifies that the password is correct for the username.
  3. The context information for that user is obtained their list of roles and so on.
  4. A security context is established for the user
  5. The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information.
The following are the steps to achieve the authentication:
  1. Authentication is an interface which has several implementations for different authentication models. For a simple user name and password authentication, spring security would use UsernamePasswordAuthenticationToken. When user enters username and password, system creates a new instance of UsernamePasswordAuthenticationToken.
  2. The token is passed to an instance of AuthenticationManager for validation. Internally what AuthenticationManager will do is to iterate the list of configured AuthenticationProvider to validate the request. There should be at least one provider to be configured for the valid authentication.
  3. The AuthenticationManager returns a fully populated Authentication instance on successful authentication.
  4. The final step is to establish a security context by invoking SecurityContextHolder.getContext().setAuthentication(), passing in the returned authentication object.
4.4 UserDetailsService
UserDetails is a core interface in Spring Security. It represents a principal, but in an extensible and application-specific way. Think of UserDetails as the adapter between your own user database and what Spring Security needs inside the SecurityContextHolder. UserDetailsService is a core interface in spring security to load user specific data. This interface is considered as user DAO and will be implemented by specific DAO implementations. For example, for a basic in memory authentication, there is an InMemoryUserDetailsManager. This interface declares only one method loadUserByUsername (String username) which simplifies the implementation classes to write other specific methods.
Suppose you want to use your existing DAO classes to load the user details from the database, just implement the UserDetailsService and override the method loadUserByUsername(String username). An example of this implementation would look like this:
@Service
public class CurrentUserDetailsService implements UserDetailsService {
    private final UserService userService;
    @Autowired
    public CurrentUserDetailsService(UserService userService) {
        this.userService = userService;
    }
 
    public CurrentUser loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.getUserByUsername(username);
        return new CurrentUser(user);
    }
}
In the above code, the model CurrentUser must of of type org.springframework.security.userdetails.UserDetails. In the below example application, I have extended the org.springframework.security.userdetails.UserDetails class and written the custom user class. This will have the advantage of not exposing our domain class.
4.5 GrantedAuthority
Apart from authenticating to the application, another important component is to get the list of granted authorities for the logged in user. This comes as part of the authorization process. This is retrieved by calling the getAuthorities() in Authentication object. This returns the list of GrantedAuthority which denotes roles for the users. Such authorities are usually “roles”, such as ROLE_ADMINISTRATOR orROLE_HR_SUPERVISOR. These roles are later on configured for web authorization, method authorization and domain object authorization.
Summary
The major building blocks of Spring Security that we’ve seen so far are:
  • SecurityContextHolder, to provide access to the SecurityContext.
  • SecurityContext, to hold the Authentication and possibly request-specific security information.
  • Authentication, to represent the principal in a Spring Security-specific manner.
  • GrantedAuthority, to reflect the application-wide permissions granted to a principal.
  • UserDetails, to provide the necessary information to build an Authentication object from your application’s DAOs or other source of security data.
  • UserDetailsService, to create a UserDetails when passed in a String-based username (or certificate ID or the like).
5. Spring Security Example Application
This section walks you through the process of creating a simple web application in Spring Boot with resources that are protected by Spring Security.
What you’ll build
You’ll build a Spring MVC application that secures the page with a login form backed by a fixed list of users. Let’s start with the use cases for every common web application with some basic access restrictions. So, the requirements of such an app could be:
  • The app will have users, each with role Admin or User
  • They log in by their emails and passwords
  • Non-admin users can view their info, but cannot peek at other users
  • Admin users can list and view all the users, and create new ones as well
  • Customized form for login
  • Remember me” authentication for laziest
  • Possibility to logout
  • Home page will be available for everyone, authenticated or not
Let’s create Web Project with Spring Boot Web Initialzr with Spring Web, Spring Data JPA, Spring Security and H2 Database. So following project structure we have after creating from web interface and adding necessary files for this example.
Let’s discuss files of this example.
Dependencies pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.dineshonjava.sbsecurity</groupId>
 <artifactId>SpringBootSecurity</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>SpringBootSecurity</name>
 <description>SpringBootSecurity project for Spring Boot and Spring Security</description>

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.4.0.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
  </plugins>
 </build>


</project>

WebSecurityConfigurerAdapter
This is the Java configuration class for writing the web based security configurations. You can override the methods in this class to configure the following things:

  • Enforce the user to be authenticated prior to accessing any URL in your application
  • Create a user with the username user , password, and role of ROLE_USER
  • Enables HTTP Basic and Form based authentication
  • Spring Security will automatically render a login page and logout success page for you

SecurityConfig.java

/**
 * 
 */
package com.dineshonjava.sbsecurity.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @author Dinesh.Rajput
 *
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
 
 @Autowired
    UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
            .antMatchers("/", "/home", "/public/**").permitAll()
            .antMatchers("/users/**").hasAuthority("ADMIN")
            .anyRequest().fullyAuthenticated()
            .and()
        .formLogin()
            .loginPage("/login")
            .failureUrl("/login?error")
            .usernameParameter("email")
            .permitAll()
            .and()
        .logout()
         .logoutUrl("/logout")
            .logoutSuccessUrl("/")
            .permitAll();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }
}

Domain Classes
User.java

/**
 * 
 */
package com.dineshonjava.sbsecurity.model;

import java.io.Serializable;

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

import com.dineshonjava.sbsecurity.role.Role;

/**
 * @author Dinesh.Rajput
 *
 */
@Entity
@Table(name = "user")
public class User implements Serializable{
 
 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "userid", nullable = false, updatable = false)
 Long userid;
 @Column(name = "username", nullable = false)
 String username;
 @Column(name = "email", nullable = false, unique = true)
 String email;
 @Column(name = "password", nullable = false)
 String password;
 @Column(name = "role", nullable = false)
 @Enumerated(EnumType.STRING)
 Role role;
 public Long getUserid() {
  return userid;
 }
 public void setUserid(Long userid) {
  this.userid = userid;
 }
 public String getUsername() {
  return username;
 }
 public void setUsername(String username) {
  this.username = username;
 }
 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 Role getRole() {
  return role;
 }
 public void setRole(Role role) {
  this.role = role;
 }
 @Override
 public String toString() {
  return "User [userid=" + userid + ", username=" + username + ", email="
    + email + ", password=" + password + ", role=" + role + "]";
 }
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((email == null) ? 0 : email.hashCode());
  result = prime * result
    + ((password == null) ? 0 : password.hashCode());
  result = prime * result + ((role == null) ? 0 : role.hashCode());
  result = prime * result + ((userid == null) ? 0 : userid.hashCode());
  result = prime * result
    + ((username == null) ? 0 : username.hashCode());
  return result;
 }
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  User other = (User) obj;
  if (email == null) {
   if (other.email != null)
    return false;
  } else if (!email.equals(other.email))
   return false;
  if (password == null) {
   if (other.password != null)
    return false;
  } else if (!password.equals(other.password))
   return false;
  if (role != other.role)
   return false;
  if (userid == null) {
   if (other.userid != null)
    return false;
  } else if (!userid.equals(other.userid))
   return false;
  if (username == null) {
   if (other.username != null)
    return false;
  } else if (!username.equals(other.username))
   return false;
  return true;
 }
 
 
}

Role.java

/**
 * 
 */
package com.dineshonjava.sbsecurity.role;

/**
 * @author Dinesh.Rajput
 *
 */
public enum Role {
 USER, ADMIN
}

Besides that, a form for creating a new user will be nice to have:
UserBean.java

/**
 * 
 */
package com.dineshonjava.sbsecurity.bean;

import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotEmpty;

import com.dineshonjava.sbsecurity.role.Role;

/**
 * @author Dinesh.Rajput
 *
 */
public class UserBean {
 @NotEmpty
    private String username = "";
 
 @NotEmpty
    private String email = "";

    @NotEmpty
    private String password = "";

    @NotEmpty
    private String passwordRepeated = "";

    @NotNull
    private Role role = Role.USER;

 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 getPasswordRepeated() {
  return passwordRepeated;
 }

 public void setPasswordRepeated(String passwordRepeated) {
  this.passwordRepeated = passwordRepeated;
 }

 public Role getRole() {
  return role;
 }

 public void setRole(Role role) {
  this.role = role;
 }

 public String getUsername() {
  return username;
 }

 public void setUsername(String username) {
  this.username = username;
 }

}

This will function as a data transfer object (DTO) between the web layer and service layer. It’s annotated by Hibernate Validator validation constraints and sets some sane defaults.

CurrentUser.java

/**
 * 
 */
package com.dineshonjava.sbsecurity.bean;

import org.springframework.security.core.authority.AuthorityUtils;

import com.dineshonjava.sbsecurity.model.User;
import com.dineshonjava.sbsecurity.role.Role;

/**
 * @author Dinesh.Rajput
 *
 */
public class CurrentUser extends org.springframework.security.core.userdetails.User {

 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 
 private User user;
 
 public CurrentUser(User user) {
        super(user.getEmail(), user.getPassword(), AuthorityUtils.createAuthorityList(user.getRole().toString()));
        this.user = user;
    }

    public User getUser() {
        return user;
    }
    
    public Long getId() {
        return user.getUserid();
    }

    public Role getRole() {
        return user.getRole();
    }
    
    @Override
    public String toString() {
        return "CurrentUser{" +
                "user=" + user +
                "} " + super.toString();
}
}


Service Layer

In service layer, where the business logic should, we’d need something to retrieve the User by his id, email, list all the users and create a new one.
UserService .java

/**
 * 
 */
package com.dineshonjava.sbsecurity.service;

import java.util.Collection;

import com.dineshonjava.sbsecurity.bean.UserBean;
import com.dineshonjava.sbsecurity.model.User;

/**
 * @author Dinesh.Rajput
 *
 */
public interface UserService {
 
    User getUserById(long id);

    User getUserByEmail(String email);

    Collection<User> getAllUsers();

    User create(UserBean userBean);
}

UserServiceImpl .java
The implementation of the service:

/**
 * 
 */
package com.dineshonjava.sbsecurity.service;

import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.dineshonjava.sbsecurity.bean.UserBean;
import com.dineshonjava.sbsecurity.model.User;
import com.dineshonjava.sbsecurity.model.UserRepository;

/**
 * @author Dinesh.Rajput
 *
 */
@Service
public class UserServiceImpl implements UserService {
 
 private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
 
 @Autowired
 UserRepository userRepository;
 
 @Override
 public User getUserById(long id) {
  LOGGER.debug("Getting user={}", id);
  return userRepository.findOne(id);
 }

 @Override
 public User getUserByEmail(String email) {
  LOGGER.debug("Getting user by email={}", email.replaceFirst("@.*", "@***"));
  return userRepository.findOneByEmail(email);
 }

 @Override
 public Collection<User> getAllUsers() {
  LOGGER.debug("Getting all users");
  return (Collection<User>) userRepository.findAll();
 }

 @Override
 public User create(UserBean userBean) {
  User user = new User();
  user.setUsername(userBean.getUsername());
        user.setEmail(userBean.getEmail());
        user.setPassword(new BCryptPasswordEncoder().encode(userBean.getPassword()));
        user.setRole(userBean.getRole());
        return userRepository.save(user);
 }

}

Spring Data Repository
This section explains the service classes and spring data repository implementation.
UserRepository .java

/**
 * 
 */
package com.dineshonjava.sbsecurity.model;

import org.springframework.data.repository.CrudRepository;

/**
 * @author Dinesh.Rajput
 *
 */
public interface UserRepository extends CrudRepository<User, Long>{
 
 User findOneByEmail(String email);
}

CurrentUserDetailsService.java

/**
 * 
 */
package com.dineshonjava.sbsecurity.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.dineshonjava.sbsecurity.bean.CurrentUser;
import com.dineshonjava.sbsecurity.model.User;

/**
 * @author Dinesh.Rajput
 *
 */
@Service
public class CurrentUserDetailsService implements UserDetailsService {
 
 private static final Logger LOGGER = LoggerFactory.getLogger(CurrentUserDetailsService.class);
 
 @Autowired
 UserService userService;
 
 @Override
 public UserDetails loadUserByUsername(String email)
   throws UsernameNotFoundException {
  LOGGER.debug("Authenticating user with email={}", email.replaceFirst("@.*", "@***"));
  User user = userService.getUserByEmail(email);
  return new CurrentUser(user);
 }

}

Spring MVC Configurations Web Layer
Home Page

/**
 * 
 */
package com.dineshonjava.sbsecurity.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author Dinesh.Rajput
 *
 */
@Controller
public class HomeController {
 
 private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
 
 @RequestMapping("/")
    public String getHomePage() {
  LOGGER.debug("Getting home page");
        return "home";
    }
}

Login Page

/**
 * 
 */
package com.dineshonjava.sbsecurity.controller;

import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
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.servlet.ModelAndView;

/**
 * @author Dinesh.Rajput
 *
 */
@Controller
public class LoginController {
 
 private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
 
 @RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView getLoginPage(@RequestParam Optional<String> error) {
     LOGGER.debug("Getting login page, error={}", error);
        return new ModelAndView("login", "error", error);
    }
}

User Page

/**
 * 
 */
package com.dineshonjava.sbsecurity.controller;

import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.dineshonjava.sbsecurity.bean.UserBean;
import com.dineshonjava.sbsecurity.bean.validator.UserBeanValidator;
import com.dineshonjava.sbsecurity.service.UserService;

/**
 * @author Dinesh.Rajput
 *
 */
@Controller
public class UserController {
 
 private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
 
 @Autowired
 UserService userService;
 
 @Autowired
 UserBeanValidator userBeanValidator;
 
 @InitBinder("form")
    public void initBinder(WebDataBinder binder) {
        binder.addValidators(userBeanValidator);
    }
 
 @RequestMapping("/users")
 public ModelAndView getUsersPage() {
  LOGGER.debug("Getting users page");
  return new ModelAndView("users", "users", userService.getAllUsers());
 }
 
 @PreAuthorize("@currentUserServiceImpl.canAccessUser(principal, #id)")
 @RequestMapping("/user/{id}")
    public ModelAndView getUserPage(@PathVariable Long id) {
  LOGGER.debug("Getting user page for user={}", id);
        return new ModelAndView("user", "user", userService.getUserById(id));
    }
 
 @PreAuthorize("hasAuthority('ADMIN')")
    @RequestMapping(value = "/user/create", method = RequestMethod.GET)
    public ModelAndView getUserCreatePage() {
      LOGGER.debug("Getting user create form");
        return new ModelAndView("user_create", "form", new UserBean());
    }
    
 @PreAuthorize("hasAuthority('ADMIN')")
    @RequestMapping(value = "/user/create", method = RequestMethod.POST)
    public String handleUserCreateForm(@Valid @ModelAttribute("form") UserBean form, BindingResult bindingResult) {
      LOGGER.debug("Processing user create form={}, bindingResult={}", form, bindingResult);
        if (bindingResult.hasErrors()) {
            return "user_create";
        }
        try {
            userService.create(form);
        } catch (DataIntegrityViolationException e) {
         LOGGER.warn("Exception occurred when trying to save the user, assuming duplicate email", e);
            bindingResult.reject("email.exists", "Email already exists");
            return "user_create";
        }
        return "redirect:/users";
    }
}

CurrentUserServiceImpl.java

/**
 * 
 */
package com.dineshonjava.sbsecurity.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.dineshonjava.sbsecurity.bean.CurrentUser;
import com.dineshonjava.sbsecurity.role.Role;

/**
 * @author Dinesh.Rajput
 *
 */
@Service
public class CurrentUserServiceImpl implements CurrentUserService {

 private static final Logger LOGGER = LoggerFactory.getLogger(CurrentUserDetailsService.class);

    @Override
    public boolean canAccessUser(CurrentUser currentUser, Long userId) {
        LOGGER.debug("Checking if user={} has access to user={}", currentUser, userId);
        return currentUser != null
                && (currentUser.getRole() == Role.ADMIN || currentUser.getId().equals(userId));
}

}


application.properties

#security.user.name=root
#security.user.password=111
#security.user.role=ADMIN
logging.level.org.springframework=WARN
logging.level.org.hibernate=WARN
logging.level.com.dineshonjava=DEBUG
spring.freemarker.template-loader-path=/WEB-INF/ftl
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-spring-macro-helpers=true

Here we are using freemarker template for views layers.

You can access this whole running project from below link.

https://github.com/DOJ-SoftwareConsultant/SpringBootSecurity

Happy Spring Boot Learning!!!:)

Previous
Next
Dinesh Rajput

Dinesh Rajput is the chief editor of a website Dineshonjava, a technical blog dedicated to the Spring and Java technologies. It has a series of articles related to Java technologies. Dinesh has been a Spring enthusiast since 2008 and is a Pivotal Certified Spring Professional, an author of a book Spring 5 Design Pattern, and a blogger. He has more than 10 years of experience with different aspects of Spring and Java design and development. His core expertise lies in the latest version of Spring Framework, Spring Boot, Spring Security, creating REST APIs, Microservice Architecture, Reactive Pattern, Spring AOP, Design Patterns, Struts, Hibernate, Web Services, Spring Batch, Cassandra, MongoDB, and Web Application Design and Architecture. He is currently working as a technology manager at a leading product and web development company. He worked as a developer and tech lead at the Bennett, Coleman & Co. Ltd and was the first developer in his previous company, Paytm. Dinesh is passionate about the latest Java technologies and loves to write technical blogs related to it. He is a very active member of the Java and Spring community on different forums. When it comes to the Spring Framework and Java, Dinesh tops the list!

Share
Published by
Dinesh Rajput

Recent Posts

Strategy Design Patterns using Lambda

Strategy Design Patterns We can easily create a strategy design pattern using lambda. To implement…

2 years ago

Decorator Pattern using Lambda

Decorator Pattern A decorator pattern allows a user to add new functionality to an existing…

2 years ago

Delegating pattern using lambda

Delegating pattern In software engineering, the delegation pattern is an object-oriented design pattern that allows…

2 years ago

Spring Vs Django- Know The Difference Between The Two

Technology has emerged a lot in the last decade, and now we have artificial intelligence;…

2 years ago

TOP 20 MongoDB INTERVIEW QUESTIONS 2022

Managing a database is becoming increasingly complex now due to the vast amount of data…

2 years ago

Scheduler @Scheduled Annotation Spring Boot

Overview In this article, we will explore Spring Scheduler how we could use it by…

3 years ago