Servlet實現RBAC權限管理(二)

勿以惡小而爲之,勿以善小而不爲--------------------------劉備

勸諸君,多行善事積福報,莫作惡

上一章簡單介紹了 RBAC權限(一) ,如果沒有看過,請觀看上一章

在觀看這一章節之前,需要先知道 老蝴蝶剛寫的文章: Servlet開發搭建

本章節所使用的表,仍然是 RBAC 權限講解的那五張表, 框架使用的是 Servlet開發搭建的框架。

一. 準備階段

一.一 數據庫準備階段

(關於數據庫的數據,會放置一個 rbac.sql 文件上來的,讀者可以執行,故不復制 insert 插入語句了)

user 表數據 (密碼被MD5 加密過了,明文爲 1234)

有圖片

role 表數據:

有圖片

user_role 表數據

有圖片

admin 是管理員, yuejl 是經理, yuezl 是普通職員

privilege 表數據:

有圖片

role_privilege 表數據:

有圖片

用戶有哪些權限,有點暈啊, 老蝴蝶這兒查詢一下。

admin 所擁有的權限:


select * from privilege a where a.id in ( select rp.pid from user_role ur left join role_privilege rp 
					on ur.rid=rp.rid  where ur.uid=1 );

有圖片

嶽建立所擁有的權限:

有圖片

嶽澤霖所擁有的權限:

有圖片

另外,還有一個部門表, dept, 用來演示具體的按鈕權限數據。

有圖片

一.二 前端頁面準備階段

(前端頁面代碼較多,這兒就不復制了,只講解一下,各個頁面的作用和相應的截圖)

有圖片

jdbc.properties ,是數據庫配置文件

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/rbac?characterEncoding=UTF-8
username=root
password=abc123

dept.js,是 部門功能的js腳本

pages/dept.jsp 頁面,是部門的頁面,實現相應信息

有圖片

pages/detailInfo.jsp ,是查看個人信息的頁面,不實現相應的信息

有圖片

pages/login.jsp,登錄的頁面,實現相應的信息

有圖片

pages/main.jsp ,主頁展示的頁面, 實現相應的信息

有圖片

pages/noPrivilege.jsp, 無權限時展示的頁面, 實現相應的信息

有圖片

pages/updateInfo.jsp, 是修改個人信息的界面,不實現相應的信息

有圖片

pages/user.jsp , 是員工的頁面,不實現相應的信息。

有圖片

以部門表,進行詳細的權限舉例分析。

二. 權限認證

目錄結構如下所示:

有圖片

二.一 認證和授權

在登錄的方法裏面,進行驗證,如果查詢出有這個員工的信息,就去查詢這個員工的權限數據。

這個方法 寫在 UserServlet 的 login 方法裏面。

二.一.一 登錄方法進行認證和查詢權限

public void login(HttpServletRequest req,HttpServletResponse resp){
		//將請求信息,封裝到對象裏面。
		User userInfo=super.reqParamToBean(req,User.class);
		userService=new UserServiceImpl();
		
		//從數據庫中查詢密碼
		User user=userService.login(userInfo);
		
		if(user!=null){
			//說明登錄成功,放置到session裏面
			HttpSession session=req.getSession();
			session.setAttribute("loginUser",user);
			
			//查詢一下,該員工的 按鈕權限信息。
			privilegeService=new PrivilegeServiceImpl();
			
			List<Privilege> privilegeList= privilegeService.getPrivilegeByUId(user.getId(),2);
			
			List<String> priCodeList=new ArrayList<String>();
			for(Privilege pri:privilegeList){
				if(pri.getPercode()!=null){
					//放置標識
					priCodeList.add(pri.getPercode());
				}
			
			}
			JsonConfig jsonConfig = new JsonConfig();
			JSONArray objData=JSONArray.fromObject(priCodeList,jsonConfig);
			JSONObject objMap=new JSONObject();
			objMap.put("data",objData);
			//轉換成 json 字符串對象
			session.setAttribute("privilegeList",objMap.toString());
			
			//登錄成功
			super.boolean2Json(resp, true);
			
		}else{
			//代碼爲001,表示用戶名或者密碼錯誤
			 super.map2Json(resp,"001");
		}
		
} 

如果登錄成功,那麼就跳轉到 main.jsp 的頁面。

(該代碼存放在 login.jsp 的頁面)
有圖片

二.一.二 權限菜單展示

main.jsp 的左側菜單,是動態地讀取出來的,然後渲染到頁面上。

二.一.二.一 菜單 div

<!-- 存放左側的菜單事件 -->
<div class="leftmenu" id="leftmenu">
    <div id="leftmenu_0" class="leftmenu-item">
       
    </div>
    
</div>

二.一.二.二 根據登錄者id 動態查詢權限,渲染到頁面 leftmenu 上

/*獲取所有的權限信息*/
 			function getAllPrivilege(){
            //取出當前登錄的用戶信息
        	   var userId='${sessionScope.loginUser.id}';
        	   console.log("id:"+userId);
        	   
        	   $.post("Privilege?method=getPrivilegeByUId",{userId:userId},function(data){
        		   //查詢出權限
        		   var allPrivilegeList=data.data;
        		   
        		   createMenuByData($("#leftmenu_0"),allPrivilegeList);
        	   })
            }
			//執行獲取權限的方法
            getAllPrivilege();
            //渲染到頁面裏面
            function createMenuByData(target,allPrivilegeList){
            	
            	target.empty();
            	
            	var firstMenus=[];
            	
            	var secondMenus=[];
            	
            	$.each(allPrivilegeList,function(idx,item){
            		//有父
            		if(item.pid){
            			secondMenus.push(item);
            		}else{
            			firstMenus.push(item);
            		}
            	})
            	
            	$.each(firstMenus,function(idx,item){
 
            		var $dl=$("<dl id='"+item.id+"'><dt>"+item.name+"</dt></dl>");
            		var $dd=$("<dd id='"+item.id+"'></dd>");
            		var $ul=$('<ul id="'+item.id+'" class="clearfix"></ul>');
            		$dd.append($ul);
            		$dl.append($dd);
            		$.each(secondMenus,function(idx1,item1){
            			if(item1.pid==item.id){
            			  var $li=$('<li id="'+item1.id+'"><a href="javascript:void(0);" 
			data-link="'+item1.url+'">'+item1.name+'</a></li>');
            			  $ul.append($li);	
            			}
            		})
            		target.append($dl);
            		
            	})
            }

二.一.二.三 根據用戶編號,查詢該用戶的權限

PrivilegeServiceImpl 實現類裏面的 getPrivilegeByUId() 方法。

有圖片

二.一.二.四 展示 按鈕的權限

上面一部分,可以根據登錄者的id 動態地展示 菜單權限,下面根據登錄者的id 動態地展示頁面上按鈕的權限。

用 部門表 dept 進行舉例。

  1. 設置標識,後面根據標識來控制按鈕的顯示與隱藏
<script>
			//獲取權限 
			var privilegeList='<%=session.getAttribute("privilegeList")%>';	
			//console.log("值是:"+privilegeList+","+typeof(privilegeList));
			var objPrivilegeList=JSON.parse(privilegeList);
			$.each(objPrivilegeList.data,function(idx,item){
				//將標識存放在 sessionStorage 裏面,進行暫時的保存。 
				sessionStorage.setItem(item,true);
			})
</script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/dept.js"></script>
  1. dept.js 裏面,從sessionStorage 裏面取出標識,進行顯隱

有圖片

二.一.三 退出登錄按鈕

二.一.三.一 前端頁面實現

<div class="right">
        <span>
            <a href="javascript:void(0);" onclick="window.location.href='User?method=logout'">退出</a>
        </span>
    </div>

二.一.三.二 退出後臺實現

//退出登錄
	public String logout(HttpServletRequest req,HttpServletResponse resp){
		
		HttpSession session=req.getSession();
		//註銷
		session.invalidate();
		
		return "login";
	}

二.二 不同用戶地驗證測試

二.二.一 admin 管理員驗證

輸入網址: http://localhost:8080/Servlet_RBAC/User?jsp=toLogin

填入 用戶名爲 admin, 密碼是 1234

有圖片

點擊登錄:

有圖片

菜單顯示是正常的, 沒有添加和修改的權限,按鈕顯示是正常的。

二.二.二 yuejl 經理驗證

輸入網址: http://localhost:8080/Servlet_RBAC/User?jsp=toLogin

填入 用戶名爲 yuejl, 密碼是 1234

有圖片

點擊登錄:

有圖片

菜單顯示是正常的, 沒有部門的權限,所以部門那沒有顯示數據。

二.二.三 yuezl 普通職員驗證

輸入網址: http://localhost:8080/Servlet_RBAC/User?jsp=toLogin

填入 用戶名爲 yuezl, 密碼是 1234

有圖片

點擊登錄:

有圖片

菜單顯示是正常的

菜單的權限和按鈕的權限,控制是正常的。

但這個時候,也是有問題的, 如果我們知道後端的訪問地址,如用戶 yuezl 沒有部門的權限,但知道 部門查詢的地址是:

http://localhost:8080/Servlet_RBAC/Dept?method=list

有圖片

知道部門 添加的地址是: http://localhost:8080/Servlet_RBAC/Dept?method=add

有圖片

有圖片

這樣是不安全的, 我們需要在每一次訪問時,都需要對 url 路徑進行一下判斷。

需要用到登錄和授權過濾器。

二.三 登錄和授權過濾器 LoginInterceptor

package com.yjl.web.interceptor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.yjl.pojo.Privilege;
import com.yjl.pojo.User;
import com.yjl.service.PrivilegeService;
import com.yjl.service.impl.PrivilegeServiceImpl;

//過濾所有的請求
@WebFilter("/*")
public class LoginInterceptor implements Filter{

	//不需要登錄驗證的url
	private static List<String> noLoginValidateUrl;
	//不需要權限驗證的url
	private static List<String> noPriValidateUrl;
	
	//跳轉到的登錄頁面
	private static String LOGIN_URL;
	//沒有權限的界面
	private static String NO_PRIVILEGE_URL;
	
	static{
		noLoginValidateUrl=new ArrayList<String>();
		
		//靜態資源
		noLoginValidateUrl.add("/static/");
		//登錄頁面
		noLoginValidateUrl.add("/User?jsp=toLogin");
		//登錄方法
		noLoginValidateUrl.add("/User?method=login");
		
		
		noPriValidateUrl=new ArrayList<String>();
		
		noPriValidateUrl.add("/Main?jsp=toMain");
		//查詢權限
		
		
		noPriValidateUrl.add("/Privilege?method=getPrivilegeByUId");
		//退出
		noPriValidateUrl.add("/User?method=logout");
		
		LOGIN_URL="/WEB-INF/pages/login.jsp";
		
		NO_PRIVILEGE_URL="/WEB-INF/pages/noPrivilege.jsp";
	}
	
	@Override
	public void destroy() {
		// TODO 自動生成的方法存根
		
	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
			throws IOException, ServletException {
		if(arg0 instanceof HttpServletRequest){
			//請求。
			HttpServletRequest req=(HttpServletRequest)arg0;
			
			//獲取Session
			HttpSession session=req.getSession();
			
			//請求路徑
			String uri=req.getRequestURI();
			//請求參數
			String reqParam=req.getQueryString();
			
			//真正的請求路徑
			String realPath="";
			
			if(reqParam==null||"".equals(reqParam)){
				realPath=uri;
			}else{
				realPath=uri+"?"+reqParam;
			}
			
			System.out.println("地址是:"+realPath);
			
			//驗證是否在 不需要驗證登錄的url裏面
			if(isContain(realPath,1)){
				arg2.doFilter(arg0, arg1);
				return ;
			}
			//如果爲空,表示沒有登錄
			if(session.getAttribute("loginUser")==null){
				req.getRequestDispatcher(LOGIN_URL).forward(req,(HttpServletResponse)arg1);
			}else{
				
				//不需要驗證權限
				if(isContain(realPath,2)){
					arg2.doFilter(arg0, arg1);
					return ;
				}
				//如果不爲空,表示登錄了。
				PrivilegeService privilegeService=new PrivilegeServiceImpl();
				//重新獲取全部權限 , 需要緩存, 這兒不用緩存。
				User user=(User)session.getAttribute("loginUser");
				List<Privilege> privilegeList=privilegeService.getPrivilegeByUId(user.getId(),null);
				boolean isHavePri=false;
				for(Privilege pri:privilegeList){
					if(pri.getUrl()!=null){
						if(realPath.contains(pri.getUrl())){
							isHavePri=true;
							break;
						}
					}
				}
				if(isHavePri){
					//放行
					arg2.doFilter(arg0, arg1);
				}else{
					req.getRequestDispatcher(NO_PRIVILEGE_URL).forward(req,(HttpServletResponse)arg1);
				}
				
			}
			
		}
	}
	
	private boolean isContain(String realPath,int type){	
		List<String> urls;
		if(type==1){
			urls=noLoginValidateUrl;
		}else{
			urls=noPriValidateUrl;
		}
		
		boolean flag=false;
		
		for(String url:urls){
			//包括,返回-1
			if(realPath.indexOf(url)!=-1){
				flag=true;
				break;
			}
		}
		return flag;
		
	}
	

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO 自動生成的方法存根
		
	}
}

重啓服務器,以 yuezl 的身份登錄後,

查詢部門的方法:

http://localhost:8080/Servlet_RBAC/Dept?method=list

有圖片

以 admin 的身份登錄後, 執行部門查詢的方法

可以查詢出數據

有圖片

但執行部門添加的方法時

有圖片

權限驗證,實現成功,詳細的代碼設計可以看鏈接。

本章節代碼鏈接爲:


鏈接:https://pan.baidu.com/s/12u2rFqmpiZ5mpuHxJYgJvA 
提取碼:gif7

謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章