I am creating the backend in Grails that needs to support both mobile app & web app.
I have managed to use compile ':spring-security-core:2.0-RC4'
for the authentication. It works fine.
Now I want to make it restful for the mobile app to call the api. So I added the following in the BuildConfig.groovy.
compile ":spring-security-rest:1.4.1", {
excludes: 'spring-security-core'
}
I am following this tutorial to use spring-security-rest
.
http://alvarosanchez.github.io/grails-spring-security-rest/docs/guide/tokenStorage.html
In my app, I have User domain which extends SecUser.
Here is my Config.grrovy.
// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'm15.authentication.SecUser'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'm15.authentication.SecUserSecRole'
grails.plugin.springsecurity.authority.className = 'm15.authentication.SecRole'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
'/': ['permitAll'],
'/index': ['permitAll'],
'/index.gsp': ['permitAll'],
'/assets/**': ['permitAll'],
'/**/js/**': ['permitAll'],
'/**/css/**': ['permitAll'],
'/**/images/**': ['permitAll'],
'/**/favicon.ico': ['permitAll']
]
grails.plugin.springsecurity.rest.login.active = true
grails.plugin.springsecurity.rest.login.endpointUrl = '/api/login'
grails.plugin.springsecurity.rest.login.failureStatusCode = '401'
grails.plugin.springsecurity.rest.login.useJsonCredentials = true
grails.plugin.springsecurity.rest.login.usernamePropertyName = 'username'
grails.plugin.springsecurity.rest.login.passwordPropertyName = 'password'
grails.plugin.springsecurity.rest.logout.endpointUrl = '/api/logout'
grails.plugin.springsecurity.rest.token.generation.useSecureRandom = true
grails.plugin.springsecurity.rest.token.generation.useUUID = false
grails.plugin.springsecurity.rest.token.storage.useGorm = false
grails.plugin.springsecurity.rest.token.storage.gorm.tokenDomainClassName = null
grails.plugin.springsecurity.rest.token.storage.gorm.tokenValuePropertyName = 'tokenValue'
grails.plugin.springsecurity.rest.token.storage.gorm.usernamePropertyName = 'username'
grails.plugin.springsecurity.rest.token.rendering.usernamePropertyName = 'username'
grails.plugin.springsecurity.rest.token.rendering.authoritiesPropertyName = 'roles'
grails.plugin.springsecurity.rest.token.validation.active = true
grails.plugin.springsecurity.rest.token.validation.headerName = 'X-Auth-Token'
grails.plugin.springsecurity.rest.token.validation.endpointUrl = '/api/validate'
//Exclude normal controllers from basic auth filter. Just the JSON API is included
grails.plugin.springsecurity.filterChain.chainMap = [
'/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // Stateless chain
'/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter' // Traditional chain
]
I'm not sure what to do next. How can I to call the api? For example, I have Client domain. How to get the list of all clients with the rest api?
Client.groovy
class Client {
Long id
String name
String toString(){
"${name}"
}
static hasMany = [users: User, apps: App]
static constraints = {
name blank: false
}
}
ClientController.groovy
package m15
import static org.springframework.http.HttpStatus.*
import org.springframework.security.access.annotation.Secured
import grails.transaction.Transactional
@Secured(['IS_AUTHENTICATED_REMEMBERED'])
@Transactional(readOnly = true)
class ClientController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Client.list(params), model:[clientInstanceCount: Client.count()]
}
def show(Client clientInstance) {
respond clientInstance
}
def create() {
respond new Client(params)
}
@Transactional
def save(Client clientInstance) {
if (clientInstance == null) {
notFound()
return
}
if (clientInstance.hasErrors()) {
respond clientInstance.errors, view:'create'
return
}
clientInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'client.label', default: 'Client'), clientInstance.id])
redirect clientInstance
}
'*' { respond clientInstance, [status: CREATED] }
}
}
def edit(Client clientInstance) {
respond clientInstance
}
@Transactional
def update(Client clientInstance) {
if (clientInstance == null) {
notFound()
return
}
if (clientInstance.hasErrors()) {
respond clientInstance.errors, view:'edit'
return
}
clientInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'Client.label', default: 'Client'), clientInstance.id])
redirect clientInstance
}
'*'{ respond clientInstance, [status: OK] }
}
}
@Transactional
def delete(Client clientInstance) {
if (clientInstance == null) {
notFound()
return
}
clientInstance.delete flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'Client.label', default: 'Client'), clientInstance.id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'client.label', default: 'Client'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}
Are you able to login and get a token?
I am using Grails 3.2.0.M1 and the following settings in application.groovy
I setup a test app and use GORM to store tokens.
I first needed to install the gorm plugin otherwise the tokens would not get stored.
compile "org.grails.plugins:spring-security-rest-gorm:2.0.0.M2"
application.groovy:
grails.plugin.springsecurity.rest.login.active=true
grails.plugin.springsecurity.rest.login.endpointUrl='/api/login'
grails.plugin.springsecurity.rest.login.failureStatusCode=401
grails.plugin.springsecurity.rest.login.useJsonCredentials=true
grails.plugin.springsecurity.rest.login.usernamePropertyName='username'
grails.plugin.springsecurity.rest.login.passwordPropertyName='password'
grails.plugin.springsecurity.rest.logout.endpointUrl='/api/logout'
grails.plugin.springsecurity.rest.token.storage.useGorm=true
grails.plugin.springsecurity.rest.token.storage.gorm.tokenDomainClassName='dashboard.AuthToken'
grails.plugin.springsecurity.rest.token.storage.gorm.tokenValuePropertyName='tokenValue'
grails.plugin.springsecurity.rest.token.storage.gorm.usernamePropertyName='username'
grails.plugin.springsecurity.rest.token.generation.useSecureRandom=true
grails.plugin.springsecurity.rest.token.generation.useUUID=false
grails.plugin.springsecurity.rest.token.validation.active=true
grails.plugin.springsecurity.rest.token.validation.endpointUrl='/api/validate'
grails.plugin.springsecurity.rest.token.storage.jwt.expiration=99999
I used curl to test the login and see if a token was returned:
curl -v -H "Content-Type: application/json" -X POST -d '{"username":"me","password":"password"}' http://localhost:8090/api/login
This returned:
* Hostname was NOT found in DNS cache
* Trying ::1...
* Connected to localhost (::1) port 8090 (#0)
> POST /api/login HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8090
> Accept: */*
> Content-Type: application/json
> Content-Length: 39
>
* upload completely sent off: 39 out of 39 bytes
< HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Cache-Control: no-store
< Pragma: no-cache
< Content-Type: application/json;charset=UTF-8
< Content-Length: 112
< Date: Wed, 01 Jun 2016 09:34:00 GMT
<
* Connection #0 to host localhost left intact
{"username":"me","roles":["ROLE_ADMIN"],"token_type":"Bearer","access_token":"ek6bonq0jnvn40pbgb4qamptorfrdotb"}
The access_token
is ek6bonq0jnvn40pbgb4qamptorfrdotb
Secured controller test action:
class TestController {
def index() {
def l = SysRole.list()
render l as JSON
}
}
Then I tested this secured controller using curl and the token which lists the user roles:
curl -v -X POST http://localhost:8090/test -H "Authorization: Bearer ek6bonq0jnvn40pbgb4qamptorfrdotb"
* Connected to localhost (::1) port 8090 (#0)
> POST /test HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8090
> Accept: */*
> Authorization: Bearer ek6bonq0jnvn40pbgb4qamptorfrdotb
>
< HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< X-Application-Context: application:development:8090
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Wed, 01 Jun 2016 09:39:52 GMT
<
* Connection #0 to host localhost left intact
[{"id":1,"authority":"ROLE_ADMIN"},{"id":2,"authority":"ROLE_USER"}]
Testing with an invalid token gives a 401 unauthorized:
* Connected to localhost (::1) port 8090 (#0)
> POST /test HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:8090
> Accept: */*
> Authorization: Bearer invalid_token
>
< HTTP/1.1 401 Unauthorized
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< WWW-Authenticate: Bearer error="invalid_token"
< Content-Length: 0
< Date: Wed, 01 Jun 2016 09:43:08 GMT
<
* Connection #0 to host localhost left intact
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With