我正在尝试使用 @Secured
来保护我的服务方法如下:
public interface IUserService {
@Secured({"ROLE_ROLE1", "ROLE_ROLE2"})
ResponseEntity saveUser(CreateUserDtoRequest userDto);
}
我想知道有没有办法定义{"ROLE_ROLE1", "ROLE_ROLE2"}
在一个变量中并读取它的 value
来自properties
文件?
如果您能建议我一个技巧,那就太好了:
- 删除重复的
{"ROLE_ROLE1", "ROLE_ROLE2"}
用其他方法 - 如果将来访问方法所需的角色发生变化,则无需更改代码、重新编译和再次部署。
最佳答案
有几种方法可以满足您的需要:
开发您的自定义 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/