According to wikipedia, authorization is the function of specifying access rights to resources.
Authorization is important to protect sensitive against different users.
The simplest way is to provide the access right to the targeted fields/components directly
for readOnly access,
for viewable access,
but the above solution is NOT feasible if there are many pages and fields/components to be authorized;
NOT flexible, if there are access right changes, re-coding is required.
Another feasible way is through FaceletsViewHandler.
In JSF with Facelets view handling, we can actually place all the authorization controls to the FaceletsViewHandler as the control central.
Steps:
1. Create a table to store all the authorization settings with the following info.
2. extends the default FaceletsViewHandler
3. override createResponseWriter
3.1 retrieve authorization data
In this method, write a method to access to persistence layer to retrieve authorization data, and return the data in the form of Map, where componentId is the key, accessRight list is the value.
Map[ componentId, [access_type, access_right] ]
3.2 find the targeted component in viewRoot.
loop through the viewRoot to find the target component
3.3 apply the access right
with the above configuration and coding, the access right control is flexible and configurable with the record in the table.
Thus, in future if there are authorization right changes, the only things to do is simply configure the record in the table based on requirements.
Done!!
Authorization is important to protect sensitive against different users.
The simplest way is to provide the access right to the targeted fields/components directly
for readOnly access,
<h:outputText id="authorizedId" value="#{bean.authorizedValue}" readonly="#{bean.authorizedReadonly}" />
for viewable access,
<h:outputText id="authorizedId" value="#{bean.authorizedValue}" disabled="#{bean.authorizedDisabled}" />
but the above solution is NOT feasible if there are many pages and fields/components to be authorized;
NOT flexible, if there are access right changes, re-coding is required.
Another feasible way is through FaceletsViewHandler.
In JSF with Facelets view handling, we can actually place all the authorization controls to the FaceletsViewHandler as the control central.
Steps:
1. Create a table to store all the authorization settings with the following info.
view_id | component_id | role | access_type | access_right |
/mypage.xhtml | myId | admin|user|guest | readonly|rendered | true|false |
2. extends the default FaceletsViewHandler
package kian.authorization;
public class AuthorizationViewHandler extends com.sun.facelets.FaceletViewHandler {
............
............
}
3. override createResponseWriter
@Override
protected ResponseWriter createResponseWriter(FacesContext context)
throws IOException, FacesException {
// 3.1 retrieve authorization data by viewId from database
// 3.2 in the viewroot, find the target component by componentId
// 3.3 apply access right to target component.
}
3.1 retrieve authorization data
In this method, write a method to access to persistence layer to retrieve authorization data, and return the data in the form of Map, where componentId is the key, accessRight list is the value.
Map[ componentId, [access_type, access_right] ]
Map<String, List> accessRightMap = retrieveAuthorizationByViewIdAndRole( context.getViewRoot().getViewId(), userRole );
3.2 find the targeted component in viewRoot.
loop through the viewRoot to find the target component
public UIComponent findComponentById(UIComponent root, String id) {
UIComponent component = null;
for (int i = 0; i < root.getChildCount() && component == null; i++) {
UIComponent child = (UIComponent) root.getChildren().get(i);
component = findComponentById(child, id);
}
if (root.getId() != null) {
if (component == null && root.getId().equals(id)) {
component = root;
}
}
return component;
}
3.3 apply the access right
if( accessrightMap.contains( component.getId() ) ) {
List list = accessrightMap.get( component.getId() );
if( list.get(0).equals( "readonly" ) ) {
component.setReadonly( list.get(1) );
} else if( list.get(0).equals("rendered") ) {
component.setRendered( list.get(1) );
}
}
with the above configuration and coding, the access right control is flexible and configurable with the record in the table.
Thus, in future if there are authorization right changes, the only things to do is simply configure the record in the table based on requirements.
Done!!
this is one of the most interesting approaches for user authorization for JSF. I'd like to see if JSF 2 has made this easier, since FaceletsViewHandler seems to be deprecated :-(
ReplyDeleteactually, for JSF 2, it's just
ReplyDeleteimport java.io.IOException;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
public class NEASViewHandler extends ViewHandlerWrapper {
private ViewHandler defaultViewHandler = null;
public NEASViewHandler() {
}
public NEASViewHandler(ViewHandler defaultViewHandler) {
super();
this.defaultViewHandler = defaultViewHandler;
}
@Override
public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException {
System.out.println("Rendering " + viewToRender);
HttpServletRequest request =
(HttpServletRequest)context.getExternalContext().getRequest();
System.out.println("Request "+ request.getPathInfo());
viewToRender.visitTree(VisitContext.createVisitContext(context),
new VisitCallback() {
@Override
public VisitResult visit(VisitContext context, UIComponent target) {
// System.out.println("Visiting " + target);
return VisitResult.ACCEPT;
}
});
defaultViewHandler.renderView(context, viewToRender);
}
//
// private UIComponent findComponentById(UIComponent root, String id) {
// UIComponent component = null;
// for (int i = 0; i < root.getChildCount() && component == null; i++) {
// UIComponent child = (UIComponent) root.getChildren().get(i);
// component = findComponentById(child, id);
// }
// if (root.getId() != null) {
// if (component == null && root.getId().equals(id)) {
// component = root;
// }
// }
// return component;
// }
@Override
public ViewHandler getWrapped() {
return defaultViewHandler;
}
}
Thanks for sharing with me.
DeleteActually the concept is same,
instead of implement our authorization logic in createResponseWriter in FaceletViewHandler.
In JSF2, the implementation is in renderView in ViewHandlerWrapper.