勿以恶小而为之,勿以善小而不为--------------------------刘备
劝诸君,多行善事积福报,莫作恶
上一章简单介绍了 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='';
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="https://www.ctyun.cn/portal/link.html?target=javascript%3Avoid%280%29%3B"
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="/static/js/dept.js"></script>
- dept.js 里面,从sessionStorage 里面取出标识,进行显隐
二.一.三 退出登录按钮
二.一.三.一 前端页面实现
<div class="right">
<span>
<a href="https://www.ctyun.cn/portal/link.html?target=javascript%3Avoid%280%29%3B" onclick="window.location.href='https://www.ctyun.cn/portal/link.html?target=User%3Fmethod%3Dlogout'">退出</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 的身份登录后, 执行部门查询的方法
可以查询出数据
但执行部门添加的方法时
权限验证,实现成功,详细的代码设计可以看链接。
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!