Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security, how to restrict user access certain resources based on dynamic roles?

given a scenario , there is a HTML contents OR some method in a controller, which only allow to be access by "a" role.

from above, we achieve by using @hasRole("a")

However, in my case, the role is dynamic:

Example, admin add a new role "b", and able to be access these content. So how to do it?

  1. I tried ACL, but that's only protect the domain object with an id.

  2. there is an annotation called hasAuthority, but i cant search anythings from internet.

  3. there is an ObjectIdentityImpl, not really how to implement.

like image 996
Stupidfrog Avatar asked Jan 15 '14 03:01

Stupidfrog


People also ask

How do you allow a user only access their own data in spring boot?

In any @Controller , @RestController annotated bean you can use Principal directly as a method argument. @RequestMapping("/users/{user_id}") public String getUserInfo(@PathVariable("user_id") Long userId, Principal principal){ // test if userId is current principal or principal is an ADMIN .... }

What dependency will you need to protect resources with Spring Security in spring boot application?

Spring Boot provides a spring-boot-starter-security starter which aggregates Spring Security related dependencies together. The simplest and preferred method to leverage the starter is to use Spring Initializr using an IDE integration (Eclipse, IntelliJ, NetBeans) or through https://start.spring.io.


2 Answers

EDIT: my solution

  1. After study, ACL is more on secure list of object. Example: u want to secure staff table, some staff record(like CEO,manager) are only accessible by higher management. the rest of staff record are view-able by all. This is what ACL to do.

    However, when we need to protect some method,controller,url,static content.... the ACL is not suitable for this. we need to use hasAuthority or hasPermission or hasRole or ......

  2. In some web systems, there are only few roles, admin and user. For this case, hasAuthority or hasRole is quite enough for this. u just annotate @hasRole('admin') for the resources u want to protect.

    However,in some systems, there are dynamic role, for example: admin create a new role "temporary_user", but the contoller or method is annotate by @hasRole('user'), which not accessible by "temporary_user".

    in this case, based on my understanding, there are few ways to do.

  3. create many roles based on how many resources u want to protect. for example: assign 'role_getRecord' to getRecords(),assign 'role_writeRecord' to writeRecord(). this is a way to do without changing spring security mechanism, but will have a lot of roles on your database table, and more complex system, will have more.

  4. @hasPermission - this is what i use right now. i create a CustomGrantedAuthority, in order to have more flexible implementation. and i do have a CustomUserDetailsService and CustomSpringSecurityUser, when user login will create CustomSpringSecurityUser with collection of CustomGrantedAuthority then return CustomSpringSecurityUser to CustomUserDetailsService. and also i do have a CustomPermission to verify the permission.

    Please vote UP, if your think is useful, and please comment if i wrong or does havea better way to do it.

here is my code

CustomSpringSecurityUser

public class CustomSpringSecurityUser implements UserDetails, CredentialsContainer {
    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;

    public CustomSpringSecurityUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        this(username, password, true, true, true, true, authorities);
    }
    public CustomSpringSecurityUser(String username, String password, boolean enabled, boolean accountNonExpired,
            boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {

        if (((username == null) || "".equals(username)) || (password == null)) {
            throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
        }

        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.accountNonExpired = accountNonExpired;
        this.credentialsNonExpired = credentialsNonExpired;
        this.accountNonLocked = accountNonLocked;
      //  this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
        this.authorities = new HashSet<GrantedAuthority>(authorities);
    }
    public Collection<GrantedAuthority> getAuthorities() {
        return authorities;
    }

    public String getPassword() {
        return password;
    }

    public String getUsername() {
        return username;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    public void eraseCredentials() {
        password = null;
    }

    private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
        Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
        SortedSet<GrantedAuthority> sortedAuthorities =
            new TreeSet<GrantedAuthority>(new AuthorityComparator());

        for (GrantedAuthority grantedAuthority : authorities) {
            Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
            sortedAuthorities.add(grantedAuthority);
        }

        return sortedAuthorities;
    }

    private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

        public int compare(GrantedAuthority g1, GrantedAuthority g2) {
            if (g2.getAuthority() == null) {
                return -1;
            }

            if (g1.getAuthority() == null) {
                return 1;
            }

            return g1.getAuthority().compareTo(g2.getAuthority());
        }
    }

    @Override
    public boolean equals(Object rhs) {
        if (rhs instanceof CustomSpringSecurityUser) {
            return username.equals(((CustomSpringSecurityUser) rhs).username);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return username.hashCode();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString()).append(": ");
        sb.append("Username: ").append(this.username).append("; ");
        sb.append("Password: [PROTECTED]; ");
        sb.append("Enabled: ").append(this.enabled).append("; ");
        sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
        sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");
        sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");

        if (!authorities.isEmpty()) {
            sb.append("Granted Authorities: ");

            boolean first = true;
            for (GrantedAuthority auth : authorities) {
                if (!first) {
                    sb.append(",");
                }
                first = false;

                sb.append(auth);
            }
        } else {
            sb.append("Not granted any authorities");
        }

        return sb.toString();
    }
}

CustomGrantedAuthority

public class CustomGrantedAuthority implements GrantedAuthority{
    private String role;
    private String permission,action;

    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getRole() {
        return role;
    }

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

    @Override
    public String getAuthority() {
        return role;
    }

}

CustomeUserDetailsService

@Service
@Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
        private OcUserService userService;
        private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            sg.com.xx.xx.table.OcUser u = userService.findByLoginname(username);

                        String pass = sg.com.xx.xx.table.OcUser.byteToHex(u.getPassword());
            Collection<? extends GrantedAuthority> permissionList = userService.getPermissionByUserId(u.getId());

            boolean enabled = true;
            boolean accountNonExpired = true;
            boolean credentialsNonExpired = true;
            boolean accountNonLocked = true;
                        CustomSpringSecurityUser user = new CustomSpringSecurityUser(u.getLoginname(), 
                    pass,
                    enabled,
                    accountNonExpired,
                    credentialsNonExpired,
                    accountNonLocked,
                    permissionList);

            return user;

        } catch (Exception e) {
            logger.error("==============================================");
                        logger.error(e.toString());
                        return null;
        }
    }
}

CustomPermission

public class CustomPermission implements PermissionEvaluator {

    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        Collection<? extends GrantedAuthority> x = authentication.getAuthorities();

        for(Object o : x)
        {
            CustomGrantedAuthority y = (CustomGrantedAuthority) o ;
            if(y.getPermission().equals(targetDomainObject) )
                if( y.getAction().equals(permission) )
                    return true;
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        int a = 5;
        return true;
    }

}
like image 120
Stupidfrog Avatar answered Oct 12 '22 00:10

Stupidfrog


I don't know what you mean under resources, but I found that the best way to work with it in spring, is to grant users permissions (authorities) instead of roles, you still have roles, but they are there just to bundle up the permissions. After this is set up, you assign actual permissions for your views and methods. I found a data model here:

http://springinpractice.com/2010/10/27/quick-tip-spring-security-role-based-authorization-and-permissions/

like image 1
Vaelyr Avatar answered Oct 12 '22 02:10

Vaelyr