勿以惡小而爲之,勿以善小而不爲--------------------------劉備
勸諸君,多行善事積福報,莫作惡
上一章簡單介紹了 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 進行舉例。
- 設置標識,後面根據標識來控制按鈕的顯示與隱藏
<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>
- 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
謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!