显示导航

Grails 基本身份验证

了解如何使用“基本”HTTP 身份验证方案保护 Grails 应用程序。

作者:Sergio del Amo

Grails 版本 5.0.1

1 入门

RFC7617 定义了“基本”超文本传输协议 (HTTP) 身份验证方案,该方案使用 Base64 编码以用户 ID/密码对的形式传输凭据。

在本指南中,你将创建一个 Grails 应用程序并通过 HTTP 基本身份验证将其保护起来。

1.1 你将需要什么

若要完成本指南,你需要以下内容

  • 一些你的时间

  • 一个好用的文本编辑器或 IDE

  • 安装了 JDK 1.8 或更高版本,并正确配置了 JAVA_HOME

1.2 如何完成指南

若要开始,请执行以下操作

Grails 指南仓库包含两个文件夹

  • initial 初始项目。通常是一个带有额外代码的简单 Grails 应用程序,让你有一个起点。

  • complete 一个完整的示例。它是完成指南介绍的步骤并对 initial 文件夹应用这些更改的结果。

若要完成本指南,请转到 initial 文件夹

  • cdgrails-guides/grails-basicauth/initial

并按照下一部分中的说明进行操作。

如果你 cdgrails-guides/grails-basicauth/complete,你可以直接到已完成示例

如果你想从头开始,请使用 Grails 应用程序 Forge 创建一个新的 Grails 应用程序。

forgeDefault

2 编写应用程序

使用 rest-api 配置文件创建应用程序

grails create-app example --profile=rest-api

2.1 添加安全依赖项

我们需要做的第一件事是将 spring-security-core Grails 插件添加到 build.gradle 中。

build.gradle
compile 'org.grails.plugins:spring-security-core:4.0.0.RC2'

然后,运行命令

grails s2-quickstart example.grails User Role

该命令将生成以下域类

  • grails-app/domain/example.grails.User

  • grails-app/domain/example.grails.Role

  • grails-app/domain/example.grails.UserRole

一个密码编码器监听器

src/main/groovy/example.grails.UserPasswordEncoderListener

以及在以下位置的默认安全配置

  • grails-app/conf/application.groovy

修改生成的 application.groovy 以启用基本身份验证。

grails-app/conf/application.groovy
grails.plugin.springsecurity.useBasicAuth = true

2.2控制器

创建返回经过身份验证的用户的用户名的 `HomeController`。

grails-app/controllers/example/grails/HomeController.groovy
package example.grails

import groovy.transform.CompileStatic
import grails.plugin.springsecurity.SpringSecurityService
import grails.plugin.springsecurity.annotation.Secured
import grails.plugin.springsecurity.userdetails.GrailsUser

@CompileStatic
@Secured('isAuthenticated()')
class HomeController {

    static responseFormats = ['json']

    SpringSecurityService springSecurityService

    def index() {
        [name: loggedUsername()]
    }

    String loggedUsername() {
        if ( springSecurityService.principal == null ) {
            return null
        }
        if ( springSecurityService.principal instanceof String ) {
            return springSecurityService.principal
        }
        if ( springSecurityService.principal instanceof GrailsUser ) {
            return ((GrailsUser) springSecurityService.principal).username
        }
        null
    }

}

2.3 视图

借助 JSON 视图将控制器输出呈现为 JSON Payload。

grails-app/views/home/index.gson
model {
    String name
}

json {
    username name
}

2.4 GORM 数据服务

GORM 数据服务通过使用 GORM 逻辑自动实现抽象类或接口的能力,免除了实施服务层逻辑的工作。

创建一个 GORM 数据服务以简化 User 域类的 CRUD 操作。

grails-app/services/example/grails/UserDataService.groovy
package example.grails

import grails.gorm.services.Service

@Service(User)
interface UserDataService {

    User save(String username, String password, boolean enabled, boolean accountExpired, boolean accountLocked, boolean passwordExpired)

    void delete(Serializable id)

    int count()
}

2.5 测试

创建一个通过基本身份验证验证用户身份验证流程的测试。

src/integration-test/groovy/example/grails/HomeControllerSpec.groovy
package example.grails

import grails.testing.mixin.integration.Integration
import grails.testing.spock.OnceBefore
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.client.HttpClient
import spock.lang.Specification

@Integration
class HomeControllerSpec extends Specification {

    UserDataService userDataService
    HttpClient client

    @OnceBefore
    void init() {
        String baseUrl = "http://localhost:$serverPort"
        this.client  = HttpClient.create(baseUrl.toURL())
    }

    def "verify basic auth is possible"() {
        given:
        final String username = 'sherlock'
        final String password = 'elementary'

        when:
        User user = userDataService.save(username, password, true, false, false, false, )

        then:
        userDataService.count() == old(userDataService.count()) + 1

        when:
        HttpRequest request = HttpRequest.GET("/home")
                .basicAuth(username, password)
        HttpResponse<Map> rsp = client.toBlocking().exchange(request, Map)

        then:
        rsp.status == HttpStatus.OK

        and:
        rsp.body().username == username

        cleanup:
        userDataService?.delete(user?.id)
    }
}

要运行测试,请执行 ./gradlew test

2.6 后续步骤

阅读 Grails Springs Security Core 插件文档的 基本和摘要身份验证 部分,了解基本身份验证的可用不同配置选项。

3 Grails 是否需要帮助?

Object Computing, Inc. (OCI) 赞助编写了本指南。提供各种咨询和支持服务。

OCI 是 Grails 的发源地

结识团队