权限管理组件的设计、开发思路

        应用软件中通常包含用户的权限管理功能,往往不同应用系统对权限管理功能的需求不同,有自己的权限模型,权限的授权、鉴权策略。权限管理是相对独立的一个功能模块,由于功能需求不同、采用模型设计、实现技术等的不同,往往给设计开发人员带来很大的负担,开发出来的东西往往耦合性大、不易扩展维护,给项目带来很大风险。为适应软件开发组件模块开发趋势,把它设计成通用、灵活复用嵌入应用系统的组件模块不是一件容易的事情。业界曾提供了许多通用权限管理的组件或解决方案。参照一些资源,以及我的一些开发项目经验,试图探索一些权限组件设计开发的一些思路,也希望能得到阅读本文的意见和建议,持续改进完善它。现在我主要从以下方面介绍:1)权限模型;2)基础框架技术组件

权限模型

       包括授权模型、鉴权模型(也叫权限验证模型)。授权模型有DAC、MAC、ACL、RBAC。鉴权模型,给出一个图解如下

比较著名的授权模型是RBAC(基于角色的访问控制)。这个资料很多,不用详细说了。主要涉及的实体有用户users(USERS)‏ 、角色roles(ROLES)‏ 、目标objects(OBS)‏ 、操作operations(OPS)‏ 、许可权permissions(PRMS)‏ 。

权限技术组件选型

      基础框架的技术组件,比较著名的应该是Acegi Security(以下简称Acegi),它是一个能为基于Spring的企业应用提供强大而灵活安全访问控制解决方案的框架,Acegi已经成为Spring官方的一个子项目,所以也称为Spring Security。Acegi提供的权限管理方面功能可以说非常全面,但是它的设计比较细致、复杂,需要一定时间学习和掌握它的框架结构和使用原理,才能得到比较好应用。属于比较重量级的组件,如果权限方面的有更多特性化的业务需求,需要定制扩展该组件时,就需要非常熟悉掌握到一定程度,甚至需要修改扩展Acegi源码,如果不够熟悉组件架构原理,就会改不好,甚至引来一些不必要的麻烦。对于开源的组件,我个人建议是对于设计的重量级的组件,如果应用系统目前用到的功能没有那么全面,只用部分功能的话,就可以了解到它的架构机制原理后,把需要使用的部分功能从开源组件中抽取出来,整合舍弃一些不太用到的源码,简单化,封装成一个相对轻量级的组件,这样既可以满足应用系统的功能,也能充分利用开源组件的框架机理(毕竟开源组件是经过很多项目的检验,可以借鉴的,很多地方比我们个人考虑更成熟)。这样形成的轻量级组件植入我们的应用系统,变得更可控,更容易维护、灵活扩展。

      Acegi实现机制可以参看文档http://blog.csdn.net/yan_dk/article/details/7228167

总体来说,Acegi实现还是功能集成度高、复杂、重量级的。笔者建议还是把它拆分成不同的功能部分,根据需要分别来用不同组件实现比较好,比如认证处理我们可以采用SSO组件就可以,权限资源拦截我们可以自己编写权限资源访问匹配方法,这样比较灵活一些。下面谈一个权限资源拦截的一个设计、编码实例。

设计思路主要是:

1.一个单例(AuthorityContextSingleton):保存当前用户拥有的所有权限资源路径,用于在权限资源拦截时提供可用权限资源;

2.一个过滤器(AuthorityInterceptorFilter):对请求资源进行拦截,根据上述单例中保存的用户拥有的权限资源路径进行匹配,如果不能正确匹配,就属于无权访问资源,进行拒绝访问的处理。

范例代码如下:

/**
 * 权限资源上下文单例                                                                                                                                                                                                                                                       
 * @author yandk
 * @date Jan 30, 2012
 */
public class AuthorityContextSingleton {
 protected Log log = LogFactory.getLog(getClass());
 private LoginService loginService;
 private String authoritySwitch;//权限资源拦截开关,true启用;false不启用。


 public void setLoginService(LoginService loginService) {
  this.loginService = loginService;
 }
 public void setAuthoritySwitch(String authoritySwitch) {
  this.authoritySwitch = authoritySwitch;
 }

 public String getAuthoritySwitch() {
  return authoritySwitch;
 }


 private Map<String,List> mapSecurityResource=new HashMap<String,List>();//安全资源映射表,包含(操作员编码,安全资源路径)的键值对。
 
// private volatile static AuthorityContextSingleton authorityContextSingleton ;
 private AuthorityContextSingleton(){
  
 }
 
 synchronized public void setSecurityResource(Oper oper){
  List<String> list_funcPath= new ArrayList<String>();
  String operName = oper.getOperName();
//  判断当前用户是否是超级用户角色,若是则开放所有访问资源,*.do
  List<Role> list_role= oper.getRoleList();
  for (Role role : list_role) {
   String roleType = role.getRoleType();
   if( 是超级管理员角色){//管理员角色类型是超级管理员时,拥有所有权限
    list_funcPath.add("*.do");
    mapSecurityResource.put(operName , list_funcPath);
    return;
    
   }
  }
//  取得参数用户的权限资源
  list_funcPath=loginService.getFuncPath(oper);
//  将参数用户及其权限资源存入安全资源映射表
  mapSecurityResource.put(PubConstant.user_authrity_Key_prefix+operName, list_funcPath);
 }

 public Map<String, List> getMapSecurityResource() {
  return mapSecurityResource;
 }

注:本单例类通过spring注入上下文使用。

/**
 * 安全资源访问的拦截控制:用户访问系统时,拦截控制只能访问被授权的资源(功能路径),如果访问未授权资源,则提示拒绝访问                                                                                                                                                                                                                                            
 * @author yandk
 * @date Jan 29, 2012
 */
public class AuthorityInterceptorFilter implements Filter {
 protected Log log = LogFactory.getLog(getClass());
 
 public final static String USERDEFINE_FUNCPATH_INIT_PARAM = "userdefine.open.funcpath";//用户自定义的开放资源(url功能路径)
 private String userdefine_funcpath;//用户自定义的开放资源-值
 
 @Override
 public void init(FilterConfig config) throws ServletException {
  userdefine_funcpath = config.getInitParameter(USERDEFINE_FUNCPATH_INIT_PARAM);  
 }
 @Override
 public void doFilter(ServletRequest request,
   ServletResponse response, FilterChain chain) throws IOException, ServletException{
  HttpServletRequest httpRequest = (HttpServletRequest) request;
  HttpSession httpSession = ((HttpServletRequest) request).getSession();
  HttpServletResponse httpResponse = (HttpServletResponse)response;
    
  try {
    ServletContext servletContext = httpSession.getServletContext();   
       ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
    AuthorityContextSingleton authorityContextSingleton = (AuthorityContextSingleton)ctx.getBean("authorityContextSingleton");
    if("true".equals( authorityContextSingleton.getAuthoritySwitch())){//如果权限资源拦截开关未启用,则不拦截
   String requestPath = httpRequest.getRequestURI();
   String ls_requestPath = UrlUtils.buildFullRequestUrl(httpRequest.getScheme(), httpRequest.getServerName(), httpRequest.getServerPort(), requestPath, null);
   Oper oper = (Oper) httpSession.getAttribute("oper");
   if(oper!=null && userdefine_funcpath!=null){//在登录之后进行权限资源的拦截处理
//                String operCode =oper.getOperCode();
    String operName = oper.getOperName();
//    判断请求路径是否与设置参数userdefine_funcpath相匹配,若匹配可以开放访问
    PathMatcher matcher = new AntPathMatcher();
    String userdefinePaths[] = null; 
    userdefinePaths = userdefine_funcpath.split(",");
    boolean flag;
    flag = matchRequestPath(matcher,ls_requestPath,userdefinePaths);
    if (flag) {//若匹配开放资源,则不拦截
     log.info(" AuthorityInterceptorFilter request path '"+ls_requestPath+"'is matched,filter chain will be continued."); 
    }else{
//       authorityContextSingleton.setSecurityResource(operCode);//aa
       Map<String,List> mapSecurityResource=authorityContextSingleton.getMapSecurityResource();
       List list_funcPath = mapSecurityResource.get(PubConstant.user_authrity_Key_prefix+operName);
       if(list_funcPath!=null&&!list_funcPath.isEmpty()){
      Object[] funcPaths = list_funcPath.toArray();
      boolean flag2;
      flag2= matchRequestPath(matcher,ls_requestPath,funcPaths);
      if(!flag2){
       httpResponse.sendRedirect(httpRequest.getContextPath()+"/accessDenied.jsp");
       return;
      }
     }  
    }
   }
   chain.doFilter(request, response);
    }
  } catch (Exception ex) {
   log.debug("SecurityResourceInterceptorFilter: " + ex.toString());
   ex.printStackTrace();
  }
 }
 
 private boolean matchRequestPath(PathMatcher matcher,String requestPath,Object[] resourcePaths){
  boolean flag=false;
  for (Object resourcePath : resourcePaths) {
    flag = matcher.match("**/"+(String)resourcePath, requestPath);
    if (flag) {
   flag=true;
   break;
    }
  }
  return flag;
 }

 @Override
 public void destroy() {
  // TODO Auto-generated method stub
  
 }

 

}

在web.xml文件中写入对过滤器组件的引用,如下

...

<!-- 安全资源拦截控制 --> 
 <filter>  
  <filter-name>authorityInterceptorFilter</filter-name>
  <filter-class>cn.ceopen.bss.pub.login.util.AuthorityInterceptorFilter</filter-class>
  <init-param>
   <param-name>userdefine.open.funcpath</param-name>
   <param-value>
   **/pub/login.do,**/pub/leftMenu.do
   </param-value>
  </init-param>
  
 </filter>
 <filter-mapping> 
  <filter-name>authorityInterceptorFilter</filter-name>  
  <url-pattern>*.do</url-pattern>
 </filter-mapping>

...

 

这样,在就可以实现对权限资源的拦截控制。


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云焰

你的鼓励是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值