I am trying implement token based authentication using Spring Security Plugin (2.0.0) and Spring Authentication Rest Plugin(1.5.3) in grails framework(2.5.0). I set header field "x-auth-token" to token and post to target controller URL. However, IDE (Intellij IDEA) pop out this error message
| 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
I checked this loadUserByToken() method and it is invoked on tokenStorageService. I have no idea why this tokenStorageService is null object. The Spring Security and Spring Security Plugin are configured as follows:
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);
}
and I have checked database, the token is stored in authentication_token table. I am new to grails and have been searching hours and no clue at all. Can anyone help me? much appreciated.
If you need anything else, please let me know.
For someone who has the same issue, after a number of tryings, I finally figured this out. It seems that I shouldn't declare restAuthenticationProvider in resources.groovy and shouldn't add restAuthenticationProvider to grails.plugin.springsecurity.providerNames in config.groovy. The complete configs for spring-security-core and spring-security-rest are listed as follows:
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
you should send username and password in json format with field "j_username" and "j_password" and it will return a token in json format. Then, send request together with this token in the header field "X-Auth-Token" to the api you want to query.
For initializing the spring-security-core plugin, please consult http://grails-plugins.github.io/grails-spring-security-core/v2/guide/single.html#tutorials
a complete of my code is available in github: https://github.com/xixinhe/api_token_authentication
Before running my code, please install oracle mysql 5.
If there is anything I wrote violating the stack overflow rules, please let me know, I will change.
I am not a native English speaker, please forgive me crappy English.
Thanks,