| 知識清單1.了解基于資源的權(quán)限管理方式 2. 掌握權(quán)限數(shù)據(jù)模型 3. 掌握基于url的權(quán)限管理(不使用Shiro權(quán)限框架的情況下實現(xiàn)權(quán)限管理) 4. shiro實現(xiàn)用戶認證 5. shiro實現(xiàn)用戶授權(quán) 6. shiro與企業(yè)web項目整合開發(fā)的方法 權(quán)限管理原理知識什么是權(quán)限管理只要有用戶參與的系統(tǒng)一般都要有權(quán)限管理,權(quán)限管理實現(xiàn)對用戶訪問系統(tǒng)的控制。按照安全規(guī)則或安全策略控制用戶可以訪問而且只能訪問自己被授權(quán)的資源。 權(quán)限管理包括用戶認證和用戶授權(quán)兩部分。 用戶認證用戶認證概念用戶認證—— 用戶去訪問系統(tǒng),系統(tǒng)需要驗證用戶身份的合法性。最常用的用戶身份認證方法:1.用戶密碼方式、2.指紋打卡機、3.基于證書的驗證方法。系統(tǒng)驗證用戶身份合法,用戶方可訪問系統(tǒng)的資源。 用戶認證流程關(guān)鍵對象subject:主體,理解為用戶,可能是程序,都要去訪問系統(tǒng)的資源,系統(tǒng)需要對subject進行身份認證。 principal:身份信息,通常是唯一的,一個主體可以有多個身份信息,但是只能有一個主身份信息(primary  principal)。 credential:憑證信息,可以是密碼、證書、指紋等。 總結(jié):主體在進行身份認證時需要提供身份信息和憑證信息。 用戶授權(quán)用戶授權(quán)概念用戶授權(quán),簡單理解為訪問控制,在用戶認證通過后,系統(tǒng)對用戶訪問資源進行控制,當用戶具有資源的訪問權(quán)限方可訪問。 授權(quán)流程其中橙色為授權(quán)流程 關(guān)鍵對象授權(quán)的過程可以理解為  who  對 what(which) 進行how操作 who:主體,即subject,subject在認證通過后,系統(tǒng)進行訪問控制。 what(which):資源(Resource) ,subject必須具備資源訪問權(quán)限才可以訪問該資源。資源包括很多方面比如:用戶列表頁面、商品修改菜單、商品id為001的商品信息。 資源分為資源類型和資源實例: 例如系統(tǒng)的用戶信息就是資源類型,相當于Java類。 系統(tǒng)中id為001的用戶就是資源實例,相當于new的Java對象。 how:權(quán)限/許可(permission),針對資源的權(quán)限或許可,subject必須具有permission方可訪問資源,如何訪問/操作需要定義permission,權(quán)限比如:用戶添加、用戶添加、商品刪除。 權(quán)限模型主體(賬號、密碼) 資源(資源名稱,訪問地址) 權(quán)限(權(quán)限名稱、資源id) 角色(角色名稱) 角色和權(quán)限關(guān)系(角色id、權(quán)限id) 如下圖: 通常企業(yè)開發(fā)中將資源和權(quán)限合并為一張權(quán)限表,如下: 資源(資源名稱、訪問地址) 權(quán)限(權(quán)限名稱、資源id) 合并為: 權(quán)限(權(quán)限名稱、資源名稱、資源訪問地址) 上圖被稱為權(quán)限管理的通用模型,不過在企業(yè)開發(fā)中根據(jù)系統(tǒng)自身特點還會對上圖進行修改,但是用戶、角色、權(quán)限、用戶角色關(guān)系、角色權(quán)限關(guān)系是必不可少的。 分配權(quán)限用戶需要分配相應的權(quán)限才可以訪問相應的資源。權(quán)限是對資源的操作許可。 通常給用戶分配資源權(quán)限需要將權(quán)限信息持久化,比如存儲在關(guān)系數(shù)據(jù)庫中。 把用戶信息、權(quán)限管理、用戶分配的權(quán)限信息寫入到數(shù)據(jù)庫(權(quán)限數(shù)據(jù)模型)。 權(quán)限控制(授權(quán)核心)基于角色的訪問控制RBAC (Role  based access  control) 基于角色的訪問控制 比如: 系統(tǒng)角色包括:部門經(jīng)理、總經(jīng)理...(角色針對用戶進行劃分) 系統(tǒng)中代碼實現(xiàn): //如果該user是部門經(jīng)理則可以訪問if中的代碼 if(user.getRole("部門經(jīng)理")){     // 系統(tǒng)資源內(nèi)容     // 用戶報表查看 } 問題: 角色是針對人進行劃分的,人作為用戶在系統(tǒng)中屬于活動內(nèi)容,如果該角色可以訪問的資源出現(xiàn)變更,則需要修改代碼,比如:需要變更為部門經(jīng)理和總經(jīng)理都可以進行用戶報表查看,代碼改為: 
if(user.getRole("部門經(jīng)理") || user.getRole("總經(jīng)理")){     // 系統(tǒng)資源內(nèi)容     // 用戶報表查看 } 由此可以發(fā)現(xiàn)基于角色的訪問控制是不利于系統(tǒng)維護的(可擴展性不強) 基于資源的訪問控制
RBAC (Resource  based  access control)  基于資源的訪問控制 資源在系統(tǒng)中是不變的,比如資源有:類中的方法,頁面中的按鈕 對資源的訪問需要具有permission權(quán)限,代碼可以寫為: 
if(user.hasPermission("用戶報表查看(權(quán)限標識符)")){     // 系統(tǒng)資源內(nèi)容     // 用戶報表查看 } 
上面的方法就可以解決用戶角色變更而不用修改上邊權(quán)限控制的代碼。 如果需要變更權(quán)限只需要在分配權(quán)限模塊去操作,給部門經(jīng)理或總經(jīng)理增加或解除權(quán)限 建議使用基于資源的訪問控制實現(xiàn)權(quán)限管理。 權(quán)限管理解決方案什么是粗粒度權(quán)限和細粒度權(quán)限?粗粒度權(quán)限管理,是對資源類型的管理,資源類型比如:菜單、url連接、用戶添加頁面、用戶信息、類方法、頁面中按鈕。 粗粒度權(quán)限管理比如:超級管理員可以訪問用戶添加頁面、用戶信息等全部頁面。 部門管理員可以訪問用戶信息頁面,包括頁面中所有按鈕。 
 細粒度的權(quán)限管理,對資源實例的權(quán)限管理。資源實例就是資源類型的具體化,比如:用戶id為001的修改連接,1110班的用戶信息、行政部的員工。 細粒度的權(quán)限管理就是數(shù)據(jù)級別的權(quán)限管理。 細粒度權(quán)限管理比如:部門經(jīng)理只可以訪問本部門的員工信息,用戶只可以看到自己的菜單,大區(qū)經(jīng)理只能查看本轄區(qū)的銷售訂單... 
 粗粒度和細粒度例子: 系統(tǒng)中有一個用戶查詢頁面,對用戶列表查詢分權(quán)限,如粗粒度管理,張三和李四都有用戶列表查詢的權(quán)限,張三和李四都可以訪問用戶列表查詢。 進一步進行細粒度的管理,張三(行政部)和李四(開發(fā)部)只可以查詢自己本部門的用戶信息,張三只能查看行政部的用戶信息,李四只能查詢開發(fā)部門的用戶信息。細粒度的權(quán)限管理就是數(shù)據(jù)級別的權(quán)限管理。 如何實現(xiàn)粗粒度和細粒度的權(quán)限管理如何實現(xiàn)粗粒度的權(quán)限管理? 粗粒度權(quán)限管理比較容易將權(quán)限管理代碼抽取出來在系統(tǒng)架構(gòu)級別統(tǒng)一管理。比如:通過SpringMVC的攔截器實現(xiàn)授權(quán)。 如何實現(xiàn)細粒度的權(quán)限管理? 對細粒度的權(quán)限管理在數(shù)據(jù)級別是沒有共性可言的,針對細粒度的權(quán)限管理就是系統(tǒng)業(yè)務邏輯的一部分,如果在業(yè)務層去處理相對簡單,如果將細粒度的權(quán)限管理統(tǒng)一在系統(tǒng)架構(gòu)級別去抽取,比較困難,即使進行了抽取,功能也可能存在擴展性不全的弊端。建議細粒度權(quán)限管理放在業(yè)務層去控制。比如:部門經(jīng)理只查詢本部門員工信息,在Service接口提供一個部門id的參數(shù),controller中根據(jù)當前用戶信息得到該用戶屬于哪個部門,調(diào)用service時將部門id傳入service,實現(xiàn)該用戶只查詢本部門的員工。 基于url攔截的方式實現(xiàn)基于url攔截的方式實現(xiàn)在實際開發(fā)中是比較常用的一種方式。 對于web系統(tǒng),通過filter過濾器實現(xiàn)url攔截,也可以通過SpringMVC的攔截器實現(xiàn)基于URL的攔截。 使用權(quán)限管理框架來實現(xiàn)對于粗粒度的權(quán)限管理,建議使用優(yōu)秀的權(quán)限管理框架進行實現(xiàn),節(jié)省開發(fā)成本,提高開發(fā)效率。 Shiro就是一個優(yōu)秀的權(quán)限管理框架。 基于URL的權(quán)限管理基于url的權(quán)限管理流程搭建環(huán)境數(shù)據(jù)庫MySQL數(shù)據(jù)庫中創(chuàng)建表:用戶表、角色表、權(quán)限表(實質(zhì)是權(quán)限和資源的結(jié)合)、用戶角色關(guān)系表、角色權(quán)限關(guān)系表 新建數(shù)據(jù)庫shiro, 為了節(jié)約測試時間,在SpringMVC+mybatis基礎之上進行整合(導入以前的基本數(shù)據(jù)),并導入權(quán)限數(shù)據(jù)如下: 有關(guān)權(quán)限的SQL腳本如下: shiro_sql_table.sql MySQL - 5.1.72-community : Database - shiro*********************************************************************/*!40101 SET NAMES utf8 */;/*!40101 SET SQL_MODE=''*/;/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;/*Table structure for table `sys_permission` */CREATE TABLE `sys_permission` (  `id` bigint(20) NOT NULL COMMENT '主鍵',  `name` varchar(128) NOT NULL COMMENT '資源名稱',  `type` varchar(32) NOT NULL COMMENT '資源類型:menu,button,',  `url` varchar(128) DEFAULT NULL COMMENT '訪問url地址',  `percode` varchar(128) DEFAULT NULL COMMENT '權(quán)限代碼字符串',  `parentid` bigint(20) DEFAULT NULL COMMENT '父結(jié)點id',  `parentids` varchar(128) DEFAULT NULL COMMENT '父結(jié)點id列表串',  `sortstring` varchar(128) DEFAULT NULL COMMENT '排序號',  `available` char(1) DEFAULT NULL COMMENT '是否可用,1:可用,0不可用',) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*Table structure for table `sys_role` */CREATE TABLE `sys_role` (  `id` varchar(36) NOT NULL,  `name` varchar(128) NOT NULL,  `available` char(1) DEFAULT NULL COMMENT '是否可用,1:可用,0不可用',) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*Table structure for table `sys_role_permission` */CREATE TABLE `sys_role_permission` (  `id` varchar(36) NOT NULL,  `sys_role_id` varchar(32) NOT NULL COMMENT '角色id',  `sys_permission_id` varchar(32) NOT NULL COMMENT '權(quán)限id',) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*Table structure for table `sys_user` */CREATE TABLE `sys_user` (  `id` varchar(36) NOT NULL COMMENT '主鍵',  `usercode` varchar(32) NOT NULL COMMENT '賬號',  `username` varchar(64) NOT NULL COMMENT '姓名',  `password` varchar(32) NOT NULL COMMENT '密碼',  `salt` varchar(64) DEFAULT NULL COMMENT '鹽',  `locked` char(1) DEFAULT NULL COMMENT '賬號是否鎖定,1:鎖定,0未鎖定',) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*Table structure for table `sys_user_role` */CREATE TABLE `sys_user_role` (  `id` varchar(36) NOT NULL,  `sys_user_id` varchar(32) NOT NULL,  `sys_role_id` varchar(32) NOT NULL,) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
 shiro_sql_table_data.sqlMySQL - 5.1.72-community : Database - shiro*********************************************************************/*!40101 SET NAMES utf8 */;/*!40101 SET SQL_MODE=''*/;/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;/*Data for the table `sys_permission` */insert  into `sys_permission`(`id`,`name`,`type`,`url`,`percode`,`parentid`,`parentids`,`sortstring`,`available`) values (1,'權(quán)限','','',NULL,0,'0/','0','1'),(11,'商品管理','menu','/item/queryItem.action',NULL,1,'0/1/','1.','1'),(12,'商品新增','permission','/item/add.action','item:create',11,'0/1/11/','','1'),(13,'商品修改','permission','/item/editItem.action','item:update',11,'0/1/11/','','1'),(14,'商品刪除','permission','','item:delete',11,'0/1/11/','','1'),(15,'商品查詢','permission','/item/queryItem.action','item:query',11,'0/1/15/',NULL,'1'),(21,'用戶管理','menu','/user/query.action','user:query',1,'0/1/','2.','1'),(22,'用戶新增','permission','','user:create',21,'0/1/21/','','1'),(23,'用戶修改','permission','','user:update',21,'0/1/21/','','1'),(24,'用戶刪除','permission','','user:delete',21,'0/1/21/','','1');/*Data for the table `sys_role` */insert  into `sys_role`(`id`,`name`,`available`) values 	('ebc8a441-c6f9-11e4-b137-0adc305c3f28','商品管理員','1'),	('ebc9d647-c6f9-11e4-b137-0adc305c3f28','用戶管理員','1');/*Data for the table `sys_role_permission` */insert  into `sys_role_permission`(`id`,`sys_role_id`,`sys_permission_id`) values 	('ebc8a441-c6f9-11e4-b137-0adc305c3f21','ebc8a441-c6f9-11e4-b137-0adc305c','12'),	('ebc8a441-c6f9-11e4-b137-0adc305c3f22','ebc8a441-c6f9-11e4-b137-0adc305c','11'),	('ebc8a441-c6f9-11e4-b137-0adc305c3f24','ebc9d647-c6f9-11e4-b137-0adc305c','21'),	('ebc8a441-c6f9-11e4-b137-0adc305c3f25','ebc8a441-c6f9-11e4-b137-0adc305c','15'),	('ebc9d647-c6f9-11e4-b137-0adc305c3f23','ebc9d647-c6f9-11e4-b137-0adc305c','22'),	('ebc9d647-c6f9-11e4-b137-0adc305c3f26','ebc8a441-c6f9-11e4-b137-0adc305c','13');/*Data for the table `sys_user` */insert  into `sys_user`(`id`,`usercode`,`username`,`password`,`salt`,`locked`) values 	('lisi','lisi','李四','bf07fd8bbc73b6f70b8319f2ebb87483','uiwueylm','0'),	('zhangsan','zhangsan','張三','cb571f7bd7a6f73ab004a70322b963d5','eteokues','0');/*Data for the table `sys_user_role` */insert  into `sys_user_role`(`id`,`sys_user_id`,`sys_role_id`) values 	('ebc8a441-c6f9-11e4-b137-0adc305c3f28','zhangsan','ebc8a441-c6f9-11e4-b137-0adc305c'),	('ebc9d647-c6f9-11e4-b137-0adc305c3f28','lisi','ebc9d647-c6f9-11e4-b137-0adc305c');/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
 查看對應權(quán)限模型的數(shù)據(jù)如下:sys_user  用戶表數(shù)據(jù) sys_role  角色表  
sys_permission 權(quán)限表sys_user_role  用戶角色關(guān)系表 sys_role_permission  角色權(quán)限關(guān)系表 開發(fā)環(huán)境JDK1.8 MyEclipse 技術(shù)架構(gòu):SpringMVC+Mybatis+jQuery easyUI 系統(tǒng)工程架構(gòu)系統(tǒng)登錄系統(tǒng)登錄相當于用戶身份認證,用戶登錄成功,要在Session中記錄用戶的身份信息。 操作流程: 用戶進入登錄頁面。 輸入用戶名和密碼進行登陸。 進行用戶名和密碼校驗。 如果校驗通過,在Session中記錄用戶身份信息。 用戶的身份信息創(chuàng)建專門類用于記錄用戶身份信息。  * 用戶身份信息,存入Session  由于Tomcat正常關(guān)閉時會將Session序列化的本地硬盤上,所以實現(xiàn)Serializable接口public class ActiveUser implements Serializable {	private String userid; //用戶id(主鍵)	private String usercode; // 用戶賬號	private String username; // 用戶姓名
 mapper
mapper接口:根據(jù)用戶賬號查詢用戶(sys_user)信息 (使用逆向工程生成權(quán)限相關(guān)的PO類和mapper接口)如下所示: 將生成的代碼拷貝到項目中 service(進行用戶名和密碼校驗)接口功能:根據(jù)用戶的身份和密碼進行認證,如果認證通過,返回用戶身份信息。 認證過程: 根據(jù)用戶身份(賬號)查詢數(shù)據(jù)庫,如果查詢不到 則拋出用戶不存在 對輸入的密碼和數(shù)據(jù)庫密碼進行比對,如果一致,認證通過。 新建權(quán)限管理Service接口 添加身份認證方法 public interface SysService {	//根據(jù)用戶的身份和密碼進行認證,如果認證通過,返回用戶身份信息	public ActiveUser authenticat(String usercode,String password) throws Exception;	public SysUser findSysUserByUserCode(String userCode) throws Exception;
 方法實現(xiàn):public class SysServiceImpl implements SysService {	private SysUserMapper sysUserMapper;	public ActiveUser authenticat(String usercode, String password) throws Exception {		 * 認證過程: 根據(jù)用戶身份(賬號)查詢數(shù)據(jù)庫,如果查詢不到則用戶不存在 		 * 對輸入的密碼和數(shù)據(jù)庫密碼進行比對,如果一致則認證通過		// 根據(jù)用戶賬號查詢數(shù)據(jù)庫		SysUser sysUser = this.findSysUserByUserCode(usercode);			throw new CustomException("用戶賬號不存在");		// 數(shù)據(jù)庫密碼(MD5加密后的密碼)		String password_db = sysUser.getPassword();		// 對輸入的密碼和數(shù)據(jù)庫密碼進行比對,如果一致,認證通過		String password_input_md5 = new MD5().getMD5ofStr(password);		if (!password_db.equalsIgnoreCase(password_input_md5)) {			throw new CustomException("用戶名或密碼錯誤");		String userid = sysUser.getId();		ActiveUser activeUser = new ActiveUser();		activeUser.setUserid(userid);		activeUser.setUsercode(usercode);		activeUser.setUsername(sysUser.getUsername());	public SysUser findSysUserByUserCode(String userCode) throws Exception {		SysUserExample sysUserExample = new SysUserExample();		SysUserExample.Criteria criteria = sysUserExample.createCriteria();		criteria.andUsercodeEqualTo(userCode);		List<SysUser> list = sysUserMapper.selectByExample(sysUserExample);		if (list != null && list.size() > 0) {
 配置Service,往類Service中使用@Autowire 需要注冊Service 注冊有兩種方法(注解或配置文件),在架構(gòu)時沒有配置掃描Service  需要在配置文件中注冊Service<!-- 認證和授權(quán)的Service -->   <bean id="sysService" class="liuxun.ssm.service.impl.SysServiceImpl"></bean>
 controller(記錄Session)	@RequestMapping("/login")	public String login(HttpSession session,String randomcode,String usercode,String password) throws Exception{		String validateCode = (String) session.getAttribute("validateCode");		//輸入的驗證碼和Session中的驗證碼進行對比		if (!randomcode.equalsIgnoreCase(validateCode)) {			throw new CustomException("驗證碼輸入錯誤");		//調(diào)用Service校驗用戶賬號和密碼的正確性		ActiveUser activeUser = sysService.authenticat(usercode, password);		//如果Service校驗通過,將用戶身份記錄到Session		session.setAttribute("activeUser", activeUser);		return "redirect:/first.action";
 用戶認證攔截器anonymousURL.properties配置匿名URL配置可以匿名訪問的URL 
 編寫身份認證攔截器	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {		String url = request.getRequestURI();		//實際開發(fā)中需要將公開地址配置在配置文件中		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");		for (String open_url : open_urls) {			if (url.indexOf(open_url)>=0) {		HttpSession session = request.getSession();		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");		//執(zhí)行到這里攔截,跳轉(zhuǎn)到登錄頁面,用戶進行身份認證		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);		//如果返回false表示攔截器不繼續(xù)執(zhí)行handler,如果返回true表示放行
 配置認證攔截器		   	<mvc:mapping path="/**"/>		   	<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>
用戶授權(quán)commonURL.properties配置公用訪問地址在此配置文件中配置公用訪問地址,公用訪問地址只需要通過用戶認證,不需要對公用訪問地址分配權(quán)限即可訪問。 獲取用戶權(quán)限范圍的菜單思路: 在用戶認證時,認證通過,根據(jù)用戶id從數(shù)據(jù)庫獲取用戶權(quán)限范圍內(nèi)的菜單,將菜單的集合存儲在Session中。 編輯存儲用戶身份信息的類ActiveUser 如下所示: public class ActiveUser implements Serializable {	private String userid; //用戶id(主鍵)	private String usercode; // 用戶賬號	private String username; // 用戶姓名	private List<SysPermission> menus; //菜單
 自定義權(quán)限Mapper因為使用逆向工程生成的Mapper是不建議去修改的 因為它的代碼聯(lián)系非常緊密,一旦修改錯誤 就會牽一發(fā)而動全身。所以需要自定義一個權(quán)限的Mapper(SysPermissionMapperCustom) 在SysPermissionMapperCustom.xml中添加根據(jù)用戶id查詢用戶權(quán)限的菜單 <select id="findMenuListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">	    WHERE sys_user_id = #{userid}))
 在SysPermissionMapperCustom.java接口中添加對應的方法public interface SysPermissionMapperCustom {	public List<SysPermission> findMenuListByUserId(String userid) throws Exception;
 在權(quán)限Service接口中添加對應的方法 在實現(xiàn)中注入SysPermissionMapperCustomSysServiceImpl.java中添加如下內(nèi)容 	public List<SysPermission> findMenuListByUserId(String userid) throws Exception {		return sysPermissionMapperCustom.findMenuListByUserId(userid);
 獲取用戶權(quán)限范圍的URL思路:在用戶認證時,認證通過后,根據(jù)用戶id從數(shù)據(jù)庫中獲取用戶權(quán)限范圍的URL,將URL的集合存儲在Session中。 修改ActiveUser 添加URL的權(quán)限集合 public class ActiveUser implements Serializable {	private String userid; //用戶id(主鍵)	private String usercode; // 用戶賬號	private String username; // 用戶姓名	private List<SysPermission> menus; //菜單	private List<SysPermission> permissions; //權(quán)限
 在SysPermissionMapperCustom.xml中添加根據(jù)用戶id查詢用戶權(quán)限的URL<select id="findPermissionListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">	WHERE TYPE = 'permission' 	    WHERE sys_user_id = #{userid}))
 在SysPermissionMapperCustom.java接口中添加對應的方法 //根據(jù)用戶id查詢權(quán)限URL	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception;
 SysServiceImpl.java中添加如下內(nèi)容 	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception {		return sysPermissionMapperCustom.findPermissionListByUserId(userid);
 用戶認證通過后取出菜單和URL放入Session
修改權(quán)限SysServiceImpl中用戶認證方法的代碼		String userid = sysUser.getId();		List<SysPermission> menus = this.findMenuListByUserId(userid);		//根據(jù)用戶id查詢權(quán)限url		List<SysPermission> permissions = this.findPermissionListByUserId(userid);		ActiveUser activeUser = new ActiveUser();		activeUser.setUserid(userid);		activeUser.setUsercode(usercode);		activeUser.setUsername(sysUser.getUsername());		activeUser.setMenus(menus);		activeUser.setPermissions(permissions);
 菜單動態(tài)顯示<c:if test="${activeUser.menus!=null }">				<c:forEach items="${activeUser.menus }" var="menu">						<a title="${menu.name }" ref="1_1" href="#"							rel="${baseurl }/${menu.url }" icon="icon-log"><span							class="icon icon-log"> </span><span class="nav"><a href=javascript:addTab('${menu.name }','${baseurl }/${menu.url }')>${menu.name }</a></span></a>
 授權(quán)攔截器public class PermissionInterceptor implements HandlerInterceptor{	//在執(zhí)行handler之前執(zhí)行的	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {		String url = request.getRequestURI();		//實際開發(fā)中需要將公開地址配置在配置文件中		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");		for (String open_url : open_urls) {			if (url.indexOf(open_url)>=0) {		List<String> common_urls = ResourcesUtil.getKeyList("commonURL");		for (String common_url : common_urls) {			if (url.indexOf(common_url)>0) {		HttpSession session = request.getSession();		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");		//從Session中取出權(quán)限范圍的URL		List<SysPermission> permissions = activeUser.getPermissions();		for (SysPermission sysPermission : permissions) {			String permission_url = sysPermission.getUrl();			if (url.indexOf(permission_url)>0) {		//執(zhí)行到這里攔截,跳轉(zhuǎn)到無權(quán)訪問的提示頁面		request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);		//如果返回false表示攔截器不繼續(xù)執(zhí)行handler,如果返回true表示放行
配置授權(quán)攔截器
注意:要將授權(quán)攔截器配置在用戶認證攔截器的下邊,這是因為SpringMVC攔截器的放行方法是順序執(zhí)行的,如果是Struts的話則正好相反。		   	<mvc:mapping path="/**"/>		   	<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>	    	<mvc:mapping path="/**"/>	    	<bean class="liuxun.ssm.controller.interceptor.PermissionInterceptor"></bean>
 運行測試:其關(guān)鍵代碼如下: PO類ActiveUser.java 存放用戶身份和權(quán)限信息的類 import java.io.Serializable; * 用戶身份信息,存入Session  由于Tomcat正常關(guān)閉時會將Session序列化的本地硬盤上,所以實現(xiàn)Serializable接口public class ActiveUser implements Serializable {	private static final long serialVersionUID = 1L;	private String userid; //用戶id(主鍵)	private String usercode; // 用戶賬號	private String username; // 用戶姓名	private List<SysPermission> menus; //菜單	private List<SysPermission> permissions; //權(quán)限
 自定義權(quán)限的Mapper SysPermissionMapperCustom.java package liuxun.ssm.mapper;import liuxun.ssm.po.SysPermission;import liuxun.ssm.po.SysPermissionExample;import org.apache.ibatis.annotations.Param;public interface SysPermissionMapperCustom {	public List<SysPermission> findMenuListByUserId(String userid) throws Exception;	//根據(jù)用戶id查詢權(quán)限URL	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception;
 SysPermissionMapperCustom.xml<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "http:///dtd/mybatis-3-mapper.dtd" ><mapper namespace="liuxun.ssm.mapper.SysPermissionMapperCustom"><select id="findMenuListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">	    WHERE sys_user_id = #{userid}))<select id="findPermissionListByUserId" parameterType="string" resultType="liuxun.ssm.po.SysPermission">	WHERE TYPE = 'permission' 	    WHERE sys_user_id = #{userid}))
 自定義權(quán)限的Service接口以及實現(xiàn)類SysService.java SysServiceImpl.javapackage liuxun.ssm.service;import liuxun.ssm.po.ActiveUser;import liuxun.ssm.po.SysPermission;import liuxun.ssm.po.SysUser;public interface SysService {	//根據(jù)用戶的身份和密碼進行認證,如果認證通過,返回用戶身份信息	public ActiveUser authenticat(String usercode,String password) throws Exception;	public SysUser findSysUserByUserCode(String userCode) throws Exception;	//根據(jù)用戶id查詢權(quán)限范圍內(nèi)的菜單	public List<SysPermission> findMenuListByUserId(String userid) throws Exception;	//根據(jù)用戶id查詢權(quán)限范圍內(nèi)的url	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception;
package liuxun.ssm.service.impl;import org.springframework.beans.factory.annotation.Autowired;import liuxun.ssm.exception.CustomException;import liuxun.ssm.mapper.SysPermissionMapperCustom;import liuxun.ssm.mapper.SysUserMapper;import liuxun.ssm.po.ActiveUser;import liuxun.ssm.po.SysPermission;import liuxun.ssm.po.SysUser;import liuxun.ssm.po.SysUserExample;import liuxun.ssm.service.SysService;import liuxun.ssm.util.MD5;public class SysServiceImpl implements SysService {	private SysUserMapper sysUserMapper;	private SysPermissionMapperCustom sysPermissionMapperCustom;	public ActiveUser authenticat(String usercode, String password) throws Exception {		 * 認證過程: 根據(jù)用戶身份(賬號)查詢數(shù)據(jù)庫,如果查詢不到則用戶不存在 		 * 對輸入的密碼和數(shù)據(jù)庫密碼進行比對,如果一致則認證通過		// 根據(jù)用戶賬號查詢數(shù)據(jù)庫		SysUser sysUser = this.findSysUserByUserCode(usercode);			throw new CustomException("用戶賬號不存在");		// 數(shù)據(jù)庫密碼(MD5加密后的密碼)		String password_db = sysUser.getPassword();		// 對輸入的密碼和數(shù)據(jù)庫密碼進行比對,如果一致,認證通過		String password_input_md5 = new MD5().getMD5ofStr(password);		if (!password_db.equalsIgnoreCase(password_input_md5)) {			throw new CustomException("用戶名或密碼錯誤");		String userid = sysUser.getId();		List<SysPermission> menus = this.findMenuListByUserId(userid);		//根據(jù)用戶id查詢權(quán)限url		List<SysPermission> permissions = this.findPermissionListByUserId(userid);		ActiveUser activeUser = new ActiveUser();		activeUser.setUserid(userid);		activeUser.setUsercode(usercode);		activeUser.setUsername(sysUser.getUsername());		activeUser.setMenus(menus);		activeUser.setPermissions(permissions);	public SysUser findSysUserByUserCode(String userCode) throws Exception {		SysUserExample sysUserExample = new SysUserExample();		SysUserExample.Criteria criteria = sysUserExample.createCriteria();		criteria.andUsercodeEqualTo(userCode);		List<SysUser> list = sysUserMapper.selectByExample(sysUserExample);		if (list != null && list.size() > 0) {	public List<SysPermission> findMenuListByUserId(String userid) throws Exception {		return sysPermissionMapperCustom.findMenuListByUserId(userid);	public List<SysPermission> findPermissionListByUserId(String userid) throws Exception {		return sysPermissionMapperCustom.findPermissionListByUserId(userid);
 登錄控制器package liuxun.ssm.controller;import javax.servlet.http.HttpSession;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import liuxun.ssm.exception.CustomException;import liuxun.ssm.po.ActiveUser;import liuxun.ssm.service.SysService;public class LoginController {    private SysService sysService;	@RequestMapping("/login")	public String login(HttpSession session,String randomcode,String usercode,String password) throws Exception{		String validateCode = (String) session.getAttribute("validateCode");		//輸入的驗證碼和Session中的驗證碼進行對比		if (!randomcode.equalsIgnoreCase(validateCode)) {			throw new CustomException("驗證碼輸入錯誤");		//調(diào)用Service校驗用戶賬號和密碼的正確性		ActiveUser activeUser = sysService.authenticat(usercode, password);		//如果Service校驗通過,將用戶身份記錄到Session		session.setAttribute("activeUser", activeUser);		return "redirect:/first.action";	@RequestMapping("/logout")	public String logout(HttpSession session) throws Exception{		return "redirect:/first.action";
 身份認證攔截器LoginInterceptor.javapackage liuxun.ssm.controller.interceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import liuxun.ssm.po.ActiveUser;import liuxun.ssm.util.ResourcesUtil;public class LoginInterceptor implements HandlerInterceptor{	//在執(zhí)行handler之前執(zhí)行的	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {		String url = request.getRequestURI();		//實際開發(fā)中需要將公開地址配置在配置文件中		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");		for (String open_url : open_urls) {			if (url.indexOf(open_url)>=0) {		HttpSession session = request.getSession();		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");		//執(zhí)行到這里攔截,跳轉(zhuǎn)到登錄頁面,用戶進行身份認證		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);		//如果返回false表示攔截器不繼續(xù)執(zhí)行handler,如果返回true表示放行	//在執(zhí)行handler返回modelAndView之前執(zhí)行	//如果需要向頁面提供一些公用的數(shù)據(jù)或配置一些視圖信息,使用此方法實現(xiàn) 從modelAndView入手	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)		System.out.println("HandlerInterceptor2...postHandle");	//執(zhí)行handler之后執(zhí)行此方法	//作為系統(tǒng)統(tǒng)一異常處理,進行方法執(zhí)行性能監(jiān)控,在preHandler中設置一個時間點 在afterCompletion設置一個時間點 二者時間差就是執(zhí)行時長	//實現(xiàn)系統(tǒng),統(tǒng)一日志記錄	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)		System.out.println("HandlerInterceptor2...afterCompletion");
 資源授權(quán)攔截器PermissionInterceptorpackage liuxun.ssm.controller.interceptor;import java.security.acl.Permission;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import liuxun.ssm.po.ActiveUser;import liuxun.ssm.po.SysPermission;import liuxun.ssm.util.ResourcesUtil;public class PermissionInterceptor implements HandlerInterceptor{	//在執(zhí)行handler之前執(zhí)行的	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {		String url = request.getRequestURI();		//實際開發(fā)中需要將公開地址配置在配置文件中		List<String> open_urls = ResourcesUtil.getKeyList("anonymousURL");		for (String open_url : open_urls) {			if (url.indexOf(open_url)>=0) {		List<String> common_urls = ResourcesUtil.getKeyList("commonURL");		for (String common_url : common_urls) {			if (url.indexOf(common_url)>0) {		HttpSession session = request.getSession();		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");		//從Session中取出權(quán)限范圍的URL		List<SysPermission> permissions = activeUser.getPermissions();		for (SysPermission sysPermission : permissions) {			String permission_url = sysPermission.getUrl();			if (url.indexOf(permission_url)>0) {		//執(zhí)行到這里攔截,跳轉(zhuǎn)到無權(quán)訪問的提示頁面		request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);		//如果返回false表示攔截器不繼續(xù)執(zhí)行handler,如果返回true表示放行	//在執(zhí)行handler返回modelAndView之前執(zhí)行	//如果需要向頁面提供一些公用的數(shù)據(jù)或配置一些視圖信息,使用此方法實現(xiàn) 從modelAndView入手	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)		System.out.println("HandlerInterceptor2...postHandle");	//執(zhí)行handler之后執(zhí)行此方法	//作為系統(tǒng)統(tǒng)一異常處理,進行方法執(zhí)行性能監(jiān)控,在preHandler中設置一個時間點 在afterCompletion設置一個時間點 二者時間差就是執(zhí)行時長	//實現(xiàn)系統(tǒng),統(tǒng)一日志記錄	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)		System.out.println("HandlerInterceptor2...afterCompletion");
 攔截器配置		   	<mvc:mapping path="/**"/>		   	<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>	    	<mvc:mapping path="/**"/>	    	<bean class="liuxun.ssm.controller.interceptor.PermissionInterceptor"></bean>
 使用URL攔截總結(jié):使用基于URL攔截的權(quán)限管理方式,實現(xiàn)起來比較簡單,不依賴框架使用過濾器或攔截器就可以實現(xiàn)  弊端:需要將所有的URL全部配置起來,比較繁瑣,不易維護,URL(資源)和權(quán)限表示方式不規(guī)范 
 |