1、创建项目
grails create-app myapp --profile=rest-api cd myapp grails--profile可以指定项目框架的类型,rest-api增加rest相关jar,去掉了gsp相关的jar。
2、创建domain
create-domain-resource com.rest.book
3、import项目到Eclipse
import方法参见上一篇博文。
编辑domain class
package org.demo import grails.rest.* @Resource() class Book { String title }
domain的写法可以参考GORM
4、创建controller
create-restful-controller com.rest.book (generate-all com.rest.book)create-restful-controller命令创建一个最简单的controller,但是功能是全的。
generate-all命令创建一个包含所有代码的controller,功能与上一个命令创建的相同,但是可以修改代码,便于自行修改。
编辑controller
package org.demo import grails.rest.* import grails.converters.* class BookController extends RestfulController { static responseFormats = ['json', 'xml', 'hal'] BookController() { super(Book) } }主要是添加了format,'hal'
5、启动前的设置
UrlMappings
// "/$controller/$action?/$id?(.$format)?"{ // constraints { // // apply constraints here // } // } "/books"(resources:"book")注掉/$controller/$action?/$id?(.$format)?,原因是这个设置会暴露所有的controller,在实际项目中不太安全,但是测试时还是很好用。
添加"/books"(resources:"book")
添加一些数据
grails-app/init/BootStrap.groovy
import org.demo.Book class BootStrap { def init = { servletContext -> new Book(title :"The Stand" ).save() new Book(title :"The Shining" ).save() } def destroy = { } }
6、启动、测试
执行gradle Task
build
bootRun
如果不明白可以参考上一篇博文
使用postman请求,当然也可以用Linux的curl命令
postman主页https://www.getpostman.com/
GET http://localhost:8080/books
得到返回
[ { "id": 1, "title": "The Stand" }, { "id": 2, "title": "The Shining" } ]
请求 GET http://localhost:8080/books.xml
得到返回
<?xml version="1.0" encoding="UTF-8"?> <list> <book id="1"> <title>The Stand</title> </book> <book id="2"> <title>The Shining</title> </book> </list>
grails3可以根据后缀返回对应的格式,默认是json。
7、添加HAL
添加hal渲染
grails-app/conf/resources.groovy
import grails.rest.render.hal.* // Place your Spring DSL code here beans = { halBookRenderer(HalJsonRenderer, org.demo.Book) halBookCollectionRenderer(HalJsonCollectionRenderer, org.demo.Book) }
重新启动项目
请求 GET http://localhost:8080/books.hal
得到返回
{ "_links": { "self": { "href": "http://localhost:8080/books.hal", "hreflang": "zh", "type": "application/hal+json" } }, "_embedded": { "book": [ { "_links": { "self": { "href": "http://localhost:8080/books/1", "hreflang": "zh", "type": "application/hal+json" } }, "title": "The Stand", "version": 0 }, { "_links": { "self": { "href": "http://localhost:8080/books/2", "hreflang": "zh", "type": "application/hal+json" } }, "title": "The Shining", "version": 0 } ] } }
8、安全认证
使用插件 grails spring security rest
8.1、安装
build.gradle
dependencies { //Other dependencies .... compile "org.grails.plugins:spring-security-rest:2.0.0.M2" }
修改dependencies后,需要刷一下Eclipse的项目,否则在eclipse里会提示编译错误。
刷新方法,右键点击项目->Gradle->Refresh Gradle Project
8.2、添加权限相关Domain类
在grails-app/domain目录下增加Role、User、UserRole。例子如下,也可以根据具体情况自行修改。
org.demo.Role.groovy
package org.demo import groovy.transform.EqualsAndHashCode import groovy.transform.ToString @EqualsAndHashCode(includes='authority') @ToString(includes='authority', includeNames=true, includePackage=false) class Role implements Serializable { private static final long serialVersionUID = 1 String authority Role(String authority) { this() this.authority = authority } static constraints = { authority blank: false, unique: true } static mapping = { cache true } }
org.demo.User.groovy
package org.demo import groovy.transform.EqualsAndHashCode import groovy.transform.ToString @EqualsAndHashCode(includes='username') @ToString(includes='username', includeNames=true, includePackage=false) class User implements Serializable { private static final long serialVersionUID = 1 transient springSecurityService String username String password boolean enabled = true boolean accountExpired boolean accountLocked boolean passwordExpired User(String username, String password) { this() this.username = username this.password = password } Set<Role> getAuthorities() { UserRole.findAllByUser(this)*.role } def beforeInsert() { encodePassword() } def beforeUpdate() { if (isDirty('password')) { encodePassword() } } protected void encodePassword() { password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password } static transients = ['springSecurityService'] static constraints = { username blank: false, unique: true password blank: false } static mapping = { password column: '`password`' } }
org.demo.UserRole.groovy
package org.demo import grails.gorm.DetachedCriteria import groovy.transform.ToString import org.apache.commons.lang.builder.HashCodeBuilder @ToString(cache=true, includeNames=true, includePackage=false) class UserRole implements Serializable { private static final long serialVersionUID = 1 User user Role role UserRole(User u, Role r) { this() user = u role = r } @Override boolean equals(other) { if (!(other instanceof UserRole)) { return false } other.user?.id == user?.id && other.role?.id == role?.id } @Override int hashCode() { def builder = new HashCodeBuilder() if (user) builder.append(user.id) if (role) builder.append(role.id) builder.toHashCode() } static UserRole get(long userId, long roleId) { criteriaFor(userId, roleId).get() } static boolean exists(long userId, long roleId) { criteriaFor(userId, roleId).count() } private static DetachedCriteria criteriaFor(long userId, long roleId) { UserRole.where { user == User.load(userId) && role == Role.load(roleId) } } static UserRole create(User user, Role role, boolean flush = false) { def instance = new UserRole(user: user, role: role) instance.save(flush: flush, insert: true) instance } static boolean remove(User u, Role r, boolean flush = false) { if (u == null || r == null) return false int rowCount = UserRole.where { user == u && role == r }.deleteAll() if (flush) { UserRole.withSession { it.flush() } } rowCount } static void removeAll(User u, boolean flush = false) { if (u == null) return UserRole.where { user == u }.deleteAll() if (flush) { UserRole.withSession { it.flush() } } } static void removeAll(Role r, boolean flush = false) { if (r == null) return UserRole.where { role == r }.deleteAll() if (flush) { UserRole.withSession { it.flush() } } } static constraints = { role validator: { Role r, UserRole ur -> if (ur.user == null || ur.user.id == null) return boolean existing = false UserRole.withNewSession { existing = UserRole.exists(ur.user.id, r.id) } if (existing) { return 'userRole.exists' } } } static mapping = { id composite: ['user', 'role'] version false } }
在grails-app/conf目录里添加application.groovy
// Added by the Spring Security Core plugin: grails.plugin.springsecurity.userLookup.userDomainClassName='org.demo.User' grails.plugin.springsecurity.authority.className='org.demo.Role' grails.plugin.springsecurity.userLookup.authorityJoinClassName='org.demo.UserRole' grails.plugin.springsecurity.controllerAnnotations.staticRules = [ [pattern: '/', access: ['permitAll']], [pattern: '/error', access: ['permitAll']], [pattern: '/index', access: ['permitAll']], [pattern: '/index.gsp', access: ['permitAll']], [pattern: '/shutdown', access: ['permitAll']], [pattern: '/assets/**', access: ['permitAll']], [pattern: '/**/js/**', access: ['permitAll']], [pattern: '/**/css/**', access: ['permitAll']], [pattern: '/**/images/**', access: ['permitAll']], [pattern: '/**/favicon.ico', access: ['permitAll']] ] grails.plugin.springsecurity.filterChain.chainMap = [ [pattern: '/assets/**', filters: 'none'], [pattern: '/**/js/**', filters: 'none'], [pattern: '/**/css/**', filters: 'none'], [pattern: '/**/images/**', filters: 'none'], [pattern: '/**/favicon.ico', filters: 'none'], [pattern: '/api/**', filters: 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter'], [pattern: '/books/**', filters: 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter'] , [pattern: '/**', filters: 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter'] ]
给controller添加权限
org.demo.BookController.groovy
package org.demo import grails.rest.* import grails.converters.* import grails.plugin.springsecurity.annotation.Secured @Secured(['ROLE_ADMIN']) class BookController extends RestfulController { static responseFormats = ['json', 'xml', 'hal'] BookController() { super(Book) } }
添加一个用户
grails-app/init/BootStrap.groovy
import org.demo.* class BootStrap { def init = { servletContext -> Role admin = new Role("ROLE_ADMIN").save() User user = new User("user", "pass").save() UserRole.create(user, admin, true) new Book(title:"The Stand").save() new Book(title:"The Shining").save() } def destroy = { } }
重新编译,启动
请求 GET http://localhost:8080/books
得到认证失败的结果
{ "timestamp": 1461773336119, "status": 401, "error": "Unauthorized", "message": "No message available", "path": "/books" }
登陆
POST http://localhost:8080/api/login
Body 选 raw JSON(application/json)
内容:{"username":"user", "password":"pass"}
得到结果
{ "username": "user", "roles": [ "ROLE_ADMIN" ], "token_type": "Bearer", "access_token": "eyJhbGciOiJIUzI1NiJ9.eyJwcmluY2lwYWwiOiJINHNJQUFBQUFBQUFBSlZTUDBcL2JRQlJcL0RrRWdrQ2hVQXFrRExNQldPUklkTTBING8xWW1WSVFzVkdwMXNSXC91d2ZuTzNKMGhXYXBNTURDQTJpSWg4Ulg0SnJEd0FhcDI2TXJjdGU4TXdTa0w2azMydTU5XC9cLzU2djdtRFFhSGdUYThhRjhWT1J4Vno2SnRWY3hnYkRUSFBiOFRPRE9rS2JJOVp5WUpNbWNIKzhFbmdCbEhoazRXV3d5dzVZUlRBWlZ6WmF1eGphYWx2RGd0THhBK09PWmdrZUtyM25QM0tIU3VNXC9BZ1cxZDFhQ29XMllZR0dvTW1uclNxNjBVNjR4Mm9ieFloYW9jTStOSmtPNlFXazVFNllmT29TU3RRUkdBWXl5ekg1V3BNclJXSGh4YnphelhGUWFhS3NCREtmTUdITDNKRW5ET3V2dTN0bVVsR0FmdmtDNW5YcDBxTHQ1QlwvVWRqMTlUUWxCcXJxU1phOHBFUlh5SE8zSGk3MDVcL3ZUMjk3RFpMQU5USjYrZVwvS2VhdmxxQjdcL2ZIUFRGNjBGMXFZNnJOZXdLcnRsTnhNRk14YkdwM3lqNHYzMzg3dmpqOE1rTEpEclA3XC9QdVlXSDVycjFGU1NNczJzNnRzUjBSNlczVE9STHoxUDN0dEN4Mlwvd0pCVklmNVMwR0QxS0ZNUVV0NnlWNlBWdFlXUnpJMWo1dExpOFwvcmJ1WHN2T0o0bU81Wm5kc3Z4QTBhcE9mcFwvZG5NNytKSUozTUhqQVJJWlUrWGdCcW1kSkNcL1hSMWZuMDZQZGZKM21BM3NcLzhGOUpYTGZvUUF3QUEiLCJzdWIiOiJ1c2VyIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJleHAiOjE0NjE3Nzc2NzcsImlhdCI6MTQ2MTc3NDA3N30.UzEAN6CUbBsdH9QW13cxvBEjiWAkLcvX38st6IsWR3I", "expires_in": 3600, "refresh_token": "eyJhbGciOiJIUzI1NiJ9.eyJwcmluY2lwYWwiOiJINHNJQUFBQUFBQUFBSlZTUDBcL2JRQlJcL0RrRWdrQ2hVQXFrRExNQldPUklkTTBING8xWW1WSVFzVkdwMXNSXC91d2ZuTzNKMGhXYXBNTURDQTJpSWg4Ulg0SnJEd0FhcDI2TXJjdGU4TXdTa0w2azMydTU5XC9cLzU2djdtRFFhSGdUYThhRjhWT1J4Vno2SnRWY3hnYkRUSFBiOFRPRE9rS2JJOVp5WUpNbWNIKzhFbmdCbEhoazRXV3d5dzVZUlRBWlZ6WmF1eGphYWx2RGd0THhBK09PWmdrZUtyM25QM0tIU3VNXC9BZ1cxZDFhQ29XMllZR0dvTW1uclNxNjBVNjR4Mm9ieFloYW9jTStOSmtPNlFXazVFNllmT29TU3RRUkdBWXl5ekg1V3BNclJXSGh4YnphelhGUWFhS3NCREtmTUdITDNKRW5ET3V2dTN0bVVsR0FmdmtDNW5YcDBxTHQ1QlwvVWRqMTlUUWxCcXJxU1phOHBFUlh5SE8zSGk3MDVcL3ZUMjk3RFpMQU5USjYrZVwvS2VhdmxxQjdcL2ZIUFRGNjBGMXFZNnJOZXdLcnRsTnhNRk14YkdwM3lqNHYzMzg3dmpqOE1rTEpEclA3XC9QdVlXSDVycjFGU1NNczJzNnRzUjBSNlczVE9STHoxUDN0dEN4Mlwvd0pCVklmNVMwR0QxS0ZNUVV0NnlWNlBWdFlXUnpJMWo1dExpOFwvcmJ1WHN2T0o0bU81Wm5kc3Z4QTBhcE9mcFwvZG5NNytKSUozTUhqQVJJWlUrWGdCcW1kSkNcL1hSMWZuMDZQZGZKM21BM3NcLzhGOUpYTGZvUUF3QUEiLCJzdWIiOiJ1c2VyIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJpYXQiOjE0NjE3NzQwODN9.sv4cdahezBEOsy5cleOUUKHiwmKISHJCpx1kywwps_U" }
需要认证的请求带上access_token,有效期3600秒(可以自行设置)。
请求 GET http://localhost:8080/books
Headers
key:Authorization
value:
Bearer eyJhbGciOiJIUzI1NiJ9.eyJwcmluY2lwYWwiOiJINHNJQUFBQUFBQUFBSlZTUDBcL2JRQlJcL0RrRWdrQ2hVQXFrRExNQldPUklkTTBING8xWW1WSVFzVkdwMXNSXC91d2ZuTzNKMGhXYXBNTURDQTJpSWg4Ulg0SnJEd0FhcDI2TXJjdGU4TXdTa0w2azMydTU5XC9cLzU2djdtRFFhSGdUYThhRjhWT1J4Vno2SnRWY3hnYkRUSFBiOFRPRE9rS2JJOVp5WUpNbWNIKzhFbmdCbEhoazRXV3d5dzVZUlRBWlZ6WmF1eGphYWx2RGd0THhBK09PWmdrZUtyM25QM0tIU3VNXC9BZ1cxZDFhQ29XMllZR0dvTW1uclNxNjBVNjR4Mm9ieFloYW9jTStOSmtPNlFXazVFNllmT29TU3RRUkdBWXl5ekg1V3BNclJXSGh4YnphelhGUWFhS3NCREtmTUdITDNKRW5ET3V2dTN0bVVsR0FmdmtDNW5YcDBxTHQ1QlwvVWRqMTlUUWxCcXJxU1phOHBFUlh5SE8zSGk3MDVcL3ZUMjk3RFpMQU5USjYrZVwvS2VhdmxxQjdcL2ZIUFRGNjBGMXFZNnJOZXdLcnRsTnhNRk14YkdwM3lqNHYzMzg3dmpqOE1rTEpEclA3XC9QdVlXSDVycjFGU1NNczJzNnRzUjBSNlczVE9STHoxUDN0dEN4Mlwvd0pCVklmNVMwR0QxS0ZNUVV0NnlWNlBWdFlXUnpJMWo1dExpOFwvcmJ1WHN2T0o0bU81Wm5kc3Z4QTBhcE9mcFwvZG5NNytKSUozTUhqQVJJWlUrWGdCcW1kSkNcL1hSMWZuMDZQZGZKM21BM3NcLzhGOUpYTGZvUUF3QUEiLCJzdWIiOiJ1c2VyIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJleHAiOjE0NjE3Nzc2NzcsImlhdCI6MTQ2MTc3NDA3N30.UzEAN6CUbBsdH9QW13cxvBEjiWAkLcvX38st6IsWR3I
请求的value格式是token_type+空格+access_token。
得到结果
[ { "id": 1, "title": "The Stand" }, { "id": 2, "title": "The Shining" } ]
注意事项:
有些数据库user是关键字,所以更换数据库时可以改成person。
grails spring security rest文档:http://alvarosanchez.github.io/grails-spring-security-rest/latest/docs
grails spring security core文档:https://grails-plugins.github.io/grails-spring-security-core/v3/index.html
相关推荐
总体来看,这段教程所包含的知识点涵盖了Grails框架在RESTful开发中的项目创建、配置管理、依赖注入以及安全策略的实施。这些内容构成了进行Grails应用开发的基础知识,并且详细介绍了如何通过Gradle构建和管理项目...
在Grails中开发RESTful API是一项常见的任务,这个文档提供了基于Grails 3.1.5版本的REST教程。Grails是一种基于Groovy语言的开源Web应用框架,它利用了Spring Boot的功能,使得构建现代互联网应用程序变得更加高效...
**Grails 框架详解及示例应用** Grails 是一个基于 Groovy 语言的开源Web应用框架,它构建于Java平台之上,利用了Spring、Hibernate和Groovy的强大力量,旨在简化开发过程,提高开发效率。Grails遵循模型-视图-控制...
3. **GORM改进**:GORM(Grails ORM)得到了增强,支持更复杂的数据库查询操作。 4. **插件支持**:增加了更多的官方支持插件,扩展了Grails的功能。 5. **RESTful Web服务**:改进了RESTful Web服务的支持,使得...
7. **用户认证与权限管理**:集成Spring Security框架,实现用户认证和授权,保护应用的安全。 8. **测试与部署**:编写单元测试和功能测试,确保应用质量;配置部署环境,发布应用至生产服务器。 四、Grails的动态...
5. **安全性**:虽然 GroovyWS 模块仅提供了有限的安全性支持,但对于 RESTful 服务而言,Grails 提供了更为丰富的安全性和认证机制。 #### 结论 尽管 Groovy 和 Grails 在 SOA 实施方面展现出了巨大的潜力,但...
本书不仅提供了理论知识,还包含了大量的实战案例,如构建 RESTful API、实现安全认证、集成第三方服务等,帮助读者将所学知识应用于实际项目中,解决真实世界的开发问题。 ### 结语 《Grails 定义性指南》第二版...
### Groovy and Grails ...通过这些章节的学习,读者将能够全面了解Groovy编程语言和Grails框架,并掌握如何使用它们来开发高效、可维护的应用程序。无论是新手还是有经验的开发者,都能从中获得宝贵的知识和实践经验。
2. **RESTful API 认证与授权**:为 RESTful API 提供强大的认证和授权机制,确保 API 接口的安全访问。 3. **OAuth2 和 OpenID Connect 集成**:支持 OAuth2 和 OpenID Connect 协议,便于与第三方服务进行身份...
- **RESTful API**:考虑到现代 Web 应用的需求,`grails-chalkboard` 很可能提供了 RESTful API 接口,使得其他应用和服务可以轻松地与其集成和交互。 - **数据库集成**:Grails 自带的 GORM(Groovy Object ...
此外,Shiro还支持RESTful服务的安全防护,确保API接口的安全性。 6. **与其他框架的集成**:Shiro与Spring、Grails、Wicket等多种流行的Java框架有着良好的集成能力,使得开发者可以在现有的项目结构中无缝引入...
9. **安全机制**:涵盖J2EE的安全特性,如认证、授权、SSL/TLS加密以及会话管理,以及如何利用JAAS(Java Authentication and Authorization Service)实现安全控制。 10. **最佳实践**:书中会总结一系列在J2EE...
在Web开发方面,本书主要讨论了Spring MVC、动态脚本语言的集成、与Grails框架和Groovy语言的整合以及RESTful Web服务的构建。Spring MVC是Spring提供的一个完整Web框架,它遵循模型-视图-控制器的设计模式,允许...
在Java世界中,轻量级框架如Spring Boot、Play Framework、Grails等,提供了一种简化应用开发的方式,它们减少了繁琐的配置,提升了开发效率。在设计实验室管理系统时,Spring Boot通常被视为首选,因为它提供了全面...
GR8Conf是一个专注于Groovy、Grails及其他GR8技术的国际性会议。Ratpack是一个高性能、反应式的Java和Groovy应用框架,它强调简洁、快速的开发和高效的运行时性能。 描述中提到"每个标签都是幻灯片中的一个示例",...
Spring Session 是一个开源项目,由 Pivotal Software 开发,旨在提供统一的会话管理解决方案。...在传统的单体应用中,session 通常存储在...在实际开发中,务必考虑安全性因素,并根据项目需求选择合适的会话存储后端。
总结,GrailsFirstApp是一个集成了Grails后端框架和JavaScript前端技术的实践项目,它涵盖了Web开发的基本要素,包括用户认证、数据操作、页面动态更新等。通过这个项目,开发者能深入理解前后端协作,掌握Web应用的...