避免@Secured注释的重复值



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

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

我想知道有没有一种方法可以在变量中定义{"ROLE_ROLE1", "ROLE_ROLE2"},并从properties文件中读取其value?如果你能给我建议一个技巧,那就太好了:

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

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


开发自定义MethodSecurityExpressionOperations

在本教程中,您将看到如何处理新的自定义安全方法(第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层中开发的,但它们也应该在服务中工作。

最新更新