本文是接續前三篇文章(一、二、三)而來的,主要在介紹如果使用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