Avoid repetitive values for @Secured annotation

柔情痞子 提交于 2021-01-27 07:23:38

问题


I am trying to secure my service methods using @Secured as below:

public interface IUserService {

@Secured({"ROLE_ROLE1", "ROLE_ROLE2"})
    ResponseEntity saveUser(CreateUserDtoRequest userDto);

}

I wanna know is there a way to define {"ROLE_ROLE1", "ROLE_ROLE2"} in a variable and read its value from a properties file? That would be great if you can suggest me a trick, to:

  1. remove repetition of {"ROLE_ROLE1", "ROLE_ROLE2"} in other methods
  2. In case of change in required roles to access a method in future, there would be no need to change the code, recompile and deploy it again.

回答1:


There are several ways to do what you need:


Develop your custom MethodSecurityExpressionOperations

In this tutorial you will see how to deal with a new custom security method (section 5) or override the current hasAuthority one (section 6)


Develop your custom method to use in SpEL

Probably an esier option, the steps could be the following ones:

1. Include the allowed roles in your application.yml (or properties)

security:
  rolesAllowed: ADMIN,USER

2. Define the class to check those roles and authorized user ones. For example:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toSet;

@Component
public class FromPropertyRoleSecurityCheck {

  private final static String ROLE_SEPARATOR = ",";

  @Value("${security.rolesAllowed}")
  private String rawRolesAllowed;


  public boolean verifyRoles() {
    return getPrincipalAuthorities()
            .map(auth -> {
                Set<String> rolesAllowed = Stream.of(rawRolesAllowed.split(ROLE_SEPARATOR))
                        .map(String::trim)
                        .collect(toSet());
                return verifyAllowedRoles(rolesAllowed, auth);
            })
            .orElse(false);
  }


  private Optional<Collection<? extends GrantedAuthority>> getPrincipalAuthorities() {
    return ofNullable(SecurityContextHolder.getContext())
            .map(SecurityContext::getAuthentication)
            .map(Authentication::getAuthorities);
  }


  private boolean verifyAllowedRoles(final Collection<String> rolesAllowed,
                                     final Collection<? extends GrantedAuthority> principalAuthorities) {
    if (CollectionUtils.isEmpty(rolesAllowed)) {
        return true;
    }
    if (CollectionUtils.isEmpty(principalAuthorities)) {
        return false;
    }
    Set<String> rolesDiff = principalAuthorities.stream().map(GrantedAuthority::getAuthority).collect(toSet());
    rolesDiff.removeAll(rolesAllowed);
    return rolesDiff.size() != principalAuthorities.size();
  }

}

3. Add the security check:

@PreAuthorize("@fromPropertyRoleSecurityCheck.verifyRoles()")
public ResponseEntity<MyDto> findById(@PathVariable @Positive Integer id) {
  ...
}

If you don't want to recompile/deploy the project every time those roles change, you can save them in an external storage like database for example (shouldn't be a problem to update any of provided examples to deal with such situations). In the second one I used a property to keep it simple, but is quite easy to include a Repository in FromPropertyRoleSecurityCheck to get them from database.

PD. Examples of provided link and custom one were developed in Controller layer, but they should work in the Service one too.



来源:https://stackoverflow.com/questions/64133772/avoid-repetitive-values-for-secured-annotation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!