在我的Java Sping Boot 3应用程序中,我想使用Spring Security和@PreAuthorize
/@PostAuthorize
。由于某种原因,我收到的Keycloak生成的令牌没有Spring期望的Authorities
下的Roles,而是在"realm_access"
属性下。
因此,我决定继续实现一个自定义Expession处理程序,这与我在www.example.com中找到的内容基本相同https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html#customizing-expression-handling
那么,一些代码
表达式根
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
public boolean hasRealmRole(String role) {
var principal = ((OAuth2AuthenticatedPrincipal)this.getPrincipal());
if (principal == null) {
return false;
}
var realmAccess = (JSONObject)principal.getAttribute("realm_access");
if (realmAccess == null) {
return false;
}
var roles = (JSONArray)realmAccess.get("roles");
if (roles == null || roles.isEmpty()) {
return false;
}
return roles.stream().anyMatch(x -> x.equals(role));
}
@Override
public void setFilterObject(Object filterObject) {
}
@Override
public Object getFilterObject() {
return null;
}
@Override
public void setReturnObject(Object returnObject) {
}
@Override
public Object getReturnObject() {
return null;
}
@Override
public Object getThis() {
return null;
}
}
表达式处理程序
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root =
new CustomMethodSecurityExpressionRoot(authentication);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
安全配置
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/health/**").permitAll()
.requestMatchers("/swagger-ui/**").permitAll()
.requestMatchers("/swagger/**").permitAll()
.requestMatchers("/v3/api-docs/**").permitAll()
// Cloud config related endpoint
.requestMatchers("/configuration/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
return http.build();
}
}
控制器
@PreAuthorize("hasRealmRole('FOOBAR')")
@GetMapping("/{id}")
public ResponseEntity<Asset> getAsset(@UUIDConstraint @PathVariable("id") String id) {
...
最后,基于上面添加的Spring引用的Beans
@Configuration
public class BaseConfig {
@Bean
public RoleHierarchy roleHierarchy() {
return new RoleHierarchyImpl();
}
@Bean
static MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
var handler = new CustomMethodSecurityExpressionHandler();
handler.setRoleHierarchy(roleHierarchy);
return handler;
}
}
现在,当我向/GET
端点发送请求时,我得到以下错误:
java.lang.IllegalArgumentException: Failed to evaluate expression 'hasRealmRole('FOOBAR')'
...
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method hasRealmRole(java.lang.String) cannot be found on type org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
为什么我的CustomMethodSecurityExpressionRoot
没有正确注册?
1条答案
按热度按时间2hh7jdfx1#
首先,您所做的并不完全适合权威机构Map:存在用于此的认证(和授权)转换器。我写了一个教程来配置一个非常通用的权威Map器there。它与在领域级别(您在
realm_access.roles
声明中找到的)以及客户端级别(resource-access.{client-id}.roles
声明)定义的Keycloak角色兼容。丰富安全SpEL DSL实际上有点棘手(我不得不复制Spring保护类以使其工作),并且应该在需要超过RoleBasedAccessC控制时使用。我有another tutorial,但它是相当坚持我的libs和启动器(前一个我链接不是)。
下面是一个示例表达式处理程序配置:
使用以下表达式root:
下面是我从Spring Security复制的代码: