Okay, here is a simple example to demonstrate how to use Spring Security to implement role-based data permission control.
First, in Spring Security, you can use the @PreAuthorize annotation to configure data permission control. The @PreAuthorize annotation can be used to verify the permissions of parameters before calling the method. If the permission verification fails, an exception will be thrown.
Create entity classes and data access layer
First, we create a User entity class and a UserRepository data access layer. The User entity class contains a username field and a departmentId field, which are used to represent the user's account name and department. The UserRepository data access layer provides a findByDepartmentId method to obtain all user information under the department based on the department ID.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private Long departmentId;
// getters and setters
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByDepartmentId(Long departmentId);
}
Create service layer
Next, we create a UserService service layer to provide query services for user information. In the UserService service layer, we define a getUsersByDepartmentId method, which is used to obtain all user information under the department based on the department ID. In the method, we first obtain the role and department information of the currently logged in user, and then filter the returned user list based on the role and department information of the currently logged in user, and only return user information that meets the conditions.
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private DepartmentService departmentService;
public List<User> getUsersByDepartmentId(Long departmentId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentUsername = authentication.getName();
Set<String> currentRoles = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
Long currentDepartmentId = departmentService.getDepartmentIdByUsername(currentUsername);
List<User> users = userRepository.findByDepartmentId(departmentId);
if (!currentRoles.contains("ADMIN") && !Objects.equals(currentDepartmentId, departmentId)) {
users = users.stream()
.filter(user -> Objects.equals(user.getDepartmentId(), currentDepartmentId))
.collect(Collectors.toList());
}
return users;
}
}
Create controller
Finally, we create a UserController controller to provide an interface for user information query. In the UserController controller, we use the @PreAuthorize annotation to configure permission control on the interface. Only users with the ADMIN role or the corresponding user with read permission can access this interface.
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{departmentId}")
@PreAuthorize("hasRole('ADMIN') or hasPermission(#departmentId, 'read')")
public List<User> getUsers(@PathVariable Long departmentId) {
return userService.getUsersByDepartmentId(departmentId);
}
}
Configure Spring Security
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private CustomPermissionEvaluator customPermissionEvaluator;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers(HttpMethod.GET, "/users/**").access("hasRole('ADMIN') or hasPermission(#departmentId, 'read')")
.antMatchers(HttpMethod.POST, "/users").access("hasRole('ADMIN') or hasPermission(#user.departmentId, 'write')")
.antMatchers(HttpMethod.PUT, "/users/**").access("hasRole('ADMIN') or hasPermission(#user.departmentId, 'write')")
.antMatchers(HttpMethod.DELETE, "/users/**").access("hasRole('ADMIN') or hasPermission(#user.departmentId, 'write')")
.and()
.formLogin()
.and()
.httpBasic()
.and()
.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
@Override
public void configure(WebSecurity web) throws Exception {
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(customPermissionEvaluator);
web.expressionHandler(handler);
}
}
In the SecurityConfig configuration class, we use two custom services, CustomUserDetailsService and CustomPermissionEvaluator . Among them, CustomUserDetailsService is used to load user information, and CustomPermissionEvaluator is used to determine whether the user has specified permissions.
Custom service
Finally, we also need to implement two custom services, CustomUserDetailsService and CustomPermissionEvaluator . The CustomUserDetailsService service is used to load user information from the database. In the CustomPermissionEvaluator service, we implement the hasPermission method, which is used to determine whether the current user has the specified permissions.
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));
List<SimpleGrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
return new org.springframework.security.core.userdetails.User(user.getUsername(), "", authorities);
}
}
@Service
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Autowired
private UserRepository userRepository;
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
if (targetDomainObject == null) {
return true;
}
Long departmentId = (Long) targetDomainObject;
User currentUser = userRepository.findByUsername(authentication.getName())
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + authentication.getName()));
if (currentUser.getDepartmentId() == null) {
return false;
}
if ("read".equals(permission)) {
return currentUser.getDepartmentId().equals(departmentId) || "ADMIN".equals(authentication.getAuthorities().iterator().next().getAuthority());
} else if ("write".equals(permission)) {
return currentUser.getDepartmentId().equals(departmentId) || "ADMIN".