spring with grails - 如何拒绝使用逻辑删除属性删除的用户的登录

时间:2014-05-12 00:43:45

标签: grails spring-security

我使用的是grails 2.2.2和spring-security-core:2.0-RC2

我的域用户对象实际上并没有从数据库中删除 - 而是我设置了一个名为"删除的属性"为真。

问题是:如何阻止Spring安全性授予对标记为已删除的用户ID的成功登录尝试?

我希望能够支持使用以前删除的名称创建新用户。

下面的Burt Beckwith的回答让我看到了覆盖Spring安全bean实现的问题。

我尝试用我的实现覆盖userDetailsS​​ervice 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')
    }
}

2 个答案:

答案 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");
    }
  }
}
相关问题