4.3?权限验证模块设计
一个好的权限管理机制在项目中应用时,最好不要让程序员在具体业务代码的方法中来判断用户权限。因为这意味着大量重复的代码。同时,也会导致权限机制的修改造成所有业务代码都需要修改一遍。
最好办法是实现与具体业务代码无关的独立的权限验证模块。这个模块可以拦截用户对资源的访问请求,并且在该请求被实施前做出权限判断,将权限不符的访问导向警告或提示页面。
?
???????在普通的JSP项目中,我们往往会利用Tomcat的Servlet filter机制来实现这样的功能,但filter机制的颗粒度不够,无法做到ACTION级别。另一个问题是,在JSF项目中,页面的跳转默认使用的是Forward形式,而不是redirect形式的URL重定向,因此Filter往往无法截获页面的转向。
?
???????因此,在JSF项目中我们首先需要解决如何截获到用户对资源的访问的问题。
4.3.1?捕获URL级别的资源访问
在JSF中,我们有时候会使用PhaseListener观察JSF生命周期,这给我们一个思路,通过PhaseListener?可以在一个点上(JSF生命周期的Restore View phase阶段)来获取请求和输出路径信息(这个路径实际上就是我们定义的URL类型的资源)。路径获取后我们就可以据此进行鉴权,从而避免在每个页面或者每个backing bean中判断用户访问权限,造成过多的冗余代码和管理上的混乱。
?
步骤一:在faces-config.xml中注册?自定义的PhaseListener。
<lifecycle>??????<phase-listener>com.laomao.view.listener.PermissionPhaseListener</phase-listener> </lifecycle> |
?
步骤二:实现我们自己的PhaseListener接口
package com.laomao.view.listener; import?javax.el.MethodExpression; import?javax.faces.FacesException; import?javax.faces.application.Application; import?javax.faces.application.NavigationHandler; import?javax.faces.context.FacesContext; import?javax.faces.event.PhaseEvent; import?javax.faces.event.PhaseId; import?javax.faces.event.PhaseListener; import?org.apache.commons.logging.Log; import?org.apache.commons.logging.LogFactory; ? public?class?PermissionPhaseListener?implements?PhaseListener?{ ??? ????private?static?Log?log?= LogFactory.getLog(PermissionPhaseListener.class); ??? ????@Override ????public?void?afterPhase(PhaseEvent event) { ????????PhaseId phaseId = event.getPhaseId(); ????????if?(phaseId == PhaseId.RESTORE_VIEW?|| phaseId == PhaseId.INVOKE_APPLICATION) { ????????????FacesContext currentContext = FacesContext.getCurrentInstance(); ????????????String viewId= currentContext.getViewRoot().getViewId(); ????????????//ViewId?就是我们需要的路径,类似URL,如/noPermission.xhtml ????????????System.out.println("afterPhase: "?+ viewId); ??????????? ????????????//判断用户的操作权限 ????????????boolean?isEnabled =?true; ????????????//TODO:?鉴权代码 ????????????//… ??????????? ????????????if(!isEnabled){ ??????????????gotoNoPermissionPage(); ????????????} ????????} ????} ??? ????/** ?????*?转入无权限声明页面 ?????*?@param?expression ?????*/ ????private?void?gotoNoPermissionPage() { ????????FacesContext context = FacesContext.getCurrentInstance(); ????????Application application = context.getApplication(); ??????????????? ????????NavigationHandler navHandler = application.getNavigationHandler(); ????????navHandler.handleNavigation(context,?null, Constants.NAVIGATION_NO_PERMISSION); ????????context.renderResponse(); ????} } ? |
?
?????????PhaseListener可以捕获到URL的变化,对于页面内部的操作则无能为力了。例如CommandButton的操作,可能删除了一条记录,但URL却没有变化,仅仅是页面局部进行了刷新。因此,我们还需要一个可以捕获到用户对ACTION类型的资源的访问的利器。这个利器就是ActionListener。
4.3.2?捕获ACTION级别的资源访问
当一个按钮被按下,或者命令联结被点击时,JSF会在invork application阶段的broadcast
event时通过ActionListener调用事先绑定的事件处理方法。如果有用户自定义的ActionListener时,JSF将使用用户自定义ActionListener来代替默认的ActionListener。
?
用户自定义ActionListener必须实现javax.faces.event.ActionListener接口,并实现其接口方法:public void processAction(ActionEvent actionEvent)。
?
注意,在这个实现方法里,必须调用控件的事件处理方法(JSF Backing Bean中的事件处理方法),并对其返回值做出画面迁移处理和其他你自己的处理等。
?
步骤一:在faces-config.xml中注册?自定义的ActionListener。
<application>???????<action-listener>com.laomao.view.listener.ActionListenerImpl</action-listener> </application> |
?
步骤二:实现我们自己的ActionListener接口
package com.laomao.view.listener; import javax.el.ELContext; import javax.el.MethodExpression; import javax.faces.FacesException; import javax.faces.application.Application; import javax.faces.application.NavigationHandler; import javax.faces.component.ActionSource2; import javax.faces.context.FacesContext; import javax.faces.el.EvaluationException; import javax.faces.el.MethodBinding; import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import javax.faces.event.ActionListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; ? public class ActionListenerImpl implements ActionListener { ???????private static Log log = LogFactory.getLog(PermissionPhaseListener.class); ?????? ???????@Override ???????public void processAction(ActionEvent actionEvent) throws AbortProcessingException { ??????????????FacesContext facesContext = FacesContext.getCurrentInstance(); ????????Application application = facesContext.getApplication(); ? ????????ActionSource2 actionSource = (ActionSource2)actionEvent.getComponent(); ??????????????? ????????MethodBinding methodBinding = actionSource.getAction(); ? ????????String fromAction; ????????String outcome; ????????if (methodBinding == null) ????????{ ????????????fromAction = null; ????????????outcome = null; ????????} ????????else ????????{ ???????????????fromAction = methodBinding.getExpressionString(); ?????????????? ????????????try ????????????{ ????????????????outcome = (String) methodBinding.invoke(facesContext, null); ????????????????//fromAction与faces-config.xml中登记的是一致的 ????????????????//fromAction: #{loginController.loginAction},?outcome: success ????????????????System.out.println("fromAction: " + fromAction + ", outcome: " + outcome); ??????????????? ????????????????boolean isEnabled = true; ????????????????//TODO:?鉴权代码 ?????????????????//… ????????????????if(!isEnabled){ ???????????????????????//如果无此权限,转到权限不足页面 ???????????????????????gotoNoPermissionPage(actionSource.getActionExpression()); ????????????????} ??????????????? ????????????} ????????????catch (EvaluationException e) ????????????{ ????????????????Throwable cause = e.getCause(); ????????????????if (cause != null && cause instanceof AbortProcessingException) ????????????????{ ????????????????????throw (AbortProcessingException)cause; ????????????????} ????????????????else ????????????????{ ????????????????????throw new FacesException("Error calling action method of component with id " + actionEvent.getComponent().getClientId(facesContext), e); ????????????????} ????????????} ????????????catch (RuntimeException e) ????????????{ ????????????????throw new FacesException("Error calling action method of component with id " + actionEvent.getComponent().getClientId(facesContext), e); ????????????} ????????} ? ????????NavigationHandler navigationHandler = application.getNavigationHandler(); ????????navigationHandler.handleNavigation(facesContext,fromAction,outcome); ????????//Render Response if needed ????????facesContext.renderResponse(); ? ???????} ?????? ???????/** ????????*?转入无权限声明页面 ????????* @param expression ????????*/ ???????private void gotoNoPermissionPage(MethodExpression expression) { ????????FacesContext context = FacesContext.getCurrentInstance(); ????????Application application = context.getApplication(); ??????????????? ????????NavigationHandler navHandler = application.getNavigationHandler(); ????????navHandler.handleNavigation(context, null == expression ? null : expression.getExpressionString(), Constants.NAVIGATION_NO_PERMISSION); ????????context.renderResponse(); ????} ? } |
?
转:http://laomaowww.blog.163.com/blog/static/16600567320124751827427/