<optgroup id="gajtf"></optgroup>
  • <meter id="gajtf"></meter>
    <mark id="gajtf"><ruby id="gajtf"></ruby></mark>

  • <small id="gajtf"><strong id="gajtf"></strong></small>
  • <mark id="gajtf"><button id="gajtf"></button></mark>
    <listing id="gajtf"><dfn id="gajtf"><s id="gajtf"></s></dfn></listing><label id="gajtf"><ruby id="gajtf"></ruby></label>
    <mark id="gajtf"></mark>

    Grails3開發之(Rest教程)

    Grails開發Restful風格簡易教程

    參考地址:http://learnwebtechs.com/2017/02/21/grails-3-spring-security-rest-mongodb-step-by-step

    環境:
    | Grails Version: 3.1.5
    | Groovy Version: 2.4.6
    | JVM Version: 1.8.0_144

    build.gradle配置:

    buildscript {
        ext {
            grailsVersion = project.grailsVersion
        }
        repositories {
            mavenLocal()
            maven { url "https://repo.grails.org/grails/core" }
        }
        dependencies {
            classpath "org.grails:grails-gradle-plugin:$grailsVersion"
            classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.8.2"
            classpath "org.grails.plugins:hibernate4:5.0.5"
        }
    }
    
    version "0.1"
    group "demo"
    
    apply plugin:"eclipse"
    apply plugin:"idea"
    apply plugin:"war"
    apply plugin:"org.grails.grails-web"
    apply plugin:"org.grails.grails-gsp"
    apply plugin:"asset-pipeline"
    
    ext {
        grailsVersion = project.grailsVersion
        gradleWrapperVersion = project.gradleWrapperVersion
        springSecurityRestVersion = '2.0.0.M2'
    }
    
    repositories {
        mavenLocal()
        maven { url "https://repo.grails.org/grails/core" }
    }
    
    dependencyManagement {
        imports {
            mavenBom "org.grails:grails-bom:$grailsVersion"
        }
        applyMavenExclusions false
    }
    
    dependencies {
        compile "org.springframework.boot:spring-boot-starter-logging"
        compile "org.springframework.boot:spring-boot-autoconfigure"
        compile "org.grails:grails-core"
        compile "org.springframework.boot:spring-boot-starter-actuator"
        compile "org.springframework.boot:spring-boot-starter-tomcat"
        compile "org.grails:grails-dependencies"
        compile "org.grails:grails-web-boot"
        compile "org.grails.plugins:cache"
        compile "org.grails.plugins:scaffolding"
        compile "org.grails.plugins:hibernate4"
        compile "org.hibernate:hibernate-ehcache"
        console "org.grails:grails-console"
        profile "org.grails.profiles:web:3.1.5"
        runtime "com.bertramlabs.plugins:asset-pipeline-grails:2.8.2"
        runtime "com.h2database:h2"
        testCompile "org.grails:grails-plugin-testing"
        testCompile "org.grails.plugins:geb"
        testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
        testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
    
        //spring-security依賴
        compile "org.grails.plugins:spring-security-core:3.0.0"
        //rest api依賴
        compile "org.grails.plugins:spring-security-rest:${springSecurityRestVersion}"
        //rest api存儲token的依賴
        compile "org.grails.plugins:spring-security-rest-gorm:${springSecurityRestVersion}"
    
        //mysql依賴
        compile "mysql:mysql-connector-java:5.1.38"
    }
    
    task wrapper(type: Wrapper) {
        gradleVersion = gradleWrapperVersion
    }
    
    assets {
        minifyJs = true
        minifyCss = true
    }
    

    在項目目錄grails-app/conf下面創建腳本application.groovy,配置spring secruity和rest

    // 添加spring-security插件核心配置
    
    //加密方式
    grails.plugin.springsecurity.password.algoritham = 'SHA-256'
    
    //用戶類
    grails.plugin.springsecurity.userLookup.userDomainClassName = 'com.system.UserInfo'
    
    //用戶角色類
    grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'com.system.UserRole'
    
    //角色類
    grails.plugin.springsecurity.authority.className = 'com.system.RoleInfo'
    
    //角色集合字段,是UserInfo的getAuthorities()
    grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities'
    
    //是否使用組類進行管理,如果使用了true,則必須配置組的相關東西
    grails.plugin.springsecurity.useRoleGroups = false
    
    //請求的url屬性字段
    grails.plugin.springsecurity.requestMap.urlField = 'url'
    
    grails.plugin.springsecurity.relationalAuthorities='allRoles'
    
    
    grails.plugin.springsecurity.filterChain.chainMap = [
        [pattern: '/api/**', filters:'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter'],
        [pattern: '/**', filters:'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter']
    ]
    
    grails.plugin.springsecurity.controllerAnnotations.staticRules = [
            [pattern: '/',               access: ['permitAll']],
            [pattern: '/500',            access: ['permitAll']],
            [pattern: '/404',            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']],
            // block all other URL access
            [pattern: '/**', access: ['denyAll'], httpMethod: 'GET'],
            [pattern: '/**', access: ['denyAll'], httpMethod: 'POST'],
            [pattern: '/**', access: ['denyAll'], httpMethod: 'PUT'],
            [pattern: '/**', access: ['denyAll'], httpMethod: 'DELETE']
    ]
    
    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/login',      filters: 'securityCorsFilter,restAuthenticationFilter'],
            //Stateless chain
            [
                    pattern: '/api/**',
                    filters: 'JOINED_FILTERS,-securityCorsFilter,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter'
            ],
    
            //Traditional chain
            [
                    pattern: '/**',
                    filters: 'JOINED_FILTERS,-securityCorsFilter,-restTokenValidationFilter,-restExceptionTranslationFilter'
            ]
    ]
    
    //rest configuration
    grails.plugin.springsecurity.rest.token.storage.useGorm = false // since using gorm for token storage
    grails.plugin.springsecurity.rest.token.generation.useSecureRandom = true
    grails.plugin.springsecurity.rest.login.active =true
    grails.plugin.springsecurity.rest.login.useJsonCredentials = true // can use json a request parameter
    grails.plugin.springsecurity.rest.login.usernamePropertyName = 'username' // field of username parameter
    grails.plugin.springsecurity.rest.login.passwordPropertyName = 'password' // field of pasword parameter
    grails.plugin.springsecurity.rest.login.useRequestParamsCredential = true
    
    grails.plugin.springsecurity.rest.token.storage.gorm.tokenDomainClassName ='com.system.AuthenticationToken' // token class name with package
    grails.plugin.springsecurity.rest.token.storage.gorm.tokenValuePropertyName = 'secretToken' // field name for token storage
    grails.plugin.springsecurity.rest.token.storage.gorm.usernamePropertyName = 'loginName'
    
    grails.plugin.springsecurity.rest.logout.endpointUrl = '/api/logout'
    grails.plugin.springsecurity.rest.login.endpointUrl = '/api/login'
    grails.plugin.springsecurity.rest.login.failureStatusCode = 401
    //token validate
    grails.plugin.springsecurity.rest.token.validation.useBearerToken = true
    grails.plugin.springsecurity.rest.token.validation.headerName = 'X-Auth-Token'
    grails.plugin.springsecurity.rest.token.validation.active=true
    grails.plugin.springsecurity.rest.token.validation.endpointUrl='/api/validate'
    //end of rest configuration
    grails.plugin.springsecurity.logout.postOnly = false
    

    創建domain

    AuthenticationToken

    package com.system
    
    class AuthenticationToken {
    
        String secretToken // field to store the token used for accessing api end point
        String loginName // login name of the user
    
        static mapping = {
            version false
        }
    }
    

    UserInfo

    package com.system
    
    import groovy.transform.EqualsAndHashCode
    import groovy.transform.ToString
    
    /**
     *  用戶
     */
    @EqualsAndHashCode(includes='username')
    @ToString(includes='username', includeNames=true, includePackage=false)
    class UserInfo implements Serializable {
    
       private static final long serialVersionUID = 1
    
       transient springSecurityService
    
       String username
       String password
       boolean enabled = true
       boolean accountExpired
       boolean accountLocked
       boolean passwordExpired
    
       UserInfo(String username, String password) {
          this()
          this.username = username
          this.password = password
       }
    
       Set<RoleInfo> getAuthorities() {
          UserRole.findAllByUser(this)*.role as Set<RoleInfo>
       }
    
       def beforeInsert() {
          encodePassword()
       }
    
       def beforeUpdate() {
          if (isDirty('password')) {
             encodePassword()
          }
       }
    
       protected void encodePassword() {
          password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
       }
    
       static transients = ['springSecurityService']
    
       static constraints = {
          password blank: false, password: true
          username blank: false, unique: true
       }
    
       static mapping = {
          password column: '`password`'
       }
    }
    

    RoleInfo

    package com.system
    
    import groovy.transform.EqualsAndHashCode
    import groovy.transform.ToString
    
    @EqualsAndHashCode(includes='authority')
    @ToString(includes='authority', includeNames=true, includePackage=false)
    class RoleInfo implements Serializable {
    
    	private static final long serialVersionUID = 1
    
    	RoleInfo(String authority, String authorityName) {
    		this.authority = authority
    		this.authorityName = authorityName
    	}
    
    	String authority     //權限標識
    	String authorityName //權限名稱
    
    	static constraints = {
    		authority blank: false, unique: true
    	}
    
    	static mapping = {
    		cache true
    	}
    
    
    	@Override
    	public String toString() {
    		"${authorityName}(${authority})"
    	}
    }
    

    UserRole

    /**
     * 用戶-角色關聯表
     * */
    
    package com.system
    
    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
    
        UserInfo user
        RoleInfo role
    
        UserRole(UserInfo u, RoleInfo 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 == UserInfo.load(userId) &&
                        role == RoleInfo.load(roleId)
            }
        }
    
        static UserRole create(UserInfo user, RoleInfo role, boolean flush = false) {
            def instance = new UserRole(user: user, role: role)
            instance.save(flush: flush, insert: true)
            instance
        }
    
        static boolean remove(UserInfo u, RoleInfo 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 boolean removeAll(UserInfo u, boolean flush = false) {
            if (u == null) return false
    
            UserRole.where { user == u }.deleteAll()
    
            if (flush) { UserRole.withSession { it.flush() } }
            return true
        }
    
        static void removeAll(RoleInfo r, boolean flush = false) {
            if (r == null) return
    
            UserRole.where { role == r }.deleteAll()
    
            if (flush) { UserRole.withSession { it.flush() } }
        }
    
        static constraints = {
            role validator: { RoleInfo 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
        }
    }
    

    添加一個測試domain類Product

    package com.test
    
    import grails.rest.Resource
    
    @Resource(readOnly = false, formats = ['json', 'xml'])
    class Product {
    
        String prodName
        String prodDesc
        Double prodPrice
        Date createDate = new Date()
    
        static constraints = {
        }
    }
    

    urlMapping

    package demo
    
    class UrlMappings {
    
        static mappings = {
            "/$controller/$action?/$id?(.$format)?"{
                constraints {
                    // apply constraints here
                }
            }
    
            "/"(view:"/index")
            "500"(view:'/error')
            "404"(view:'/notFound')
    
            "/api/product"(resource: "product")
        }
    }
    

    創建controller,用于測試

    package com.test
    
    import grails.converters.JSON
    import grails.rest.RestfulController
    import org.springframework.security.access.annotation.Secured
    
    @Secured("ROLE_ADMIN")
    class ProductController extends RestfulController<Product> {
    
        static responseFormats = ['json', 'xml']
    
        ProductController() {
            super(Product)
        }
    
        def list() {
            render ([result:true, message: "操作成功"]) as JSON
        }
    }
    

    然后在grails-app/init/bootstrap.groovy中添加啟動任務

    import com.system.RoleInfo
    import com.system.UserInfo
    import com.system.UserRole
    import com.test.Product
    import grails.plugin.springsecurity.SecurityFilterPosition
    import grails.plugin.springsecurity.SpringSecurityUtils
    
    class BootStrap {
    
        def init = { servletContext ->
    
            //注冊注銷過濾器,如果不添加則注銷不起作用
            SpringSecurityUtils.clientRegisterFilter('restLogoutFilter', SecurityFilterPosition.LOGOUT_FILTER.order - 1)
    
            //建立默認用戶權限
            def superadminRoleInfo = new RoleInfo('ROLE_SUPERADMIN', '超級管理員').save()
            def adminRoleInfo = new RoleInfo('ROLE_ADMIN', '管理員').save()
            def userRoleInfo = new RoleInfo('ROLE_USER', '用戶').save()
    
    
            def superUser = new UserInfo('sys', 'sys9988').save()
            UserRole.create superUser, superadminRoleInfo, true
    
            def user = new UserInfo('admin', 'admin9988').save()
            UserRole.create user, adminRoleInfo, true
    
            def testUser = new UserInfo('tom', 'tom9988').save()
            UserRole.create testUser, userRoleInfo, true
    
    
            def prod1 = new Product(prodName:"iPhone 7",prodDesc:"New iPhone 7 32GB",prodPrice:780).save flush:true
            def prod2 = new Product(prodName:"iPhone 7 Plus",prodDesc:"New iPhone 7 Plus 128GB",prodPrice:990).save flush:true
            def prod3 = new Product(prodName:"iPhone 7 SE",prodDesc:"New iPhone 7 SE 64GB",prodPrice:520).save flush:true
        }
    
        def destroy = {
    
        }
    }
    

    大吉大利,今晚吃雞!

    接下來開始測試。

    打開PostMan接口測試工具:

    首先測試登錄,粘貼請求url到地址欄,請求方式選擇post,選擇body--raw,然后在文本框中輸入請求參數,點擊send,如果成功則會返回用戶信息,否則為空。

    {"username":"admin","password":"admin"}

    attachments-2017-11-H039vqQV5a0cf70f07bc2.png驗證token是否過期,若未過期則返回用戶相關信息,否則返回空,在head里面添加參數,

    key為Authorization,value為登錄返回的token,也就是access_token。
    注意:value為:Bearer+空格+access_token

    attachments-2017-11-CMGRGja05a0cf7587ba60.png

    測試接口/product/list,后臺配置了只允許ROLE_ADMIN這個角色才能訪問

    attachments-2017-11-KQPADcE65a0cf76de5a60.png


    注銷登錄,url: http://localhost:8080/api/logout

    attachments-2017-11-vsAwpH9W5a0cf77f2c700.png

    如果需要自定義token驗證也可以注入token服務

    def tokenStorageService

    說明:

    IS_AUTHENTICATED_ANONYMOUSLY //允許匿名用戶進入
    IS_AUTHENTICATED_FULLY //允許登錄用戶進入
    IS_AUTHENTICATED_REMEMBERED //允許登錄用戶和rememberMe用戶進入



    • 發表于 2017-11-16 10:28
    • 閱讀 ( 4046 )
    • 分類:grails

    0 條評論

    請先 登錄 后評論
    不寫代碼的碼農
    Jonny

    程序猿

    65 篇文章

    作家榜 ?

    1. 威猛的小站長 119 文章
    2. Jonny 65 文章
    3. 江南煙雨 33 文章
    4. - Nightmare 33 文章
    5. doublechina 31 文章
    6. HJ社區-肖峰 29 文章
    7. 偽攝影 20 文章
    8. Alan 14 文章
    小喜通天报