Reputation: 11
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.
Upvotes: 0
Views: 704
Reputation: 11
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,
Upvotes: 1