Mastering Spring Security: Roles and Privileges
Spring Security Roadmap

Introduction
In any modern application, security is paramount. Spring Security is a powerful and flexible framework that provides comprehensive security services for Java applications. One of the core aspects of security is managing user access, which is typically done through roles and privileges. This article explores how to implement roles and privileges using Spring Security, covering the basics, advanced configurations, and practical examples to help you secure your applications effectively.
Understanding Roles and Privileges
Roles
Roles are high-level permissions assigned to users. They represent a collection of permissions or privileges that define what actions a user can perform within an application. For instance, in a blogging platform, you might have roles such as ADMIN, EDITOR, and VIEWER.
Privileges
Privileges are more granular and specific than roles. They define fine-grained access controls that are aggregated into roles. For example, privileges might include READ_PRIVILEGE, WRITE_PRIVILEGE, DELETE_PRIVILEGE, etc. These privileges can be grouped into roles to simplify management.
Setting Up Spring Security
To get started with Spring Security, you need to include the necessary dependencies. If you’re using Maven, add the following to your pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>This dependency includes Spring Security and all its required components.
Configuring Roles and Privileges
Defining UserDetails and UserDetailsService
Spring Security uses the UserDetails and UserDetailsService interfaces to handle user authentication and authorization. You need to implement these interfaces to provide user details and roles.
CustomUserDetails Class: The CustomUserDetails class implements the UserDetails interface and provides user-specific data.
package com.medium.security;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class CustomUserDetails implements UserDetails {
private String username;
private String password;
private List<GrantedAuthority> authorities;
public CustomUserDetails(User user) {
this.username = user.getUsername();
this.password = user.getPassword();
this.authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}Explanation:
- username and password: These fields store the user’s credentials.
- authorities: This field stores the user’s roles as a list of
GrantedAuthorityobjects. - The constructor initializes these fields using a
Userobject. - The methods from
UserDetailsinterface provide Spring Security with the necessary information about the user's account status and roles.
CustomUserDetailsService Class: The CustomUserDetailsService class implements the UserDetailsService interface and loads user-specific data.
package com.medium.security;
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;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new CustomUserDetails(user);
}
}Explanation:
- The
CustomUserDetailsServiceclass uses theUserRepositoryto fetch the user details from the database. - The
loadUserByUsernamemethod fetches aUserobject by username and converts it to aCustomUserDetailsobject. If the user is not found, it throws aUsernameNotFoundException.
Configuring Security
Spring Security’s configuration is done through a configuration class annotated with @EnableWebSecurity.
package com.medium.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/editor/**").hasRole("EDITOR")
.antMatchers("/view/**").hasAnyRole("VIEWER", "EDITOR", "ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout()
.permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}Explanation:
- configure(AuthenticationManagerBuilder auth): This method configures authentication by using the
CustomUserDetailsServiceandpasswordEncoder. - configure(HttpSecurity http): This method configures authorization rules. It specifies which roles can access which URL patterns. For example, only users with the
ADMINrole can access URLs under/admin/**. - passwordEncoder(): This bean provides a
PasswordEncoderimplementation usingBCryptPasswordEncoderfor hashing passwords.
Defining Roles and Privileges in the Database
Entity Definitions
Define your User, Role, and Privilege entities. This example uses JPA annotations to map the entities to database tables.
User Entity:
package com.medium.model;
import javax.persistence.*;
import java.util.Collection;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Collection<Role> roles;
// Getters and setters
}Role Entity:
package com.medium.model;
import javax.persistence.*;
import java.util.Collection;
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private Collection<User> users;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "roles_privileges",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "privilege_id")
)
private Collection<Privilege> privileges;
// Getters and setters
}Privilege Entity:
package com.medium.model;
import javax.persistence.*;
import java.util.Collection;
@Entity
public class Privilege {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "privileges")
private Collection<Role> roles;
// Getters and setters
}Explanation:
- User: Represents the user entity with fields for
id,username,password, and a collection of roles. The@ManyToManyannotation sets up a many-to-many relationship with theRoleentity. - Role: Represents the role entity with fields for
id,name, a collection of users, and a collection of privileges. The@ManyToManyannotation sets up relationships with bothUserandPrivilegeentities. - Privilege: Represents the privilege entity with fields for
idandname, and a collection of roles. The@ManyToManyannotation sets up a relationship with theRoleentity.
Repositories
Create repositories for your entities to interact with the database.
UserRepository:
package com.medium.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.medium.model.User;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}RoleRepository:
package com.medium.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.medium.model.Role;
public interface RoleRepository extends JpaRepository<Role, Long> {
}PrivilegeRepository:
package com.medium.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.medium.model.Privilege;
public interface PrivilegeRepository extends JpaRepository<Privilege, Long> {
}Explanation:
- UserRepository: Extends
JpaRepositoryto provide CRUD operations for theUserentity. ThefindByUsernamemethod retrieves a user by username. - RoleRepository: Extends
JpaRepositoryto provide CRUD operations for theRoleentity. - PrivilegeRepository: Extends
JpaRepositoryto provide CRUD operations for thePrivilegeentity.
Managing Roles and Privileges
With your entities and repositories in place, you can now manage roles and privileges in your application.
Creating Users with Roles and Privileges
You can create users with specific roles and privileges by interacting with the repositories.
InitDbService Class:
package com.medium.service;
import com.medium.model.Privilege;
import com.medium.model.Role;
import com.medium.model.User;
import com.medium.repository.PrivilegeRepository;
import com.medium.repository.RoleRepository;
import com.medium.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Arrays;
@Service
public class InitDbService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private PrivilegeRepository privilegeRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@PostConstruct
public void init() {
Privilege readPrivilege = new Privilege();
readPrivilege.setName("READ_PRIVILEGE");
Privilege writePrivilege = new Privilege();
writePrivilege.setName("WRITE_PRIVILEGE");
privilegeRepository.saveAll(Arrays.asList(readPrivilege, writePrivilege));
Role adminRole = new Role();
adminRole.setName("ROLE_ADMIN");
adminRole.setPrivileges(Arrays.asList(readPrivilege, writePrivilege));
roleRepository.save(adminRole);
Role userRole = new Role();
userRole.setName("ROLE_USER");
userRole.setPrivileges(Arrays.asList(readPrivilege));
roleRepository.save(userRole);
User admin = new User();
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("admin123"));
admin.setRoles(Arrays.asList(adminRole));
userRepository.save(admin);
User user = new User();
user.setUsername("user");
user.setPassword(passwordEncoder.encode("user123"));
user.setRoles(Arrays.asList(userRole));
userRepository.save(user);
}
}Explanation:
- InitDbService: A service class annotated with
@Serviceand@PostConstructto initialize the database with roles, privileges, and users. - init(): This method creates and saves privileges, roles, and users to the database. It sets up an admin user with both read and write privileges and a normal user with only read privileges.
Advanced Security Configuration
Method-Level Security
Spring Security allows you to secure methods in your service layer using annotations.
Enable Method Security:
package com.medium.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
}Explanation:
- EnableGlobalMethodSecurity: This annotation enables method-level security. The
prePostEnabledattribute allows the use of@PreAuthorizeand@PostAuthorizeannotations.
Using Method-Level Security:
package com.medium.service;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class SecureService {
@PreAuthorize("hasRole('ADMIN')")
public void adminOnlyMethod() {
System.out.println("Admin only method");
}
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public void userAndAdminMethod() {
System.out.println("User and Admin method");
}
}Explanation:
- SecureService: A service class with methods secured using
@PreAuthorizeannotations. - adminOnlyMethod(): This method can only be accessed by users with the
ADMINrole. - userAndAdminMethod(): This method can be accessed by users with either the
USERorADMINrole.
Conclusion
Spring Security provides a comprehensive framework for managing roles and privileges in Java applications. By understanding and implementing roles and privileges, you can secure your application effectively, ensuring that users have appropriate access levels. This guide covered the basics of roles and privileges, setting up Spring Security, defining entities, managing roles and privileges, and using method-level security. With these tools and techniques, you can build secure and robust Java applications.
Explore More on Spring and Java Development:
Enhance your skills with our selection of articles:
- Spring Beans Mastery (Dec 17, 2023): Unlock advanced application development techniques. Read More
- JSON to Java Mapping (Dec 17, 2023): Streamline your data processing. Read More
- Spring Rest Tools Deep Dive (Nov 15, 2023): Master client-side RESTful integration. Read More
- Dependency Injection Insights (Nov 14, 2023): Forge better, maintainable code. Read More
- Spring Security Migration (Sep 9, 2023): Secure your upgrade smoothly. Read More
- Lambda DSL in Spring Security (Sep 9, 2023): Tighten security with elegance. Read More
- Spring Framework Upgrade Guide (Sep 6, 2023): Navigate to cutting-edge performance. Read More
References:
- Spring Security Reference Documentation. Retrieved from https://docs.spring.io/spring-security/site/docs/current/reference/html5/
- Baeldung. (2021). Spring Security Tutorial. Retrieved from https://www.baeldung.com/spring-security
- Walls, C. (2016). Spring Security in Action. Manning Publications.
- Turnquist, G. L. (2017). Learning Spring Boot 2.0: Simplify the development of lightning-fast applications based on microservices and reactive programming. Packt Publishing.
- Spring Framework Guru. (2017). Spring Boot Web Application, Part 6 — Spring Security with DAO Authentication Provider. Retrieved from https://springframework.guru/spring-security-role-based-authorization/
- Pivotal Software, Inc. Spring Security Architecture. Retrieved from https://docs.spring.io/spring-security/site/docs/current/reference/html5/#servlet-architecture






