本文是接續前三篇文章(一、二、三)而來的,主要在介紹如果使用Auth/Roles進行元件授權(authorization)的動作。
-- Authorization --
在Apache Wicket的Auth/Roles專案中,在身份認證(authentication)完成後必須要針對各項元件進行授權(authorization)的動作,以避免不具權限的使用者進入了受限的頁面。
基本上Auth/Roles專案是使用RoleAuthorizationStrategy(註1)讓Application得以使用Role來進行權限的設定。
權限設定的方式有二個,一個是使用Wicket Metadata;另一個是Annotation。
若要使用metadata的方式,必須使用org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy中的靜態方法authorize(.....),使用時的程式碼如下:
//方法一
//authorize(Class<T> componentClass, String roles)
//設定某角色能否初始化某元件
MetaDataRoleAuthorizationStrategy.authorize(RestrictedPage.class,"Role Name");
//方法二
//authorize(Component component, Action action, String roles)
//設定某角色能否針對某元件進行某項動作
//Action.ENABLE Action.RENDER
MetaDataRoleAuthorizationStrategy.authorize("html embed wicket id",Action.X,"Role Name");
-- 體驗範例程式 --
本文中要介紹的範例程式,將會使用上述二種方法來進行授權的管理(本範例程式不提供登入畫面,只使用固定的點選方式進行使用者的切換,因此Applicaion及Session均不使用先前範例中的Authentication方式)。
首先是使用者的部份,類別名稱為User.java,它是用來進行Model Mapping的,一般而言大多數的Model均需實作Serializable介面,但是這裡實作用的是Apache Wicket中自訂的Serializable稱為ICluserable(主要用途在於in-memory的處理)。
在此類別中,有二個member,一個是uid(使用者名稱)另一個是roles(org.apache.wicket.authroles.authorization.strategies.role.Roles,註2)。
//User.java
package com.myapp.wicket;
import org.apache.wicket.IClusterable;
import org.apache.wicket.authroles.authorization.strategies.role.Roles;
public class User implements IClusterable {
//使用者名稱
private final String uid;
//角色名稱
private final Roles roles;
public User(String uid, String roles) {
if (uid == null) {
throw new IllegalArgumentException("uid must be not null");
}
if (roles == null) {
throw new IllegalArgumentException("roles must be not null");
}
this.uid = uid;
this.roles = new Roles(roles);
}
//呼叫在Roles類別中的hasRole(..)methos
//若有該角色則回傳true
public boolean hasRole(String role) {
return roles.hasRole(role);
}
//呼叫在Roles類別中的hasAnyRole(..)methos
//若本使用者的角色符合任一個roles(參數)裡的角色就回傳true
public boolean hasAnyRole(Roles roles) {
return this.roles.hasAnyRole(roles);
}
public String getUid() {
return uid;
}
@Override
public String toString() {
return uid + " " + roles;
}
}
接下來看看WebSession的部份,和前幾個範例不同的是,不使用WebSession進行身份驗證的動作(使用固定連結的方式切換使用者),再將角色檢查(RoleChecking)的功能交給繼承IRoleCheckingStrategy的子類別UserRolesAuthorizer。
//mysession.java
package com.myapp.wicket;
import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.request.Request;
public class mysession extends WebSession {
//目前的使用者
private User user = Application.USERS.get(0);
public mysession(Request request) {
super(request);
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
在Application的部份,同樣的不繼承AuthenticatedWebApplication類別,只在init()中註冊授權策略,並使用角色授權策略。同時使用了meta data的方式設定AdminPage的授權。
//Application.java
package com.myapp.wicket;
import java.util.Arrays;
import java.util.List;
import org.apache.wicket.Session;
import org.apache.wicket.authroles.authorization.strategies.role.RoleAuthorizationStrategy;
import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Response;
public class Application extends WebApplication {
//固定只有3個使用者,最後一個使用者並沒有傳入角色名稱
public static List<User> USERS = Arrays.asList(new User("jon", "ADMIN"),
new User("kay", "USER"), new User("pam", ""));
public Application() {
super();
}
@Override
public Class getHomePage() {
return HomePage.class;
}
@Override
public Session newSession(Request request, Response response) {
return new mysession(request);
}
@Override
protected void init() {
super.init();
this.getMarkupSettings().setDefaultMarkupEncoding("UTF-8");
//註冊授權策略
//使用角色授權策略
getSecuritySettings().setAuthorizationStrategy(
new RoleAuthorizationStrategy(new UserRolesAuthorizer()));
//使用wicket metadata(html markup)的方式進行授權管理
MetaDataRoleAuthorizationStrategy.authorize(AdminPage.class, "ADMIN");
//使用annotation的方式進行授,就不需要以下這行
//MetaDataRoleAuthorizationStrategy.authorize(AdminAnnotationPage.class, "ADMIN");
}
}
當使用者登入成功(通過身份認證)後,必須要有一個類別來進行角色的檢查,UserRolesAuthorizer類別就是用來做角色檢查的。它必須繼承IRoleCheckingStrategy介面,並實作角色檢查的method(public boolean hasAnyRole(Roles roles) {...})。
//UserRolesAuthorizer.java
package com.myapp.wicket;
import org.apache.wicket.Session;
import org.apache.wicket.authroles.authorization.strategies.role.IRoleCheckingStrategy;
import org.apache.wicket.authroles.authorization.strategies.role.Roles;
public class UserRolesAuthorizer implements IRoleCheckingStrategy {
public UserRolesAuthorizer() {
}
//實作此method
//此部份可以更加複雜,例如只允許某使用者在9AM-4PM具有ADMIN角色
//當使用者通過身份認證後,在此進行角色檢查
@Override
public boolean hasAnyRole(Roles roles) {
//取得WebSession中已通過認證的User
//在此先用Roles中的hasAnyRole進行簡易檢查
mysession authSession = (mysession) Session.get();
return authSession.getUser().hasAnyRole(roles);
}
}
在本範例程式中,只有在首頁呈現兩個需要管理員身份才能進入的bookmarkable超連結,其中AdminPage.html使用meta data方式授權,而AdminAnnotationPage.html使用annotation方式進行授權。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"
xml:lang="en"
lang="en">
<head>
<wicket:head>
<title>Apache Wicket Auth/Roles -- Authorization</title>
</wicket:head>
</head>
<body>
<wicket:extend>
<div>
目前的使用者是:
<strong>
<span wicket:id="currentUser">
[user]
</span>
</strong>
<br />
變更使用者:
<ul>
<li wicket:id="users">
<a href="#" wicket:id="selectUserLink">
<span wicket:id="userId">
[user id]
</span>
</a>
</li>
</ul>
</div>
<br />
<div>
<a href="#" wicket:id="adminBookmarkableLink">
前往bookmarkable AdminPage.html<br/>(使用metadata方式授權)
</a>
</div>
<br />
<div>
<a href="#" wicket:id="adminAnnotBookmarkableLink">
前往bookmarkable AdminAnnotationPage.html<br/>(使用annotation方式授權)
</a>
</div>
<br />
</wicket:extend>
</body>
</html>
和HomePage.html相對應的HomePage.java程式。
//HomePage.java
package com.myapp.wicket;
import org.apache.wicket.Session;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
public class HomePage extends BasePage {
public HomePage() {
add(new Label("currentUser", new PropertyModel<User>(this, "session.user")));
add(new ListView<User>("users", Application.USERS) {
@Override
protected void populateItem(ListItem<User> item) {
final User user = item.getModelObject();
item.add(new Link("selectUserLink") {
@Override
public void onClick() {
mysession session = (mysession) Session.get();
session.setUser(user);
}
}.add(new Label("userId", new Model<User>(user))));
}
});
//連到使用meta data授權方式的AdminPagelhtml
add(new BookmarkablePageLink<Void>("adminBookmarkableLink", AdminPage.class));
//連到使用annotation授權方式的AdminAnnotationPagelhtml
add(new BookmarkablePageLink<Void>("adminAnnotBookmarkableLink",
AdminAnnotationPage.class));
}
}
只有ADMIN才能進入的AdminPage.html及其相對應的AdminPage.java(使用meta data方式授權,所在在AdminPage.java中沒有任何和授權相關的程式碼,它的設定已在Application#init()中指定)。
<wicket:extend>
這個頁面只有管理人員看得到,此頁面授權使用meta data方式處理
</wicket:extend>
//AdminPage.java
package com.myapp.wicket;
public final class AdminPage extends BasePage {
public AdminPage() {
super ();
}
}
只有ADMIN才能進入的AdminAnnotationPage.html及其相對應的AdminAnnotationPage.java(使用annotation方式授權)。
<wicket:extend>
本頁面只有管理員才看得到,使用annotation方式進行授權
</wicket:extend>
#AdminAnnotationPage.java
package com.myapp.wicket;
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
//本頁面使用annotation方式進行授權
//本語法指定只有ADMIN才能初始化本頁面
@AuthorizeInstantiation("ADMIN")
public final class AdminAnnotationPage extends BasePage {
public AdminAnnotationPage() {
super ();
}
}
本文先在此打住,下一篇文章將會指定頁面中各別元件的授權。
註1:
RoleAuthorizationStrategy是Auth/Roles專案中,實作IAuthorizationStrategy的類別。RoleAuthorizationStrategy的建構子必須傳入一個實作IRoleCheckingStrategy的子類別。
註2:
org.apache.wicket.authroles.authorization.strategies.role.Roles裡的角色名稱是以逗號(common)來分隔的,例如"ADMIN,USER"。
No comments:
Post a Comment