目標
- 盡可能簡單的使用Grails Shiro Plugin實現身份驗證功能.
安裝
- Grails版本:2.1.1
- 約定:在代碼示例中,任何使用$開頭,說明這是一個shell命令
1 $ grails create-app shiro-example 2 $ cd shiro-example
- shiro plugin最新的插件版本是1.1.4,可以使用grails install-plugin shiro安裝,但是這里使用最新的快照版本。不需要用shell安裝只需要在項目的BuildConfig.groovy中增加插件的引用即可
plugins { runtime ":hibernate:$grailsVersion" runtime ":jquery:1.8.3" runtime ":resources:1.1.6" runtime ":shiro:1.2.0-SNAPSHOT"
- 當我們修改了BuildConfig.groovy文件后,需要對系統進行重新編譯,修改才能生效
$ grails compile
- 現在插件已經安裝好了,下一步建立一個初步的腳手架,執行這個shell后,系統會幫我們生成一系列的腳手架文件,這個--prefix=[包路徑]是可選項,如果使用包路徑,不要忘記最后面的那個"."
$ grails shiro-quick-start --prefix=com.example. | Created file grails-app/domain/com/example/User.groovy | Created file grails-app/domain/com/example/Role.groovy | Created file grails-app/realms/com/example/DbRealm.groovy | Created file grails-app/controllers/com/example/AuthController.groovy | Created file grails-app/views/auth/login.gsp | Created file grails-app/conf/com/example/SecurityFilters.groovy
配置
Bootstrap.groovy
1 import com.example.Role 2 import com.example.User 3 4 class BootStrap { 5 6 def shiroSecurityService 7 8 def init = { servletContext -> 9 // Create the admin role 10 def adminRole = Role.findByName('ROLE_ADMIN') ?: 11 new Role(name: 'ROLE_ADMIN').save(flush: true, failOnError: true) 12 13 // Create the user role 14 def userRole = Role.findByName('ROLE_USER') ?: 15 new Role(name: 'ROLE_USER').save(flush: true, failOnError: true) 16 17 // Create an admin user 18 def adminUser = User.findByUsername('admin') ?: 19 new User(username: "admin", 20 passwordHash: shiroSecurityService.encodePassword('password')) 21 .save(flush: true, failOnError: true) 22 23 // Add roles to the admin user 24 assert adminUser.addToRoles(adminRole) 25 .addToRoles(userRole) 26 .save(flush: true, failOnError: true) 27 28 // Create an standard user 29 def standardUser = User.findByUsername('joe') ?: 30 new User(username: "joe", 31 passwordHash: shiroSecurityService.encodePassword('password')) 32 .save(flush: true, failOnError: true) 33 34 // Add role to the standard user 35 assert standardUser.addToRoles(userRole) 36 .save(flush: true, failOnError: true) 37 38 } 39 def destroy = { 40 } 41 }
- 在代碼第6行,引入了shiro的安全服務,並使用服務的encodePassword方法進行加密
- 在代碼第9到15行,新增admin和user兩個角色
- 在代碼第17到21行,新增一個賬號為admin的用戶,並對password進行加密
- 在代碼第23到26行,給admin用戶進行角色授權
- 在代碼第28到36行,給user用戶進行角色授權
運行測試一下效果
$grails run-app
or
$grails -Dserver.port=8888 run-app
登錄http://localhost:8080/shiro-example,點com.example.AuthController控制器,系統彈出登錄窗口,輸入賬號和密碼后,系統返回主界面,什么都沒發生過。這是因為我們沒有需要驗證的界面,下面再增加一點東西
- 新增一個控制器
$ grails create-controller com.example.Home
- 給這個控制器增加三個action
1 package com.example 2 3 class HomeController { 4 5 def index() { 6 render "這個頁面不需要驗證" 7 } 8 9 def secured() { 10 render "這個頁面需要user角色才能訪問" 11 } 12 13 def admin() { 14 render "這個頁面需要admin角色才能訪問" 15 } 16 }
- 正如我們看到的,我可以分別登陸不同的視圖,特定的角色才能返回正常的結果
- 如果權限不夠的用戶登錄到了相應頁面,系統提示“You do not have permission to access this page”
- 如果我們現在運行,並訪問,不管登錄到index、secured還是admin,系統都會提示“You do not have permission to access this page”
現在,分別給不同的action進行授權,打開SecurityFilters.groovy,拷貝如下代碼
1 package com.example 2 3 /** 4 * Generated by the Shiro plugin. This filters class protects all URLs 5 * via access control by convention. 6 */ 7 class SecurityFilters { 8 9 /** 10 * Array of controller/action combinations which will be skipped from authentication 11 * if the controller and action names match. The action value can also be '*' if it 12 * encompasses all actions within the controller. 13 */ 14 static nonAuthenticatedActions = [ 15 [controller: 'home', action: 'index'] 16 ] 17 18 /** 19 * Array of controller/action combinations that will be authenticated against the user's 20 * role. The map also includes the roles which the controller/action pair will match 21 * against. 22 */ 23 static authenticatedActions = [ 24 [controller: 'home', action: 'secured', roles: ['ROLE_ADMIN', 'ROLE_USER']], 25 [controller: 'home', action: 'admin', roles: ['ROLE_ADMIN']] 26 ] 27 28 def filters = { 29 30 all(controller: '*', action: '*') { 31 before = { 32 33 // Determine if the controller/action belongs is not to be authenticated 34 def needsAuth = !nonAuthenticatedActions.find { 35 (it.controller == controllerName) && 36 ((it.action == '*') || (it.action == actionName)) 37 } 38 39 if (needsAuth) { 40 41 // Get the map within the authenticated actions which pertain to the current 42 // controller and view. 43 def authRoles = authenticatedActions.find { 44 (it.controller == controllerName) && 45 ((it.action == '*') || (it.action == actionName)) 46 } 47 48 if (authRoles) { 49 50 // Perform the access control for each of the roles provided in the authRoles 51 accessControl { 52 authRoles.roles.each { roleName -> 53 role(roleName) 54 } 55 } 56 } 57 58 // Skip authentication if the authRoles was not found 59 else { 60 return true 61 } 62 } 63 64 // Skip authentication if no auth is needed 65 else { 66 return true 67 } 68 } 69 } 70 71 } 72 }
- 代碼第14到16行,登記不需要授權的action
- 代碼第23到26行,登記需要授權的action,並指定相應的角色
現在,大功告成,再使用不同的角色,訪問不同的頁面,應該可以得到不同的效果
http://localhost:8080/shiro-example/home/index : non
http://localhost:8080/shiro-example/home/secured : user,admin