在 Apache Shiro Grails 中使用 passwordHash 登录

Login with passwordHash in Apache Shiro Grails

我在 grails 中使用 Apache Shiro 插件来处理用户身份验证。

我想在系统的管理部分添加一项功能,我们的管理员可以在其中为特定用户登录。

我想避免为管理员创建一个新的复杂界面。

当我不知道他的密码,但只知道他的密码哈希时,如何以用户身份登录?

这似乎是一个带有大量安全问题的可怕想法。相反,您应该考虑允许已登录的管理员临时以用户身份登录。

这些可以帮助您以编程方式以用户身份登录Grails - ShiroSecurity - manually login user and Grails: ShiroSecurity - log in user without UsernamePasswordToken

我最终解决了它,但我不确定我的解决方案的安全性,所以我希望你对它发表评论。

我在管理控制器中创建了新的操作登录。此控制器只能由管理员访问,其他任何人都无法访问。动作看起来像:

def signIn(String username) {

        def authToken = new UsernamePasswordToken(username, params.password as String)

        // Specific setting for administrator signIn
        //
        params.rememberMe = false
        params.targetUri = '/'
        authToken.host = 'admin'

        // If a controller redirected to this page, redirect back
        // to it. Otherwise redirect to the root URI.
        def targetUri = params.targetUri ?: "/index/index"

        // Handle requests saved by Shiro filters.
        SavedRequest savedRequest = WebUtils.getSavedRequest(request)
        if (savedRequest) {
            targetUri = savedRequest.requestURI - request.contextPath
            if (savedRequest.queryString) targetUri = targetUri + '?' + savedRequest.queryString
        }

        try{
            // Perform the actual login. An AuthenticationException
            // will be thrown if the username is unrecognised or the
            // password is incorrect.
            SecurityUtils.subject
            SecurityUtils.subject.login(authToken)

            log.info "Redirecting to ${targetUri}."
            redirect(uri: targetUri)
        }
        catch (AuthenticationException ex){
            // Authentication failed, so display the appropriate message
            // on the login page.
            log.info "Authentication failure for user ${params.username}."
            flash.message = message(code: "login.failed")

            // Keep the username and "remember me" setting so that the
            // user doesn't have to enter them again.
            def m = [ username: params.username ]

            // Remember the target URI too.
            if (params.targetUri) {
                m["targetUri"] = params.targetUri
            }

            // Now redirect back to the login page.
            redirect(action: "login", params: m)
        }
    }

而且我还稍微修改了我的 DbShiroRealm。

def authenticate(authToken) {


    log.info "Attempting to authenticate ${authToken.username} in DB realm..."
    def username = authToken.username

    // Null username is invalid
    if (username == null) {
        throw new AccountException("Null usernames are not allowed by this realm.")
    }

    // Get the user with the given username. If the user is not
    // found, then they don't have an account and we throw an
    // exception.
    def user = User.findByUsername(username)
    if (!user) {
        throw new UnknownAccountException("No account found for user [${username}]")
    }

    log.info "Found user ${user.username} in DB"

    // Now check the user's password against the hashed value stored
    // in the database.
    def account = new SimpleAccount(username, user.password, "ShiroDbRealm")

    if( !(authToken.host == 'admin')) { // skips password authentication
        if (!credentialMatcher.doCredentialsMatch(authToken, account)) {
            log.info "Invalid password (DB realm)"
            throw new IncorrectCredentialsException("Invalid password for user ${username}")
        }
    } 

    return account
}

我使用了 UsernamePasswordToken 中的主机字段来携带正在登录的信息。

覆盖 UsernamePasswordToken 并添加额外的布尔字段以跳过密码验证会更合适。