有许多方法可以使用注释以及基于端点的安全配置来设计对 API 的基于权限的访问。
注释:
@Secured
@PreAuthorize
@PostAuthorize
@RolesAllowed
@PreFilter
@PostFilter
为了使用注释,您需要启用以下安全配置
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
-
prePostEnabled 属性启用 Spring Security
pre/post 注释
-
securedEnabled 属性确定是否应启用
@Secured 注释
-
jsr250Enabled 属性允许我们使用
@RoleAllowed 注释
@Secured & @RoleAllowed
具有给定角色的用户能够执行该方法。 @RoleAllowed 注解是 JSR-250 中 @Secured 注解的等效注解。
@Secured({ "ROLE_ADMIN", "ROLE_SUPERADMIN" })
public ResponseEntity<?> save(...) {
...
}
@RolesAllowed({ "ROLE_ADMIN", "ROLE_SUPERADMIN" })
public ResponseEntity<?> save(...) {
...
}
@PreAuthorize & @PostAuthorize
@PreAuthorize 注释在进入方法之前检查给定的表达式,而@PostAuthorize 注释在方法执行后验证它并可能改变结果。
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_SUPERADMIN')")
public ResponseEntity<?> save(...) {
...
}
@PreAuthorize & @PostAuthorize 和@Secured 之间的主要区别在于@Secured 不支持SpEL(Spring 表达式语言)。要检查更多差异,您可以阅读更多详细信息here
@PreAuthorize("#username == authentication.principal.username")
public String methodX(String username) {
//...
}
@PostAuthorize("#username == authentication.principal.username")
public String methodY(String username) {
//...
}
这里,只有当参数 username 的值与当前主体的用户名相同时,用户才能调用 methodX。您可以检查其他可能的 SpEL(Spring 表达式语言) 自定义here
您可以从here获取更多详细信息
使用configure(HttpSecurity http) 和configure(WebSecurity web) 方法。
@EnableWebSecurity
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web
.ignoring()
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/test/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/**").hasAuthority(AuthoritiesConstants.USER)
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN);
// @formatter:on
}
}
-
configure(WebSecurity web)
此方法中使用的端点忽略了 spring 安全过滤器,安全功能(安全标头、csrf 保护等)也被忽略,并且不会设置安全上下文,并且无法保护端点以进行跨站点脚本、XSS 攻击、内容嗅探。
-
configure(HttpSecurity http)
此方法中使用的端点忽略了 antMatchers 中使用的端点的身份验证,其他安全功能将生效,例如安全标头、CSRF 保护等。
您可以在configure(HttpSecurity http) 中使用hasRole()、hasAnyRole()、hasAuthority()、hasAnyAuthority() 方法。请注意,对于 hasRole()、hasAnyRole() 方法,您不需要使用 ROLE_ 前缀,而对于其他两个方法,您必须使用 ROLE_
要了解区别和用法,您可以获取详细信息here
您还可以按以下方式创建 utils 方法,这可能会有所帮助。
/**
* Get the login of the current user.
*
* @return the login of the current user.
*/
public static Optional<String> getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
return (String) authentication.getPrincipal();
}
return null;
});
}
/**
* Check if a user is authenticated.
*
* @return true if the user is authenticated, false otherwise.
*/
public static boolean isAuthenticated() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(authentication.getAuthorities());
return authorities.stream()
.noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS));
})
.orElse(false);
}
/**
* If the current user has a specific authority (security role).
* <p>
* The name of this method comes from the {@code isUserInRole()} method in the Servlet API.
*
* @param authority the authority to check.
* @return true if the current user has the authority, false otherwise.
*/
public static boolean isCurrentUserInRole(String authority) {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(authentication.getAuthorities());
return authorities.stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
})
.orElse(false);
}
public static Optional<Authentication> getAuthenticatedCurrentUser() {
log.debug("Request to get authentication for current user");
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication());
}
更新
@Component("userVerifier")
public class UserVerifier {
public boolean isPermitted(Authentication authentication) {
String PERMITTED_USERNAME = Arrays.asList("abc", "xyz");
return PERMITTED_USERNAME.stream.anyMatch(username -> authentication.getName().equals(username));
}
}
在安全配置中,我们可以使用configure(HttpSecurity http) 如下,它将调用isPermitted() 方法。
http
.authorizeRequests()
.antMatchers("/your-endpoint/{id}")
.access("@userVerifier.isPermitted(authentication)")
...
或者使用如下注解:
@PreAuthorize("@userVerifier.isPermitted(authentication)")
@PostMapping("{id}")
public ResponseEntity<?> save(Authentication authentication, Principal principal, @PathVariable Integer id, @RequestBody UserNewDTO dto) {
........
}
您可以从here 和from this blog 找到更多详细信息