新增驗證
既然我們有一個管理皮膚,自然需要進行驗證。幸運的是,Play已經提供了一個叫做Secure的模組來幫助我們。
啟動Secure模組
在yabe/conf/application.conf
啟動Secure模組,然後重啟應用。
# 匯入secure模組
module.secure=${play.path}/modules/secure
在重啟後,Play應該提示說模組已被載入。
Secure模組提供一系列預設的路由。要匯入這些路由,在/yabe/conf/routes
加入:
# Import Secure routes
* / module:secrue
保護管理控制器
這個模組提供了一個controllers.Secure
控制器,它定義了所有所需的攔截器。當然我們可以繼承這個控制器,但是Java只支援單繼承,可能不可以這麼做。
除了直接繼承Secure
控制器,我們也可以給控制器加@With
註解來告訴Play啟動對應的攔截器:
package controllers;
import play.*;
import play.mvc.*;
@With(Secure.class)
public class Posts extends CRUD {
}
同樣處理Comments
,Users
和Tags
控制器。
現在如果你想訪問管理皮膚,就會跳轉到登入頁面。
不過,現在無論你往使用者/密碼框填什麼,都會讓你通過。
自定義驗證過程
你可以用應用提供的controllers.Secure.Security
例項來自定義驗證過程。通過繼承自該類,我們可以指定驗證使用者的方式。
建立yabe/app/controllers/Security.java
,並過載authenticata()
方法:
package controllers;
import models.*;
public class Security extends Secure.Security {
static boolean authenticate(String username, String password) {
return true;
}
}
因為我們已經在模型層實現了User物件,驗證方法的實現就很簡單了:
static boolean authenticate(String username, String password) {
return User.connect(username, password) != null;
}
現在前往http://localhost:9000/logout登出,然後嘗試以initial-data.yml
中的任意使用者登入,比如bob@gmail.com/secret
。
重構管理皮膚
我們通過CRUD模組開啟了管理皮膚,但它跟部落格的UI格格不入。所以我們還需要實現另一個管理皮膚。這個將給每個作者釋出自己的文章的許可權。當然,原來的管理皮膚還是可以留下來給超級管理員用。
讓我們建立一個Admin
控制器:
package controllers;
import play.*;
import play.mvc.*;
import java.util.*;
import models.*;
@With(Secure.class)
public class Admin extends Controller {
@Before
static void setConnectedUser() {
if(Security.isConnected()) {
User user = User.find("byEmail", Security.connected()).first();
renderArgs.put("user", user.fullname);
}
}
public static void index() {
render();
}
}
然後重構yabe/conf/routes
裡面的路由:
# Administration
GET /admin/? Admin.index
* /admin module:crud
記住路由檔案中的順序是有確切含義的;排在前面的會比後面的優先匹配。所以Admin
的路由要放在對映到CRUD模組的路由的前面。否則,/admin/
會被對映到CRUD.index
而不是Admin.index
。
現在在yabe/app/views/main.html
新增到控制器的連結:
…
<ul id="tools">
<li>
<a href="@{Admin.index()}">Log in to write something</a>
</li>
</ul>
…
接下來是建立yabe/app/views/Admin/index.html
模板。讓我們先從基礎開始:
Welcome ${user}!
現在,前往部落格主頁,點選“Log in to write something”連結,你應該到達新的管理皮膚:
好的開始!但因為管理皮膚將會有一系列新的頁面,我們需要定義一個父模板。建立yabe/app/views/admin.html
:
<!DOCTYPE html>
<html>
<head>
<title>Administration</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
#{get 'moreStyles' /}
<link rel="stylesheet" type="text/css" media="screen"
href="@{'/public/stylesheets/main.css'}" />
<link rel="shortcut icon" type="image/png"
href="@{'/public/images/favicon.png'}" />
<script src="@{'/public/javascripts/jquery-1.4.2.min.js'}"></script>
<script src="@{'/public/javascripts/jquery.tools-1.2.5.toolbox.expose.min.js'}"></script>
</head>
<body id="admin">
<div id="header">
<div id="logo">
yabe. <span>administration</span>
</div>
<ul id="tools">
<li>
<a href="@{Secure.logout()}">Log out</a>
</li>
</ul>
</div>
<div id="main">
#{doLayout /}
</div>
<p id="footer">
Yabe is a (not so) powerful blog engine built with the
<a href="http://www.playframework.org">Play framework</a>
as a tutorial application.
</p>
</body>
</html>
如你所見,它像是前面用在部落格引擎上的模板。這裡替換了Log in連結成Log out,呼叫secure模組提供的Secure
控制器的logout
action。
現在就把它用到yabe/app/views/Admin/index.html
模板:
#{extends 'admin.html' /}
Welcome ${user}!
重新整理!
嘗試下log out,它會讓你重新登入:
我們是用secure模組的預設方法來處理登出。不過自定義也是十分簡單的,重寫controllers.Security
類中的onDisconnected()
方法即可:
static void onDisconnected() {
Application.index();
}
你可以同樣過載onAuthenticated()
:
static void onAuthenticated() {
Admin.index();
}
新增身份
我們有兩個管理皮膚:一個用於編輯者,另一個用於管理員。如你曾見,User
模型有一個isAdmin
成員,表示一個使用者是否有管理員許可權。
secure模組不僅提供了authentication
,還支援authorization
。在secure模組中,這叫做 profiles
。要建立admin
身份(profile),你僅需重寫controllers.Security
中的 check()
。
static boolean check(String profile) {
if("admin".equals(profile)) {
return User.find("byEmail", connected()).<User>first().isAdmin;
}
return false;
}
如果使用者具有管理員許可權,我們可以提供一個管理員選單。更新app/views/admin.html
來新增頂級選單:
…
<div id="main">
<ul id="adminMenu">
<li class="${request.controller == 'Admin' ? 'selected' : ''}">
<a href="@{Admin.index()}">My posts</a>
</li>
#{secure.check 'admin'}
<li class="${request.controller == 'Posts' ? 'selected' : ''}">
<a href="@{Posts.list()}">Posts</a>
</li>
<li class="${request.controller == 'Tags' ? 'selected' : ''}">
<a href="@{Tags.list()}">Tags</a>
</li>
<li class="${request.controller == 'Comments' ? 'selected' : ''}">
<a href="@{Comments.list()}">Comments</a>
</li>
<li class="${request.controller == 'Users' ? 'selected' : ''}">
<a href="@{Users.list()}">Users</a>
</li>
#{/secure.check}
</ul>
#{doLayout /}
</div>
…
注意我們用#{secure.check /}
標籤,只給admin
使用者展示選單。
但是我們的CRUD部分依然處於危險之中!如果使用者知道URL,他/她還是可以訪問它。我們必須保護這些控制器。最簡單的方法是使用@Check
註解。舉個例子,對於Posts
控制器:
package controllers;
import play.*;
import play.mvc.*;
@Check("admin")
@With(Secure.class)
public class Posts extends CRUD {
}
同樣處理Tags
,Comments
和Users
控制器。現在作為普通使用者(比如``)登入。你應該看不到CRUD管理員連結。如果試圖訪問http://localhost:9000/admin/u...,你會得到一個403 Forbidden響應。
自定義CRUD佈局
當我們使用基於CRUD的那個管理皮膚時,就無法使用管理佈局了。因為CRUD模組提供了自己的佈局。不過當然我們可以過載掉它。使用Play命令:
play crud:ov --layout
你會得到一個/yabe/app/views/CRUD/layout.html
。來把它的內容替換掉,整合我們的admin.html
佈局:
#{extends 'admin.html' /}
#{set 'moreStyles'}
<link rel="stylesheet" type="text/css" media="screen"
href="@{'/public/stylesheets/crud.css'}" />
#{/set}
<div id="crud">
#{if flash.success}
<div class="crudFlash flashSuccess">
${flash.success}
</div>
#{/if}
#{if flash.error || error}
<div class="crudFlash flashError">
${error ?: flash.error}
</div>
#{/if}
<div id="crudContent">
#{doLayout /}
</div>
</div>
如你所見,我們重用了crud.css
並使用get/set模板變數機制來融合admin.html
。現在看下CRUD模組的管理皮膚,它應該跟管理佈局結合在一起了:
美化登入介面
管理皮膚的介面大體上已經完成了。最後要做的,是美化登入介面。如常,從自定義預設的css開始吧。
play secure:ov --css
要想維持原來的css,我們需要在頂部匯入main.css
。在yabe/public/stylesheets/secure.css
頂部新增這一行:
@import url(main.css);
…
加入這些到你的yabe/conf/messages
檔案來自定義登入介面資訊:
secure.username=Your email:
secure.password=Your password:
secure.signin=Log in now