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