2

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???

2 Answers2

1

Assign two different roles to Admin and John ROLE_ADMIN, ROLE_USER respectively. And then check role inside controller and call corresponding service method to return data according to their role.

@RequestMapping(value = "/load/{id}", method = RequestMethod.GET)
@ResponseBody
public StudentYearViewModel load(HttpServletRequest request, Authentication authentication, @PathVariable long id) {

    if (request.isUserInRole("ROLE_ADMIN")) {
         return ModelMapper.map(iStudentService.loadByEntityId(id), StudentViewModel.class); //return all records
    } if (request.isUserInRole("ROLE_USER")) {
     String username = authentication.getName(); //get logged in user i.e. john
         return ModelMapper.map(iStudentService.loadByEntityId(id, username), StudentViewModel.class); //return records by username
    }
}
Monzurul Shimul
  • 8,132
  • 2
  • 28
  • 42
1

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.

Leon
  • 12,013
  • 5
  • 36
  • 59
  • thanks!! your solution is so much better but I have question. "filterObject.getClassId()" how does Work??? Is this Performance good? Is this best way to get All list than remove some one?? I think if i write this filter in DB is not OK?? – seyedmohsen Mirhosieni Mar 04 '17 at 05:19
  • `filterObject` is obtained by Spring using introspection. Performance is a very relative term. I use it in a lot of places without any performance issues. My feeling is to write the best cleanest, easiest to maintain code and to address performance issues when they arise. Filtering the data in the database is "better" from a performance point of view, assuming you are successfully hitting indexes. Filtering on the database can also reduce network I/O. – Leon Mar 06 '17 at 06:35