Search code examples
springspring-securityaclhdiv

Best implementation 'Access' User in spring security


I have a problem to implement security in my application ... I have custom authentication and use @PreAuthorize to handle my user authorization. This works fine. Now I want to implement Access Control for each user, which means in my application when two users, 'Admin' and 'John', could call method

@RequestMapping(value = "/load/{id}", method = RequestMethod.GET)
@ResponseBody
public StudentYearViewModel load(@PathVariable long id) {
    return ModelMapper.map(iStudentService.loadByEntityId(id), StudentViewModel.class);
}

'Admin' can use this method for all Student instances but 'John' can see only his classmate! All users could call this method (@PreAuthorize is not suitable) but their Access is limited HOW do it?? Now have general way?

is ACL best Way?(has best example?)

HDIV framework could help me solve my problem??

what is best solution???


Solution

  • You want to look at @PostFilter and @PreFilter. They work pretty much like @PreAuthorize, but can remove results from lists. You also want to assign different roles to your users, assuming you are not doing that already.

    Global rules, like admin being able to see everything, you can implement by writing a concrete implementation of PermissionEvaluator. You then add that to the MethodSecurityExpressionHandler

    Time for a simple example.

    This code was written in a text editor. It may not compile and is only here to show the steps needed

    A very simplistic PermissionEvaluator

    public class MyPermissionEvaluator implements PermissionEvaluator {
        private static final SimpleGrantedAuthority AUTHORITY_ADMIN = new SimpleGrantedAuthority('admin');
    
        public boolean hasPermission(final Authentication authentication, final Object classId, final Object permission) {
            boolean permissionGranted = false;
    
            // admin can do anything
            if (authentication.getAuthorities().contains(AUTHORITY_ADMIN)) {
                permissionGranted = true;
            } else {
                // Check if the logged in user is in the same class
            }
            return permissionGranted;
        }
    
        @Override
        public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
                Object permission) {
            return false;
        }
    
    }
    

    Then configure method security

    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    
        @Bean
        public MethodSecurityExpressionHandler methodSecurityExpressionHandler(final PermissionEvaluator permissionEvaluator){
            DefaultMethodSecurityExpressionHandler securityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
            securityExpressionHandler.setPermissionEvaluator(permissionEvaluator);
            return securityExpressionHandler;
        }
    
        @Bean
        public PermissionEvaluator permissionEvaluator() {
            return new MyPermissionEvaluator();
        }
    }
    

    Now we can use our filter on a method

    @PostFilter("hasPermission(filterObject.getClassId(), 'READ')")
    @Override
    public List<Student> getAll() {
        return querySomeStudents();
    }
    

    hasPermission in the @PostFilter ACL will invoke hasPermission in MyPermissionEvaluator. filterObject refers to the individual items in the list. Wherever you code returns false, it will remove the item from the list.