【问题标题】:Grails Spring Security Rest Basic ConfigurationGrails Spring Security Rest 基本配置
【发布时间】:2015-03-29 13:01:43
【问题描述】:

我正在 Grails 中创建需要同时支持移动应用程序和 Web 应用程序的后端。 我设法使用compile ':spring-security-core:2.0-RC4' 进行身份验证。它工作正常。

现在我想让移动应用程序调用 api 变得安静。所以我在 BuildConfig.groovy 中添加了以下内容。

compile ":spring-security-rest:1.4.1", {
    excludes: 'spring-security-core'
}

我正在按照本教程使用spring-security-resthttp://alvarosanchez.github.io/grails-spring-security-rest/docs/guide/tokenStorage.html

在我的应用程序中,我有扩展 SecUser 的用户域。

这是我的 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
]

我不确定下一步该做什么。我怎样才能调用api?例如,我有客户域。如何使用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 }
        }
    }
}

【问题讨论】:

    标签: grails spring-security


    【解决方案1】:

    您可以登录并获得令牌吗?

    我正在使用 Grails 3.2.0.M1 和 application.groovy 中的以下设置
    我设置了一个测试应用程序并使用 GORM 来存储令牌。

    我首先需要安装 gorm 插件,否则令牌不会被存储。

    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
    

    我使用 curl 测试登录,看看是否返回了令牌:

    curl -v -H "Content-Type: application/json" -X POST -d '{"username":"me","password":"password"}' http://localhost:8090/api/login

    返回:

    * 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"}
    

    access_tokenek6bonq0jnvn40pbgb4qamptorfrdotb

    安全控制器测试操作:

    class TestController {
    
        def index() { 
            def l = SysRole.list()
    
            render l as JSON
        }
    }
    

    然后我使用 curl 和列出用户角色的令牌测试了这个安全控制器:

    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"}]
    

    使用无效令牌进行测试会导致 401 未授权:

    * 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
    

    【讨论】:

      猜你喜欢
      • 2011-10-08
      • 2017-02-16
      • 2016-05-05
      • 2019-05-09
      • 2015-05-04
      • 2014-11-02
      • 2018-02-16
      • 2015-09-19
      • 1970-01-01
      相关资源
      最近更新 更多