PandaJS 使用說明(1.7):權限控制和數據校驗
利用上一篇文章提到的 proxy 對象,我們還可以實現權限控制和數據校驗。
權限控制的思路是截獲對 page.* 和 api.* 的調用,並利用 session 中記錄的用戶角色信息進行權限檢查;
數據校驗將重用校驗代碼,在客戶端和服務器端對數據進行雙重檢查。
權限控制
這裡以對 page.* 的調用為例。基本思路是:
1. 通過正則表達式 /^page./ 和 /^api./ 匹配需要攔截的方法調用
2. 獲取參數中的 req (ServletHttpRequest)
3. 獲取 session 中的用戶角色
4. 如果用戶的角色是 admin ,則顯示相應頁面;否則顯示登陸頁面
Javascript代碼
(function() {
var log = panda.log("proxy.security");
proxy.security = { priority: 80 };
// 對 page.* 的調用進行權限控制
proxy.security.page = {
priority: 100,
expr: /^page./,
func: function(name, method, args) {
// 獲取方法的第二個參數,即 req
var req = args[1];
// 讀取 session 中的role。返回值是 java.lang.String
// 加上空字符串轉為 JavaScript 中的 String
var role = req.session.getAttribute("user.role") + "";
// 如果角色是 "admin",則顯示相應頁面
// 否則,顯示登錄頁面
if (role === "admin") {
return this[method].apply(this, args);
} else {
log.info("Redirect to login page.");
return panda.render("login");
}
}
}
// 利用類似的方法對 api.* 的調用進行權限控制,略
proxy.security.api = { … }
}());
簡單起見,這裡僅包含瞭 admin 一種角色。
除此之外,還需要創建文件 webapp/login.html (登陸頁面) webspp/js/login.js (向服務器發送用戶名和密碼的客戶端 JS ) 和 scripts/api/auth.js(登錄用戶名和密碼驗證),具體內容請查看附件中的相應文件。
啟動 mongod 和 PandaJS 工程(見附件),輸入http://localhost/,將顯示登錄頁面,在控制臺輸出(或日志)中也可以看到“Redirect to login page.”的提示。
輸入用戶名和密碼並點擊 Sign in 之後,將顯示用戶列表。
數據校驗
首先編寫在服務器端和瀏覽器中共用的 validator 對象:
webapp/js/both/validator.js
Javascript代碼
validator = {};
// 校驗異常信息
validator.USER_INVALID = "Invalid user data.";
validator.USER_NAME_EMPTY = "Name cannot be empty.";
validator.USER_NAME_TOO_LONG = "Name cannot be longer than 50.";
validator.USER_NAME_FORMAT = "Name format is not conrrect.";
validator.USER_DESC_EMPTY = "Description cannot be empty.";
validator.USER_DESC_TOO_LONG = "Description cannot be longer than 50.";
// 檢查 user 對象的方法
validator.validateUser = function(user) {
// 參數類型錯誤,可能是惡意攻擊
if (typeof user.name !== "string"
|| typeof user.desc !== "string") {
return { success: false, error: validator.USER_INVALID };
}
// name 為空
if (!user.name) {
return { success: false, error: validator.USER_NAME_EMPTY };
}
// name 過長
if (user.name.length > 50) {
return { success: false, error: validator.USER_NAME_TOO_LONG };
}
// name 格式檢查
if (!/^[A-z][A-z0-9._]*$/.test(user.name)) {
return { success: false, error: validator.USER_NAME_FORMAT };
}
// desc 為空
if (!user.desc) {
return { success: false, error: validator.USER_DESC_EMPTY };
}
// desc 過長
if (user.desc.length > 50) {
return { success: false, error: validator.USER_DESC_TOO_LONG };
}
// 提取 name 和 desc;因為對象中可能還有其他不需要的屬性
var data = { name: user.name, desc: user.desc }
return { success: true, data: data };
}
這段代碼在服務器端的 proxy.validation 對象 和 瀏覽器端的 save(…) 中被用到。
客戶端的校驗是為瞭給用戶更快的反饋,服務器端的校驗是為瞭避免惡意攻擊。
代碼實現如下:
scripts/app/proxy/validation.js
Javascript代碼
(function() {
var log = panda.log("proxy.validation");
proxy.validation = { priority: 60 };
// 創建或更新 user 時,檢查 user 數據
proxy.validation.saveUser = {
priority: 100,
expr: /^dbo.users.(add|update)$/,
func: function(name, method, args) {
var validated = validator.validateUser(args[0]);
if (!validated.success) {
log.info(validated.error);
throw validated.error;
}
args[0] = validated.data;
return this[method].apply(this, args);
}
};
// 創建 user 時,檢查用戶是否已經存在
proxy.validation.addUser = {
priority: 80,
expr: /^dbo.users.add$/,
func: function(name, method, args) {
var user = args[0];
if (this.exists(user.name)) {
var msg = "The user already exists.";
log.info(msg);
throw msg;
}
return this[method].apply(this, args);
}
};
}());
webapp/js/index.js
Javascript代碼
$(function(){
// 其他代碼,略
function save(action, user) {
// 驗證 user 數據有效性
var validated = validator.validateUser(user);
if (!validated.success) {
$("#error").html(validated.error).show();
return;
}
// 向服務器端發送請求
var req = { action: action, params: validated.data };
panda.post(req, show, function(error){
$("#error").html(error).show();
});
}
function show(users) { … }
});
可以看到,檢查輸入是否為空和檢查輸入參數長度、格式的部分是共用的,但檢查用戶是否存在的邏輯隻存在於服務器端的 proxy 對象。
另外,proxy 對象的 func 中 的 this 表示的是被截獲的對象,因此我們可以在 proxy.validation.saveUser 中調用 dbo.exists(name) 來檢查用戶是否已經存在。
此外,還需要實現 dao.users.exists(name) 方法,並在 index.html 中添加對 webapp/js/both/validator.js 的引用,詳細內容請查看附件中的相應文件。
小結
1. 我們可以利用 proxy 對象攔截方法的調用,進行權限和數據的檢查
2. 我們可以將服務器和瀏覽器端共用的代碼放在 both 目錄下
3. proxy 對象的 func 中 的 this 表示的是被截獲的對象
本文出自“裴小星的博客”