Grails,Spring 安全 Rest 插件,无法在 null object 上调用方法 loadUserByToken()

Grails, Spring Security Rest Plugin, Cannot invoke method loadUserByToken() on null object

我正在尝试在 grails 框架 (2.5.0) 中使用 Spring 安全插件 (2.0.0) 和 Spring 身份验证 Rest 插件 (1.5.3) 实施基于令牌的身份验证。我将 header 字段 "x-auth-token" 设置为令牌,将 post 设置为目标控制器 URL。但是,IDE (Intellij IDEA) 弹出这个错误信息

| Error 2016-07-12 15:58:27,864 [http-bio-8080-exec-10] ERROR [/hello_world].
[default]  - Servlet.service() for servlet [default] in context with path [/hello_world] threw exception
Message: Cannot invoke method loadUserByToken() on null object
Line | Method
->>   55 | authenticate in grails.plugin.springsecurity.rest.RestAuthenticationProvider
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|     75 | doFilter     in grails.plugin.springsecurity.rest.RestTokenValidationFilter
|     53 | doFilter . . in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
|    143 | doFilter     in grails.plugin.springsecurity.rest.RestAuthenticationFilter
|     62 | doFilter . . in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
|     80 | doFilter     in grails.plugin.springsecurity.rest.RestLogoutFilter
|     59 | doFilter . . in grails.plugin.springsecurity.web.SecurityRequestHolderFilter
|     82 | doFilter     in com.brandseye.cors.CorsFilter
|   1142 | runWorker .  in java.util.concurrent.ThreadPoolExecutor
|    617 | run          in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run . . . .  in java.lang.Thread

我检查了这个 loadUserByToken() 方法,它是在 tokenStorageService 上调用的。我不知道为什么这个 tokenStorageService 是 null object。 Spring 安全和 Spring 安全插件配置如下:

Config.groovy

// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'hello_world.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'hello_world.UserRole'
grails.plugin.springsecurity.authority.className = 'hello_world.Role'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
    '/':                ['permitAll'],
    '/index':           ['permitAll'],
    '/index.gsp':       ['permitAll'],
    '/assets/**':       ['permitAll'],
    '/**/js/**':        ['permitAll'],
    '/**/css/**':       ['permitAll'],
    '/**/images/**':    ['permitAll'],
    '/**/favicon.ico':  ['permitAll'],
    '/api/login':       ['permitAll']
]

grails {
    plugin {
        springsecurity {

            filterChain.chainMap = [
                    '/api/guest/**': 'anonymousAuthenticationFilter,restTokenValidationFilter,restExceptionTranslationFilter,filterInvocationInterceptor',
                    '/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter',  // Stateless chain
                    '/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter'                                                                          // Traditional chain
            ]

            providerNames = ['restAuthenticationProvider','daoAuthenticationProvider', 'rememberMeAuthenticationProvider']

            auth.loginFormUrl = '/login/auth'

            useSecurityEventListener = true

            onAuthenticationSuccessEvent = { e, appCtx ->
                // handle AuthenticationSuccessEvent

                System.out.println("Authentication Succeeded");
            }

            onAuthenticationSwitchUserEvent = { e, appCtx ->
                // handle AuthenticationSwitchUserEvent
            }

            onAuthorizationEvent = { e, appCtx ->
                // handle AuthorizationEvent
            }

            onRestTokenCreationEvent = { e, appCtx ->

                System.out.println("Token Created")
            }

            apf {
                filterProcessesUrl = '/api/login'
                allowSessionCreation = false
//                usernamePropertyName = 'username'
//                passwordPropertyName = 'password'
            }

            rest {

                active = true

                login {
                    active = true
                    endpointUrl = '/api/login'
                    failureStatusCode = 401
                    useJsonCredentials = true
                    usernamePropertyName = 'username'
                    passwordPropertyName = 'password'
                }

                token {

                    validation {
                        active = true
                        endpointUrl = '/api/validate'
                        headerName = 'x-auth-token'
                        useBearerToken = false
                        tokenPropertyName = 'access_token'
                        enableAnonymousAccess = true
                    }

                    generation {
                        active = true
                        useSecureRandom = true
                        useUUID = false
                    }

                    rendering {
                        usernamePropertyName = 'username'
                        authoritiesPropertyName = 'roles'
                        tokenPropertyName = 'token'
                    }

                    storage {
                        active = true
                        useGorm = true

                        gorm {
                            tokenDomainClassName = 'hello_world.AuthenticationToken'
                            tokenValuePropertyName = 'tokenValue'
                            usernamePropertyName = 'username'
                        }
                    }
                }
            }
        }
    }
}

resources.groovy

import grails.plugin.springsecurity.rest.RestAuthenticationProvider
beans = {
    restAuthenticationProvider(RestAuthenticationProvider);
}

而且我检查了数据库,令牌存储在 authentication_token table 中。我是 grails 的新手,一直在搜索几个小时,一点线索也没有。谁能帮我?非常感谢。

如果您还需要什么,请告诉我。

遇到同样问题的人,经过多次尝试,我终于弄明白了。看来我不应该在 resources.groovy 中声明 restAuthenticationProvider,也不应该在 config.groovy 中将 restAuthenticationProvider 添加到 grails.plugin.springsecurity.providerNames。 spring-security-core和spring-security-rest的完整配置如下:

config.groovy

grails {
    plugin {
        springsecurity {

            useSecurityEventListener = true

            filterChain {
                chainMap = [
                        '/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter',  // Stateless chain
                        '/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter'                                                                          // Traditional chain
                ]
            } //filterChain

            apf {
                filterProcessesUrl = '/api/login'
            } //apf

            rest {

                login {
                    active = true
                    useRequestParamsCredentials = false
                    useJsonCredentials = true
                    usernamePropertyName = 'j_username'
                    passwordPropertyName = 'j_password'
                    endpointUrl = '/api/login'
                } //login

                logout {

                } //logout

                token {

                    validation {
                        active = true
                        endpointUrl = '/api/validate'
                        useBearerToken = false
                        headername = 'X-Auth-Token'
                    } //validation

                    generation {
//                        active = true
//                        useSecureRandom = true;
//                        useUUID = false;
                    }

                    rendering {
                        usernamePropertyName = 'username'
                        authoritiesPropertyName = 'roles'
                        tokenPropertyName = 'token'
                    }

                    storage {
//                        useJWT = true;
                    } //storage
                } //token
            } //rest

            cors.headers = ['Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Auth-Token']
        } //springsecurity
    } //plugin
} //grails

您应该发送 json 格式的用户名和密码,字段 "j_username" 和 "j_password" 并且它将 return 一个 json 格式的令牌。然后,将请求连同头字段 "X-Auth-Token" 中的此令牌一起发送到要查询的 api 。

初始化spring-security-core插件,请参考http://grails-plugins.github.io/grails-spring-security-core/v2/guide/single.html#tutorials

github 中提供了我的完整代码:https://github.com/xixinhe/api_token_authentication

在运行我的代码之前,请先安装oracle mysql 5.

如果我写的有什么违反堆栈溢出规则的,请告诉我,我会改的。

我不是英语母语,请原谅我蹩脚的英语。

谢谢,