我使用的是grails 2.2.2和spring-security-core:2.0-RC2
我的域用户对象实际上并没有从数据库中删除 - 而是我设置了一个名为"删除的属性"为真。
问题是:如何阻止Spring安全性授予对标记为已删除的用户ID的成功登录尝试?
我希望能够支持使用以前删除的名称创建新用户。
下面的Burt Beckwith的回答让我看到了覆盖Spring安全bean实现的问题。我尝试用我的实现覆盖userDetailsService bean和springSecurityService中的几个方法,如下所示(我所做的只是使用相同的父类实现,但更改了User.findWhere()方法以使用已删除:false)。
我还将这些bean定义添加到resources.groovy但我发现有时会调用原始的SpringSecurityService.getCurrentUser()方法而不是我的实现。 (如果我将spring安全源更改为我的实现,这一切都运行正常,但我宁愿使用覆盖,以便将来的版本升级不会中断。)
class MySpringSecurityService extends SpringSecurityService {
@Override
Object getCurrentUser() {
if (!isLoggedIn()) {
return null
}
String className = SpringSecurityUtils.securityConfig.userLookup.userDomainClassName
String usernamePropName = SpringSecurityUtils.securityConfig.userLookup.usernamePropertyName
grailsApplication.getClassForName(className).findWhere(
(usernamePropName): principal.username,
deleted: false)
}
}
class MyUserDetailsService extends GormUserDetailsService {
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException {
def conf = SpringSecurityUtils.securityConfig
String userClassName = conf.userLookup.userDomainClassName
def dc = grailsApplication.getDomainClass(userClassName)
if (!dc) {
throw new IllegalArgumentException("The specified user domain class '$userClassName' is not a domain class")
}
Class<?> User = dc.clazz
User.withTransaction { status ->
def user = User.findWhere((conf.userLookup.usernamePropertyName): username, deleted: false)
if (!user) {
log.warn "User not found: $username"
throw new NoStackUsernameNotFoundException()
}
Collection<GrantedAuthority> authorities = loadAuthorities(user, username, loadRoles)
createUserDetails user, authorities
}
}
}
我的resources.groovy看起来像这样:
beans = {
userDetailsService(MyUserDetailsService)
springSecurityService(MySpringSecurityService) {
authenticationTrustResolver = ref('authenticationTrustResolver')
grailsApplication = ref('grailsApplication')
passwordEncoder = ref('passwordEncoder')
objectDefinitionSource = ref('objectDefinitionSource')
userDetailsService = ref('userDetailsService')
userCache = ref('userCache')
}
}
答案 0 :(得分:1)
这个没有经过测试,但是应该工作或非常接近工作。
您需要覆盖插件注册的preAuthenticationChecks
Spring bean中的逻辑。默认实现执行isAccountNonLocked()
,isEnabled()
和isAccountNonExpired()
检查,因此您可以对其进行子类化并添加检查:
package com.foo.bar
import grails.plugin.springsecurity.userdetails.DefaultPreAuthenticationChecks
import org.springframework.security.authentication.AccountStatusException
class MyPreAuthenticationChecks extends DefaultPreAuthenticationChecks {
private static final EXCEPTION = new DeletedException()
void check(UserDetails user) {
// do the standard checks
super.check user
// then the custom check(s)
if (user.deleted) {
log.debug 'User account is deleted'
throw EXCEPTION
}
}
static class DeletedException extends AccountStatusException {
LockedException() {
super('User account is deleted')
}
// avoid the unnnecessary cost
Throwable fillInStackTrace() {
this
}
}
}
将它放在与您选择的包对应的/ src / groovy目录的子目录中,并在grails-app/conf/spring/resources.groovy
中注册您的:
import com.foo.bar.MyPreAuthenticationChecks
beans = {
preAuthenticationChecks(MyPreAuthenticationChecks)
}
请注意&#34;用户&#34;您正在使用的实例是UserDetails
实例,而不是为填充UserDetails
而加载的域类实例。因此,要访问deleted
媒体资源,您还需要自定义UserDetailsService
。这很简单,也很常见,因为它在文档中有自己的章节:http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/userDetailsService.html
请注意,将异常作为静态内部类,使用单例并覆盖fillInStackTrace
方法都是可选的,并且与此问题无关。如果您愿意,可以将它放在自己的顶级课程中(因为它不可能在本课程之外使用,因此我将其保持在内部是有意义的)。您也可以每次创建一个新实例,但实例之间没有区别(没有有用的状态),因此不需要。我覆盖到fillInStackTrace
,以避免在根本不需要填充所有堆栈帧时产生费用。
答案 1 :(得分:0)
迟到了,但如果它为某人服务,那么它就是:
简单地简化Burt Beckwith的答案,如果你只是想抛出相同的异常,就好像用户不存在一样,你可以像这样扩展DefaultPreAuthenticationChecks
(其中Person是与用户一起的表),那里不需要自定义UserDetailsService
:
import grails.plugin.springsecurity.userdetails.DefaultPreAuthenticationChecks;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
class MyPreAuthenticationChecks extends DefaultPreAuthenticationChecks {
void check(UserDetails user) {
// do the standard checks
super.check user
if (Person.findByUsername(user.getUsername())?.deleted){
log.debug("Wrong credentials");
throw new UsernameNotFoundException("Wrong credentials");
}
}
}