【问题标题】:Grails Spring Security get user by idGrails Spring Security 通过 id 获取用户
【发布时间】:2013-11-29 23:56:26
【问题描述】:

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

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

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

我知道有可能覆盖UserDetailsService 方法loadUserByUsername(String username),但是此方法用于在会话期间和登录表单中刷新用户的凭据,实际上我想通过用户名对用户进行身份验证.

我有三个问题:

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

【问题讨论】:

  • @Gregg,不言而喻,Int PK 会更快。不是吗?

标签: grails spring-security


【解决方案1】:

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

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
        }
    }
}

要返回此 Token,只需注册您自己的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 接口提供的更多数据强制转换为之前填充的对象。

【讨论】:

    猜你喜欢
    • 2015-12-06
    • 2014-06-10
    • 2017-03-20
    • 2011-10-02
    • 2020-12-09
    • 2015-06-02
    • 2017-10-09
    • 2016-07-22
    • 2014-07-29
    相关资源
    最近更新 更多