通过SpringMVC+Annotation实现方法、按钮级别的细粒度权限控制

随着企业信息化的不断深入,各种各样的信息系统成为提高企业运营及管理效率的必备工具,越来越多的企业核心机密如销售机会、客户资料、设计方案等通过信息系统存储、备案、流转,这些核心资料一旦外泄,势必对企业造成极大损失。科技时代,信息是企业生存的命脉,信息的安全也必然成为企业极度重视的问题。如今,随着各种信息安全措施的实施,信息泄密已经从外部泄漏向内部人员泄漏转移。外部的黑客、病毒要想获取有价值的信息,必须穿透多道防火墙,逃避多重杀毒工具的追杀,再对信息进行筛选才能如愿以偿;而内部人员知道什么信息是有价值的,如果不对信息进行必要的安全防护,企业内部一些有有心人员会十分容易地获取自己所需要的信息资料。

最近的一份调查显示,几乎有一半的各行各业专业人士承认当他们跳槽时会带走资料,包括文件、销售协议和合同清单等各种资料,并将它们告诉下一个老板。调查还发现,八成的职员可以轻松地下载“有竞争力”的资料和信息,然后带到下一份工作中。

信息安全任重而道远。要保证信息系统的安全,需要考虑到很多方面如防火墙、加密传输、防SQL注入等,但很多的安全方案都是从如何把守大门着手的,如身份认证、数字证书,不管是传统的用户名加口令方式还是基于生物特征识别的指纹、视网膜扫描技术,乃至各类电子政务领域常用的USBkey都是在进入系统大门时大做文章,一旦身份识别完成进入大门后,却听之任之,很少再有处理方案。本文重点不在如何进行身份认证,而在身份认证完成后也即进入系统大门后,如何保证用户只在自己有权限的范围内进行操作,而不是可以进行任意功能的操作即系统内部细粒度权限控制解决方案。


常用的权限系统设计模式是以角色为核心的,即角色是具有相同权限的一类人员的集合:

1.     一个角色可以有包含多个操作人员,一个操作人员也可以属于多个角色

2.     一个角色可以具有多个功能的操作权限,一个功能也可以被多个角色所拥有。

在登录时通过查询登录用户所属角色,即可得到个用户的所有功能集合,如下图:



多数业务系统的页面功能菜单设计是以三级为标准的,即一级功能菜单、二级功能菜单、三级功能菜单,通常情况下一二级功能菜单只是用于功能分类,是不具有功能访问地址的,三级菜单才是功能的真正入口,常规权限系统就是通过控制每个人员对应的功能菜单的显示与隐藏来实现权限控制。要实现细粒度权限控制,可在设计功能表时再加入第四层:页面元素,隶属于第三层功能菜单,这些页面元素用来标识功能页面中的每一个功能按钮,如增加、修改、删除、查询都可算是页面元素,在为角色分配权限时,第四层也同样纳入统一权限管理,如果有此页面元素的权限,则页面上就显示该按钮,如果没有此页面元素的功能权限,则该按钮就不会显示出来。


对于没有权限访问的功能或页面除了进行前台的隐藏之外,还需要在后台访问时进行权限的验证,否则操作人员绕开页面直接通过输入URL访问功能就会造成权限漏洞,通过SpringMVC+Annotation的方式可以轻松实现,代码如下:

第一步:创建SpringMVC拦截器,拦截所有需要进行权限验证的功能请求

<!-- 开启注解 -->
	<mvc:annotation-driven/>
	
	
	<!-- 静态资源访问 -->
	 <mvc:resources location="/static/" mapping="/static/**"/> 
	  
	 <!-- 拦截器 -->
	  <mvc:interceptors>  
        <!-- 多个拦截器,顺序执行 -->  
        <mvc:interceptor>  
          <!-- 如果不配置或/**,将拦截所有的Controller -->
           <mvc:mapping path="/**" /> 
           <!-- 在Freemarker界面展示之前做一些通用处理   -->
           <bean class="xx.xxxx.core.web.FreeMarkerViewInterceptor"></bean>  
        </mvc:interceptor>  
    </mvc:interceptors>  


第二步:创建作用于Method级别的Annotation类,用于传入功能ID

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Permission {

	/**
	 * 功能ID,该功能ID,对应数据库中的功能ID
	 * @return
	 * @version V1.0.0
	 * @date Jan 13, 2014 4:59:35 PM
	 */
	String value();
	
}
第三步:通过静态常量建立数据库中的功能ID与执行方法的一对一关系

public class FuncConstants {

	/**
	 * 系统管理-角色管理-增加角色
	 */
	public final static String Xtgl_Jsgl_AddJs = "4399d98bb0d84114acb5693081e83bc9";
	/**
	 * 系统管理 - 部门管理- 部门列表
	 */
	public final static String Xtgl_Bmgl_BmList = "dbc4bf80f8b6418788b79de204d37932";
	
}

第四步:在SpringMVC拦截器中验证权限

/**
 * FreeMarker视图拦截器,页面展示之前做一些通用处理
 * @version V1.0.0
 * @date Dec 12, 2013 4:20:04 PM
 */
public class FreeMarkerViewInterceptor extends HandlerInterceptorAdapter {

	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
		
	}

	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView view) throws Exception {
		String contextPath = request.getContextPath();
		if (view != null) {
			request.setAttribute("base", contextPath);
		}
	}
  
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		
		//处理Permission Annotation,实现方法级权限控制
		HandlerMethod method = (HandlerMethod)handler;
		Permission permission = method.getMethodAnnotation(Permission.class);
		
		//如果为空在表示该方法不需要进行权限验证
		if (permission == null) {
			return true;
		}
		
		//验证是否具有权限
		if (!WebUtil.hasPower(request, permission.value())) {
			response.sendRedirect(request.getContextPath()+"/business/nopermission.html");
			return false;
		}
		return true;
		
		
		//注意此处必须返回true,否则请求将停止
		//return true;
	}

}

至此,基于按钮、方法验证的细粒度权限体系完成!



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。