Grails Spring Security通过id获取用户

时间:2013-11-16 16:07:15

标签: grails spring-security

我正在使用Spring Security插件编写Grails应用程序。

我已将GORM生成的查询启用到控制台中,我注意到每个请求Security都会向数据库查询用户,并通过用户名选择它们。

我的意图是通过它的ID来加载用户,而不是用户名来提高性能。

我知道有可能覆盖UserDetailsService方法loadUserByUsername(String username),但此方法既用于在会话期间刷新用户的凭据,也用于登录表单,实际上我想要验证用户的用户名。

我有三个问题:

  1. 如何通过ID加载用户?我应该在GrailsUserUserDetails的实施)而不是常规用户名中注入用户ID而不是用户名并使用long selectById = Long.valueOf(String username)吗?
  2. 如何创建不同的用户提供程序以刷新会话以按ID获取用户而不同用户登录(当我想通过用户名/电子邮件获取用户时)?
  3. 有可能不是每次请求都获取用户凭据,而是每隔X秒获取一次?

1 个答案:

答案 0 :(得分:2)

最后我设法解决了这个问题。查询由以下内容生成:

springSecurityService.getCurrentUser()

不幸的是,此方法通过用户名(来自Principal对象)获取User模型类并将其映射到数据库字段,最多配置为: grails.plugin.springsecurity.userLookup.usernamePropertyName 如上所述in documentation

我试过了

grails.plugin.springsecurity.userLookup.usernamePropertyName = 'id'

但是我收到了从String到Long的类转换异常。

解决方法很简单 - 创建自己的原则,将username字段输入为Long。

请参阅我的解决方案中的PrincipalProxy

package com.selly.util.security

import java.security.Principal;

import grails.plugin.springsecurity.userdetails.GrailsUser

import org.springframework.security.core.Authentication
import org.springframework.security.core.GrantedAuthority

class AppMetadataAuthenticationToken implements Authentication, Principal {

    private boolean authenticated
    private GrailsUser userDetails
    private Principal principal

    public AppMetadataAuthenticationToken(GrailsUser userDetails) {
        this.userDetails = userDetails
        this.principal = new PrincipalProxy(userDetails)
    }

    public GrailsUser getUser() {
        return userDetails
    }

    public String getUsername() {
        return userDetails.getUsername()
    }

    @Override
    public String getName() {
        return userDetails.getUsername()
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return userDetails.getAuthorities()
    }

    @Override
    public Object getCredentials() {
        return userDetails.password
    }

    @Override
    public Object getDetails() {
        return getUser()
    }

    @Override
    public Object getPrincipal() {
        return principal
    }

    @Override
    public boolean isAuthenticated() {
        return authenticated
    }

    @Override
    public void setAuthenticated(boolean authenticated) throws IllegalArgumentException {
        this.authenticated = authenticated
    }

    static class PrincipalProxy implements Principal {

        GrailsUser grailsUser
        Long username

        public PrincipalProxy(GrailsUser grailsUser) {
            this.grailsUser = grailsUser
            this.username = grailsUser.id
        }

        @Override
        public String getName() {
            return grailsUser.id
        }
    }
}

要返回此令牌,只需注册您自己的AuthenticationProvider

package com.selly.util.security;

import grails.plugin.springsecurity.SpringSecurityService

import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException

public class AppUsernamePasswordAuthenticationProvider extends DaoAuthenticationProvider implements AuthenticationProvider {

    SpringSecurityService springSecurityService

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        def token = (UsernamePasswordAuthenticationToken) authentication

        def user = userDetailsService.loadUserByUsername(authentication.principal)

        if(!user)
            throw new UsernameNotFoundException("Cannot find user", authentication.principal)

        if(!passwordEncoder.isPasswordValid(user.password, authentication.credentials, null))
            throw new BadCredentialsException("Invalid password")

        return new AppMetadataAuthenticationToken(user)
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

}


package com.selly.util.security;

import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException

public class AppMetadataAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        // TODO Auto-generated method stub
        return authentication;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // TODO Auto-generated method stub
        return AppMetadataAuthenticationToken.class.isAssignableFrom(authentication);
    }

}

resources.groovy

中注册
appUsernamePasswordAuthenticationProvider(AppUsernamePasswordAuthenticationProvider) {
    userDetailsService = ref('userDetailsService')
    passwordEncoder = ref('passwordEncoder')
    userCache = ref('userCache')
    saltSource = ref('saltSource')
    preAuthenticationChecks = ref('preAuthenticationChecks')
    postAuthenticationChecks = ref('postAuthenticationChecks')
    springSecurityService = ref('springSecurityService')
}

Config.groovy

grails.plugin.springsecurity.providerNames = [
    'appMetadataAuthenticationProvider',
    'appUsernamePasswordAuthenticationProvider',
//  'daoAuthenticationProvider',
//  'anonymousAuthenticationProvider',
//  'rememberMeAuthenticationProvider'
]

现在一切都很完美:

Hibernate: select this_.id as id13_0_, this_.account_expired as account2_13_0_, this_.account_locked as account3_13_0_, this_.enabled as enabled13_0_, this_."password" as password5_13_0_, this_.password_expired as password6_13_0_, this_.username as username13_0_, this_.workspace as workspace13_0_ from users this_ where (**this_.id=?**) limit ?

除了使用getCurrentUser()之外,您还可以使用getPrincipal()投射到之前填充的对象,其数据多于Principal界面提供的数据。

相关问题