测试安全的365bet地区应用程序

365bet地区在本指南中,您将看到如何测试使用365bet地区 Spring Security REST插件和365bet地区 Spring Security Core插件添加的安全约束。

s塞尔吉奥·德尔阿莫

365bet地区版本 4.0.1

365bet地区培训

365bet地区培训由创建并积极维护365bet地区框架的人们开发和交付

入门

在本指南中,您将

  • 365bet地区创建使用365bet地区 Spring Security Rest插件保护的REST API端点的功能测试

  • 创建一个GebSpec365bet地区使用365bet地区 Spring Security Core插件保护的页面的功能测试尝试访问此类页面时,您将重定向到登录表单

您将需要什么

要完成本指南,您将需要以下内容

  • 花些时间在你手上

  • 体面的文本编辑器或IDE

  • 安装了JDK或更高版本JAVA首页适当配置

如何完成指南

要开始,请执行以下操作

要么

365bet地区指南存储库包含两个文件夹

  • 初始初始项目通常是一个简单的365bet地区应用程序,其中包含一些其他代码,可以帮助您快速入门

  • 完成一个完整的示例它是按照指南中介绍的步骤进行操作并将这些更改应用于文档的结果。初始

要完成指南,请转到初始

  • 光盘进入grails指南grails测试安全性初始

并按照下一节中的说明进行操作

您可以直接前往完成的例子如果你光盘进入grails指南grails测试安全性已完成

编写申请

我们将要编写一个结合了传统Web应用程序和API的应用程序,API端点将带有前缀

域类

我们将创建一个域类, 公告我们将通过本指南作为示例

grailsw创建域类公告创建grails应用程序域示例grails公告groovy创建src test groovy示例grails AnnouncementSpec groovy
grails应用程序域示例grails公告groovy
例子 常规转换CompileStatic

静态编译
 公告 {

    信息静态的约束

网络控制器

我们将使用静态脚手架为网络应用生成控制器

grailsw生成所有Announcement Rendered模板控制器groovy到目标grails应用程序控制器示例grails AnnouncementController groovy呈现的模板Spec groovy到目标src test groovy示例grails AnnouncementControllerSpec groovy grails应用程序域的脚手架已完成grails公告模板到groovy Rendered模板视图编辑gsp渲染模板创建gsp到目标grails应用程序视图公告创建gsp渲染模板索引gsp到目标grails应用程序视图公告索引gsp渲染模板显示gsp到目标grails应用程序视图公告显示gsp为grails应用程序域示例生成的视图grails公告groovy

api控制器

要创建公告API控制器,我们将使用创建控制器命令

grailsw创建控制器ApiAnnouncement创建grails应用程序控制器示例grails ApiAnnouncementController groovy创建src测试groovy示例grails ApiAnnouncementControllerSpec groovy

我们希望该控制器处理对api公告我们需要将以下行添加到我们的网址映射.

grails应用程序控制器示例grails UrlMappings groovy
'api公告'(控制者: 'api公告')

控制器将是RestfulController而且只会以JSON回应

grails应用程序控制器示例grails ApiAnnouncementController groovy
例子 365bet地区 Rest RestfulController
 常规转换CompileStatic

静态编译
 ApiAnnouncementController 延伸RestfulController静态的responseFormats'json'ApiAnnouncementController公告

保护应用程式

添加对365bet地区365bet地区 Spring Security Rest插件以及最新版本的春季安全核心给我们建立gradle文件

建立gradle
编译'org grails插件Spring Security Rest RC'编译'org grails插件春季安全核心RC'

我们将运行快速入门由命令提供Spring Security Core插件产生用户权威

grailsw的快速入门示例grails用户SecurityRole

快速入门脚本生成三个域类用户, 安全角色UserSecurityRole.

grails应用程序域示例grails用户groovy
例子 常规转换EqualsAndHashCode
 常规转换ToString
 grails编译器365bet地区CompileStatic

365bet地区CompileStatic
EqualsAndHashCode包括'用户名')
ToString包括'用户名'includeNamesincludePackage)
 用户 实施 可序列化 {
        私人的 静态的 最后 serialVersionUID1

        用户名密码布尔值已启用
        布尔值accountExpired布尔值帐户被锁定布尔值密码已过期getAuthorities UserSecurityRole findAllByUser这个)  清单securityRole 
        }

        静态的约束密码空白: , 密码: 用户名空白: , 独特: 
        }

        静态的映射密码: '密码'
        }
}
grails应用程序域示例grails SecurityRole groovy
例子 常规转换EqualsAndHashCode
 常规转换ToString
 grails编译器365bet地区CompileStatic

365bet地区CompileStatic
EqualsAndHashCode包括'权威')
ToString包括'权威'includeNamesincludePackage)
 安全角色 实施 可序列化 {

        私人的 静态的 最后 serialVersionUID1
        权威静态的约束权限空白: , 独特: 
        }

        静态的映射缓存
        }
}
grails应用程序域示例grails UserSecurityRole groovy
例子 365bet地区 Gorm DetachedCriteria
 常规转换ToString

 org codehaus groovy util HashCodeHelper
 grails编译器365bet地区CompileStatic

禁止警告(['FactoryMethodName', '实例'])
365bet地区CompileStatic
ToString快取includeNamesincludePackage)
 UserSecurityRole 实施 可序列化 {

        私人的 静态的 最后 serialVersionUID1用户用户SecurityRole securityRole覆写
        布尔值等于其他如果其他实例UserSecurityRole其他userId用户ID其他securityRoleId securityRole ID覆写
        整型hashCode整型hashCode HashCodeHelper initHash如果用户hashCode HashCodeHelper updateHash hashCode用户ID如果securityRole hashCode HashCodeHelper updateHash hashCode securityRole ID hashCode静态的UserSecurityRole获取用户身份securityRoleId条件对于用户ID安全角色ID获取静态的 布尔值存在用户身份securityRoleId条件用于用户ID安全角色ID计数私人的 静态的DetachedCriteria标准用户身份securityRoleId UserSecurityRole,其中用户用户加载userId securityRole SecurityRole加载securityRoleId静态的UserSecurityRole创建用户用户SecurityRole securityRole布尔值齐平) {
                定义实例UserSecurityRole用户用户securityRolesecurityRole实例保存齐平刷新实例静态的 布尔值删除用户u SecurityRole r如果空值[R空值UserSecurityRole,其中用户u securityRole r deleteAll静态的 整型removeAll用户u u空值 ? 0UserSecurityRole用户u deleteAll 整型
        }

        静态的 整型removeAll SecurityRole r r空值 ? 0UserSecurityRole其中securityRole r deleteAll 整型
        }

        静态的约束安全性验证器SecurityRole r UserSecurityRole ur如果您的用户ID UserSecurityRole withNewSession如果UserSecurityRole存在您的用户ID r ID返回 ['userRole存在']
                                        }
                                }
                        }
                }
        }

        静态的映射ID综合: ['用户', 'securityRole'
        }
}

如果您使用的是GORM或更高版本以及Spring Security core或更高版本,则quickstart会生成并注册一个bean以处理密码编码

src main groovy示例grails UserPasswordEncoderListener groovy
例子 grails插件springsecurity SpringSecurityService
 org grails数据存储区映射核心数据存储区
 组织grails数据存储区映射引擎事件AbstractPersistenceEvent
 组织grails数据存储区映射引擎事件AbstractPersistenceEventListener
 组织grails数据存储区映射引擎事件EventType
 组织grails数据存储区映射引擎事件PreInsertEvent
 组织grails数据存储区映射引擎事件PreUpdateEvent
 org springframework上下文ApplicationEvent
 org springframework bean工厂注释自动装配
 常规转换CompileStatic

禁止警告(['不必要的信', '线长', '实例'])
静态编译
 UserPasswordEncoderListener 延伸AbstractPersistenceEventListener自动接线SpringSecurityService springSecurityService UserPasswordEncoderListener最后数据存储数据存储数据存储覆写
    受保护的 虚空onPersistenceEvent AbstractPersistenceEvent事件如果事件entityObject实例用户用户u事件entityObject用户如果u密码事件eventType EventType PreInsert事件eventType EventType PreUpdate u isDirty'密码'事件getEntityAccess setProperty'密码'encodePassword u密码覆写
    布尔值supportsEventType延伸ApplicationEvent eventType eventType PreUpdateEvent eventType PreInsertEvent私人的 encodePassword密码springSecurityService密码编码器springSecurityService encodingPassword密码密码
grails app conf春季资源groovy
 示例grails UserPasswordEncoderListener
将您的Spring DSL代码放在这里bean userPasswordEncoderListener UserPasswordEncoderListener引用'hibernateDatastore'))
}

我们将在文件中配置应用程序的安全规则应用程序如下所示,我们有一个无状态链和一个传统链.

grails应用配置会议应用程序
grails插件springsecurity rest令牌存储jwt秘密'请更改此秘密为新的一个'securityConfigType"InterceptUrlMap"  (1)filterChain链图图案: '',过滤器: 'JOINED FILTERS匿名身份验证过滤器异常翻译过滤器身份验证处理过滤器安全性上下文持久性过滤器记住我身份验证过滤器'],(2)
                [图案: '/**', 过滤器: '加入过滤器restTokenValidationFilter restExceptionTranslationFilter'] (3)userLookup userDomainClassName'示例grails用户' (4)AuthorityJoinClassName'示例grails UserSecurityRole' (4)权限className'例子grails SecurityRole' (4)InterceptUrlMap图案: '/',                      访问: ['allowAll']],
                    [图案: '错误',                 访问: ['allowAll']],
                    [图案: '指数',                 访问: ['allowAll']],
                    [图案: '索引gsp',             访问: ['allowAll']],
                    [图案: '关掉',              访问: ['allowAll']],
                    [图案: '资产',             访问: ['allowAll']],
                    [图案: 'js',              访问: ['allowAll']],
                    [图案: '的CSS',             访问: ['allowAll']],
                    [图案: '图片',          访问: ['allowAll']],
                    [图案: '网站图标',        访问: ['allowAll']],
                    [图案: '登录',              访问: ['allowAll']], (5)
                    [图案: '登出',                访问: ['allowAll']],
                    [图案: '登出',             访问: ['allowAll']],
                    [图案: '公告',          访问: ['角色老板', '角色员工']],
                    [图案: '公告索引',    访问: ['角色老板', '角色员工']],  (6)
                    [图案: '公告创建',   访问: ['角色老板']],
                    [图案: '公告保存',     访问: ['角色老板']],
                    [图案: '公告更新',   访问: ['角色老板']],
                    [图案: '公告删除', 访问: ['角色老板']],
                    [图案: '公告编辑',   访问: ['角色老板']],
                    [图案: '公告节目',   访问: ['角色老板', '角色员工']],
                    [图案: '消防登录',             访问: ['角色一致']], (7)
                    [图案: 'oauth访问令牌',    访问: ['角色一致']], (8)
                    [图案: 'api公告',     访问: ['角色老板'], httpMethod: '得到'],  (9)
                    [图案: 'api公告',   访问: ['角色老板'], httpMethod: '得到'],
                    [图案: 'api公告',   访问: ['角色老板'], httpMethod: '删除'],
                    [图案: 'api公告',     访问: ['角色老板'], httpMethod: '开机自检'],
                    [图案: 'api公告',   访问: ['角色老板'], httpMethod: '']
            ]
        }
    }
}
1 我们选择使用InterceptUrlMap
2 无状态链
3 传统链
4 s quickstart脚本生成的类
5 登录页面的URL
6 网址仅适用于经过身份验证并具有ROLE BOSS或ROLE EMPLOYEE角色的用户
7 365bet地区Spring Security Rest for 365bet地区默认身份验证端点它应该允许匿名访问
8 365bet地区Spring Security Rest for 365bet地区默认为刷新令牌端点,它应该允许匿名访问
9 网址仅适用于经过身份验证并具有角色BOSS的用户

我们将在其中填充数据库引导带在应用程序启动时插入两个用户

grails应用程序服务示例grails UserService groovy
例子 grails gorm services服务

服务用户接口UserService用户保存用户名密码用户findByUsername用户名
grails应用程序服务示例grails SecurityRoleService groovy
例子 grails gorm services服务

服务安全角色接口SecurityRoleService SecurityRole保存授权SecurityRole findByAuthority权威
grails应用程序服务示例grails UserSecurityRoleService groovy
例子 grails gorm services服务

服务UserSecurityRole接口
grails应用程序初始化示例grails BootStrap常规
例子 grails编译器365bet地区CompileStatic

365bet地区CompileStatic
 引导带AnnouncementServiceannouncementServiceUserServiceuserServiceSecurityRoleServicesecurityRoleServiceUserSecurityRoleServiceuserSecurityRoleService定义初始化ServletContext清单<当局'角色老板', '角色员工'每个权威权威如果securityRoleService findByAuthority权限sec​​urityRoleService保存权限如果userService findByUsername'夏洛克'用户u userService保存'夏洛克', '初级'userSecurityRoleService保存u securityRoleService findByAuthority'角色老板'))
        }

        如果userService findByUsername'沃森'用户u userService保存'沃森', '贝克街'userSecurityRoleService保存u securityRoleService findByAuthority'角色员工'NoticeService保存'巴斯克维尔的猎犬')
    }
    定义破坏

测试休息API

为了测试应用程序的REST部分,我们使用Micronaut的HttpClient

建立gradle
测试编译"io micronaut micronaut http客户端"

Micronaut HTTP客户端使将JSON有效负载绑定到POGO变得容易。创建多个POGO,我们将在测试中使用

src集成测试groovy示例grails BearerToken groovy
例子 com quickxml jackson批注JsonProperty

 不记名令牌 {
    杰森地产('访问令牌')
    accessToken杰森地产('刷新令牌')
    refreshToken清单<角色用户名
src集成测试groovy示例grails CustomError groovy
例子 常规转换CompileStatic

静态编译
 CustomError {
    整数状态错误信息路径
src集成测试groovy示例grails UserCredentials groovy
例子 常规转换CompileStatic

静态编译
 用户凭证 {
    用户名密码

我们的第一个测试将验证api公告只有具有角色的用户才能访问端点角色老板.

src集成测试groovy示例grails ApiAnnouncementControllerSpec groovy
例子 grails测试mixin集成集成
 Micronaut核心类型参数
 io micronaut http HttpRequest
 
 io micronaut http HttpStatus
 io micronaut http客户端HttpClient
 io micronaut http客户端异常HttpClientException
 只是自动清理
 spock lang共享
 spock lang规格
 365bet地区测试Spock一次

禁止警告(['方法名称', 'DuplicateNumberLiteral', '实例'])
积分
 ApiAnnouncementControllerSpec 延伸规格共享
    HttpClient客户端之前 (1)
    虚空初始化客户端HttpClient创建 ("HTTP本地主机$服务器端口")) (2)
    }

    定义 '测试API公告网址已安全'() {
        什么时候'api公告'客户阻止交换请求(3)的论点清单自定义错误的公告视图参数然后抛出HttpClientException e响应状态HttpStatus UNAUTHORIZED(4)

        什么时候可选的jsonError e响应getBody CustomError然后jsonError isPresent jsonError获取状态401jsonError得到错误''jsonError获取消息'无可用讯息'jsonError获取路径'api公告'
    }

    定义 "测试具有ROLE BOSS角色的用户是否可以访问api公告网址"() {
        什么时候: '用夏洛克登录'UserCredentials凭证用户凭证用户名: '夏洛克', 密码: '初级'HttpRequest请求HttpRequest POST'消防登录'证书(5)HttpResponse响应客户端以阻止交换请求BearerToken然后状态码200 == '角色老板' }

        什么时候:
        accessToken分别为主体accessToken然后accessToken什么时候清单rsp客户端到阻止交换HttpRequest GET'api公告'标头'授权书', "承载者${accessToken}"的论点清单公告查看(6)

        然后rsp状态码200 (7)rsp身体空值
        ((清单rsp身材1
        ((清单rsp身体得到0) 实例公告视图公告视图清单rsp身体得到0'巴斯克维尔的猎犬'
    }

    定义 "测试角色为ROLE EMPLOYEE的用户无法访问api公告url"() {
        什么时候: '使用Watson登录'UserCredentials凭证用户凭证用户名: '沃森', 密码: '贝克街'HttpRequest请求HttpRequest POST'消防登录'信誉HttpResponse响应客户端以阻止交换请求BearerToken然后状态码200身体角色查找 == '角色老板'身体角色查找 == '角色员工' }

        什么时候:
        accessToken分别为主体accessToken然后accessToken什么时候响应客户端到阻塞交换HttpRequest GET'api公告'标头'授权书', "承载者${accessToken}"))

        然后:
        定义(8)
    }
}
1 365bet地区测试Spock一次批注是一种实现相同行为的简捷方式,可以通过应用运行一次之前夹具方法的注释
2 服务器端口属性会自动注入,它包含应用程序将在其中运行以进行功能测试的随机端口
3
4 服务器返回一个指示资源安全的指示
5 呼叫认证端点
6 我们通过访问令牌我们从请求标头中的身份验证端点获得
7 具有角色的经过身份验证的用户角色老板可以访问资源
8 经过身份验证的用户,但没有角色角色老板不允许访问资源

测试Web端点

现在,我们将使用Geb框架.

为了使该测试更易于阅读和维护,我们创建了两个Geb页面登录页面公告清单页面.

src集成测试groovy示例grails LoginPage groovy
例子 给页面

 登录页面 延伸静态的网址'登录验证'

    静态的在标题'登录'
    }

    静态的内容登录按钮$('', 0usernameInputField$('用户名', 0passwordInputField$('密码', 0) }
    }

    虚空登录用户名密码用户名输入字段用户名密码输入字段密码登录按钮单击
src集成测试groovy示例grails AnnouncementListingPage groovy
例子 给页面

 公告清单页面 延伸静态的网址'公告索引'

    静态的$('清单公告'文字包含'公告清单'
    }
}
src集成测试groovy示例grails AnnouncementControllerSpec groovy
例子 geb spock GebSpec
 grails测试mixin集成集成

禁止警告('方法名称')
积分
 公告控制器规格 延伸GebSpec虚空 '测试公告索引受保护,但具有角色ROLE BOSS的用户可以访问'() {
        什么时候: '''公告索引'

        然后: '它被重定向到登录页面'在LoginPage什么时候: '使用ROLE BOSS用户登录'LoginPage页面浏览器页面LoginPage页面登录'夏洛克', '初级')

        然后: '他可以访问公告列表页面'在AnnouncementListingPage虚空 '测试公告索引受到保护,但具有角色ROLE EMPLOYEE的用户可以访问'() {
        什么时候: '''公告索引'

        然后: '它被重定向到登录页面'在LoginPage什么时候: '使用ROLE EMPLOYEE用户登录'LoginPage页面浏览器页面LoginPage页面登录'沃森', '贝克街')

        然后: '他可以访问公告列表页面'在AnnouncementListingPage

运行应用程序

我们已经删除了该项目中的单元测试。我们的项目仅包含先前代码清单中显示的集成和功能测试。

运行测试

grailsw grails测试应用程序grails打开测试报告

您需要365bet地区帮助吗

365bet地区服务:

免费咨询

OCI 365bet地区团队包括365bet地区联合创始人检查我们的并向发展和维护365bet地区的工程师学习

Grails OCI团队