避免在 @Secured 注解中重复的值。

huangapple go评论72阅读模式
英文:

Avoid repetitive values for @Secured annotation

问题

我正在尝试使用以下方式通过 `@Secured` 来保护我的服务方法

```java
public interface IUserService {

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

}

我想知道是否有一种方法可以将 {"ROLE_ROLE1", "ROLE_ROLE2"} 定义为一个变量,并从 properties 文件中读取其 value 值?
如果您能为我提供一个诀窍,那将非常好:

  1. 从其他方法中消除 {"ROLE_ROLE1", "ROLE_ROLE2"} 的重复
  2. 如果将来访问方法所需的角色发生更改,则无需更改代码,重新编译并再次部署。
请注意,由于您要求只返回翻译好的部分,上述即为您提供的文本的翻译。如果您有任何其他需要,请随时告诉我。

<details>
<summary>英文:</summary>

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 `{&quot;ROLE_ROLE1&quot;, &quot;ROLE_ROLE2&quot;}` 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 `{&quot;ROLE_ROLE1&quot;, &quot;ROLE_ROLE2&quot;}` 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. 
</details>
# 答案1
**得分**: 1
有几种方法可以实现你的需求:
----------
## 开发自定义的 `MethodSecurityExpressionOperations` ##
在这个 [教程][1] 中,你会看到如何处理新的自定义安全方法(*第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 = "&quot;,&quot;";
@Value("&quot;${security.rolesAllowed}&quot;")
private String rawRolesAllowed;
public boolean verifyRoles() {
return getPrincipalAuthorities()
.map(auth -&gt; {
Set&lt;String&gt; rolesAllowed = Stream.of(rawRolesAllowed.split(ROLE_SEPARATOR))
.map(String::trim)
.collect(toSet());
return verifyAllowedRoles(rolesAllowed, auth);
})
.orElse(false);
}
private Optional&lt;Collection&lt;? extends GrantedAuthority&gt;&gt; getPrincipalAuthorities() {
return ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.map(Authentication::getAuthorities);
}
private boolean verifyAllowedRoles(final Collection&lt;String&gt; rolesAllowed,
final Collection&lt;? extends GrantedAuthority&gt; principalAuthorities) {
if (CollectionUtils.isEmpty(rolesAllowed)) {
return true;
}
if (CollectionUtils.isEmpty(principalAuthorities)) {
return false;
}
Set&lt;String&gt; rolesDiff = principalAuthorities.stream().map(GrantedAuthority::getAuthority).collect(toSet());
rolesDiff.removeAll(rolesAllowed);
return rolesDiff.size() != principalAuthorities.size();
}
}
**3.** 添加安全检查:
@PreAuthorize("&quot;@fromPropertyRoleSecurityCheck.verifyRoles()&quot;")
public ResponseEntity&lt;MyDto&gt; findById(@PathVariable @Positive Integer id) {
...
}
----------
**如果你不想每次更改这些角色时都重新编译/部署项目**,你可以将它们保存在外部存储中,比如数据库(在提供的示例中更新任何一个示例来处理这种情况应该不成问题)。在第二个示例中,我使用了一个属性来保持简单,但是在 `FromPropertyRoleSecurityCheck` 中包含一个 `Repository` 来从数据库获取它们也很容易。
附:提供链接的示例和自定义示例都是在 **Controller** 层开发的,但它们也应该适用于 **Service** 层。
[1]: https://www.baeldung.com/spring-security-create-new-custom-security-expression
<details>
<summary>英文:</summary>
There are several ways to do what you need:
----------
## Develop your custom `MethodSecurityExpressionOperations` ##
In this [tutorial][1] 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 = &quot;,&quot;;
@Value(&quot;${security.rolesAllowed}&quot;)
private String rawRolesAllowed;
public boolean verifyRoles() {
return getPrincipalAuthorities()
.map(auth -&gt; {
Set&lt;String&gt; rolesAllowed = Stream.of(rawRolesAllowed.split(ROLE_SEPARATOR))
.map(String::trim)
.collect(toSet());
return verifyAllowedRoles(rolesAllowed, auth);
})
.orElse(false);
}
private Optional&lt;Collection&lt;? extends GrantedAuthority&gt;&gt; getPrincipalAuthorities() {
return ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.map(Authentication::getAuthorities);
}
private boolean verifyAllowedRoles(final Collection&lt;String&gt; rolesAllowed,
final Collection&lt;? extends GrantedAuthority&gt; principalAuthorities) {
if (CollectionUtils.isEmpty(rolesAllowed)) {
return true;
}
if (CollectionUtils.isEmpty(principalAuthorities)) {
return false;
}
Set&lt;String&gt; rolesDiff = principalAuthorities.stream().map(GrantedAuthority::getAuthority).collect(toSet());
rolesDiff.removeAll(rolesAllowed);
return rolesDiff.size() != principalAuthorities.size();
}
}
**3.** Add the security check: 
@PreAuthorize(&quot;@fromPropertyRoleSecurityCheck.verifyRoles()&quot;)
public ResponseEntity&lt;MyDto&gt; findById(@PathVariable @Positive Integer id) {
...
}
----------
**If you don&#39;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&#39;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.
[1]: https://www.baeldung.com/spring-security-create-new-custom-security-expression
</details>

huangapple
  • 本文由 发表于 2020年9月30日 16:29:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/64133772.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定