java - 避免@Secured 注解的重复值

标签 java spring-boot spring-security

我正在尝试使用 @Secured 来保护我的服务方法如下:

public interface IUserService {

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

}

我想知道有没有办法定义{"ROLE_ROLE1", "ROLE_ROLE2"}在一个变量中并读取它的 value来自properties文件? 如果您能建议我一个技巧,那就太好了:

  1. 删除重复的{"ROLE_ROLE1", "ROLE_ROLE2"}用其他方法
  2. 如果将来访问方法所需的角色发生变化,则无需更改代码、重新编译和再次部署。

最佳答案

有几种方法可以满足您的需要:


开发您的自定义 MethodSecurityExpressionOperations

在此tutorial您将看到如何处理新的自定义安全方法(第 5 节)或覆盖当前的 hasAuthority 方法(第 6 节)


开发您的自定义方法以在 SpEL 中使用

可能是一个更简单的选择,步骤可能是以下步骤:

1. 在您的application.yml(或properties)中包含允许的角色

security:
  rolesAllowed: ADMIN,USER

2. 定义类来检查那些角色和授权用户。例如:

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. 添加安全检查:

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

如果您不想每次这些角色更改时都重新编译/部署项目,您可以将它们保存在外部存储中,例如数据库(更新任何内容应该不成问题)提供的例子来处理这种情况)。在第二个中,我使用了一个属性来保持简单,但是在 FromPropertyRoleSecurityCheck 中包含一个 Repository 以从数据库中获取它们是很容易的。

PD。提供的链接和自定义链接的示例是在 Controller 层开发的,但它们也应该在 Service 层中工作。

关于java - 避免@Secured 注解的重复值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64133772/

相关文章:

java - android java从sd卡播放wav音频文件

java - 如何使用map的依赖注入(inject)来测试Spring boot应用程序配置类?

spring - Spring Boot 中解析 graphql schema 时出错,如何修复?

spring-boot - 彻底删除log4j

java - 如何避免过滤器后在 dispatcherServlet 中关闭输入流

java - 如何访问 Spring Soap 端点中的 SOAP header ?

java - 网络代理背后的 Spring-Boot

java - 无法解析 R.array

java - 使用 oAuth 验证 javascript 小部件

java - Eclipse 项目到 mercurial