【发布时间】:2019-09-13 18:55:57
【问题描述】:
我在使用 Spring Security 中的 Pre/Post Authorization Annotations 和带有 Keycloak 集成的 Servlet API 时遇到问题。我调查了很多文章、教程和以下问题没有进一步的运气:
- Obtaining user roles in servlet application using keycloak
- Spring Boot Keycloak - How to get a list of roles assigned to a user?
- Using spring security annotations with keycloak
- Spring Boot + Spring Security + Hierarchical Roles
- How do I add method based security to a Spring Boot project?
- Configure DefaultMethodSecurityExpressionHandler using Spring Security Java Config
- SpringBoot + method based hierarchical roles security: ServletContext is required
我想要的只是删除 ROLES_ 前缀,使用分层角色和一种舒适的方式来检索用户的角色。
到目前为止,我可以在 Controller 中检索这样的分层角色,但不能使用注释:
@Controller
class HomeController {
@Autowired
AccessToken token
@GetMapping('/')
def home(Authentication auth, HttpServletRequest request) {
// Role 'admin' is defined in Keycloak for this application
assert token.getResourceAccess('my-app').roles == ['admin']
// All effective roles are mapped
assert auth.authorities.collect { it.authority }.containsAll(['admin', 'author', 'user'])
// (!) But this won't work:
assert request.isUserInRole('admin')
}
// (!) Leads to a 403: Forbidden
@GetMapping('/sec')
@PreAuthorize("hasRole('admin')") {
return "Hello World"
}
}
我猜@PreAuthorize 注释不起作用,因为那个 Servlet 方法不成功。
在 Keycloak 和 Spring 中只定义了三个角色——管理员、作者、用户:
enum Role {
USER('user'),
AUTHOR('author'),
ADMIN('admin')
final String id
Role(String id) {
this.id = id
}
@Override
String toString() {
id
}
}
密钥斗篷配置
从该网络安全中删除 @EnableGlobalMethodSecurity 注释后,会显示由 No ServletContext set 错误引起的 Error creating bean with name 'resourceHandlerMapping' - 不知道它来自哪里!
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
@Autowired
void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(keycloakAuthenticationProvider().tap { provider ->
// Assigns the Roles via Keycloaks role mapping
provider.grantedAuthoritiesMapper = userAuthoritiesMapper
})
}
@Bean
RoleHierarchyImpl getRoleHierarchy() {
new RoleHierarchyImpl().tap {
hierarchy = "$Role.ADMIN > $Role.AUTHOR > $Role.USER"
}
}
@Bean
GrantedAuthoritiesMapper getUserAuthoritiesMapper() {
new RoleHierarchyAuthoritiesMapper(roleHierarchy)
}
SecurityExpressionHandler<FilterInvocation> expressionHandler() {
// Removes the prefix
new DefaultWebSecurityExpressionHandler().tap {
roleHierarchy = roleHierarchy
defaultRolePrefix = null
}
}
// ...
@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
AccessToken accessToken() {
def request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()
def authToken = (KeycloakAuthenticationToken) request.userPrincipal
def securityContext = (KeycloakSecurityContext) authToken.credentials
return securityContext.token
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http)
http
.authorizeRequests()
.expressionHandler(expressionHandler())
// ...
}
}
全局方法安全配置
我需要明确允许allow-bean-definition-overriding,否则我会收到bean with that name already defined 错误,这表明我完全失去了对整个情况的控制,不知道发生了什么。
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Autowired
RoleHierarchy roleHierarchy
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
((DefaultMethodSecurityExpressionHandler)super.createExpressionHandler()).tap {
roleHierarchy = roleHierarchy
defaultRolePrefix = null
}
}
}
任何其他可能很重要的配置?非常感谢您的帮助!
【问题讨论】:
-
isUserInRole将检查用户是否具有ROLE_admin角色,但事实并非如此。在 Spring Security 中进行角色检查时,默认情况下会自动添加ROLE_前缀。而不是使用hasRole使用hasAuthority,它不会添加ROLE_前缀。 -
这就是为什么我将
defaultRolePrefix设置为 null,这应该可以解决问题。 -
不是真的,因为您还需要修改其他一些地方。 Spring Security 参考指南中有一个示例,它修改了 4 个不同位置的前缀,使用
BeanPostProcessor而不是重新声明所有 bean。
标签: spring spring-boot groovy spring-security keycloak