首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

精通 Grails:身份驗證與授權實戰

     編輯:Java 相關知識

延續「微型部落格」Blogito 的建置,本文實作完整的使用者子系統:登入驗證、角色授權、細粒度權限控管,並探討插件化的進階安全方案。

核心領域模型

class User {
    static constraints = {
        login(unique: true)
        password(password: true)
        name()
        role(inList: ['author', 'admin'])
    }
    static hasMany = [entries: Entry]
    String login, password, name
    String role = 'author'
    String toString() { name }
}

role 預設 author,inList 限制只能是 author 或 admin,表單自動產生下拉選單。

身份驗證:登入/登出流程

class UserController {
    def scaffold = User
    def login = {}
    def authenticate = {
        def user = User.findByLoginAndPassword(params.login, params.password)
        if (user) {
            session.user = user
            flash.message = "Hello ${user.name}!"
            redirect(controller: 'entry', action: 'list')
        } else {
            flash.message = "Sorry, ${params.login}. Please try again."
            redirect(action: 'login')
        }
    }
    def logout = {
        flash.message = "Goodbye ${session.user.name}"
        session.user = null
        redirect(controller: 'entry', action: 'list')
    }
}

login.gsp 表單送往 authenticate,欄位名稱對應 params.login/params.password。

登入失敗畫面

登入成功提示

自訂 TagLib:頁首登入控制

class LoginTagLib {
    def loginControl = {
        if (session.user) {
            out << "Hello ${session.user.name} "
            out << """[${link(action: 'logout', controller: 'user') { 'Logout' }}]"""
        } else {
            out << """[${link(action: 'login', controller: 'user') { 'Login' }}]"""
        }
    }
}

嵌入 _header.gsp:

<div id="loginHeader">
    <g:loginControl />
</div>

CSS 靠右浮動,體驗媲美 Google/Amazon 頁首。

頁首登入控制實際效果

授權機制:從粗到細

1. BeforeInterceptor:全域攔截

class EntryController {
    def beforeInterceptor = [action: this.&auth, except: ['index', 'list', 'show']]
    def auth() {
        if (!session.user) {
            redirect(controller: 'user', action: 'login')
            return false
        }
    }
    // ...
}

未登入嘗試存取 /entry/create 即被導向登入頁;登入後放行。

2. 動作層級授權:僅作者可編輯

def edit = {
    def entryInstance = Entry.get(params.id)
    if (!(session.user.login == entryInstance.author.login)) {
        flash.message = "Sorry, you can only edit your own entries."
        redirect(action: 'list')
    }
    // ...
}

同樣套用至 delete、update,或抽取為私有方法複用。

3. 角色基礎授權 (RBAC)

BootStrap.groovy 建立 admin 使用者:

def admin = new User(login: 'admin', password: 'password', name: 'Administrator', role: 'admin')
admin.save()

UserController 限制帳號管理僅限 admin:

def beforeInterceptor = [action: this.&auth, except: ['login', 'authenticate', 'logout']]
def auth() {
    if (!(session?.user?.role == 'admin')) {
        flash.message = "You must be an administrator to perform that task."
        redirect(action: 'login')
        return false
    }
}

非管理員被阻擋

密碼加密:自訂 Codec

class HashCodec {
    static encode = { str ->
        MessageDigest md = MessageDigest.getInstance('SHA')
        md.update(str.getBytes('UTF-8'))
        return (new BASE64Encoder()).encode(md.digest())
    }
}

在 UserController 中改用 params.password.encodeAsHash(),10 行代碼提升安全等級。

插件化進階方案

手寫策略適合簡單場景,複雜需求建議採用成熟插件:

  • Authentication Plugin:使用者自行註冊、Email 驗證
  • OpenID Plugin:委託第三方身分提供者
  • LDAP Plugin:整合企業目錄服務
  • Spring Security Plugin:企業級 ACL、權限繼承、記憶我
  • JSecurity (Shiro) Plugin:領域/權限/角色模板化

原則:優勢大於複雜度時才引入插件。

代碼規模驗證

$ grails stats
+----------------------+-------+-------+
| Name                 | Files | LOC   |
+----------------------+-------+-------+
| Controllers          | 2     | 95    |
| Domain Classes       | 2     | 32    |
| Tag Libraries        | 2     | 21    |
| Unit Tests           | 5     | 20    |
| Integration Tests    | 1     | 10    |
+----------------------+-------+-------+
| Totals               | 12    | 178   |
+----------------------+-------+-------+

不足 200 行完成完整身分驗證與授權系統。

結語

Blogito 現具備登入/登出、角色授權、細粒度權限控管。下一篇將實作檔案上傳與 Atom 聯合供稿,讓 Blogito 成為真正可用的部落格引擎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved