接四,四里面对权限说了个大概,今天打算将权限好好说说,接下来几个板块将对整个系统的权限做详细介绍,这是整个系统最核心的部分.
刚开始做系统时对权限抱有一颗敬畏的心,一直不敢深究,也没有遇到什么好的权限系统可以学习,前段时间公司开发了个很小的权限管理系统,我正好参与设计了权限设计.刚开始开会讨论时,大家意见不一致,其实都对权限模型不太懂,最后我建议放弃RBAC,倒不是说RBAC不好,而是这个系统是在太小了,就是管理几个其他系统的入口,不同的人登录给他不同的入口就完事了,最后我刚好接触过一个权限模型,就用了!大概思想是这样的,在页面分配权限时,每个人都有一个权限字符串,长度和所有的要控制的入口一样,位数一一对应,谁有这个权限,对应为就为1,在页面分配权限时,对这几个系统勾选,如果勾选了,在后在就用js构造出对应的字符串,然后保存到对应的管理员表中,这个方法很简单,但是很实用,只适合小型的系统,如果权限字符串过长的话,就不好控制了.
var teipprim =""; //构造权限字符串 function checkPrim(){ for (var i=1;i<11;i++){ c = "0"; tempid = "prim"+i; if (document.getElementById(tempid).checked == true)c = "1"; teipprim = teipprim+c; } if (teipprim!='000000000000000'){ document.all.privileges.value = teipprim; }else{ document.all.privileges.value = ""; } } //还原权限列表 function recoverPrim(temp){ if (teipprim=='000000000000000'){ document.all.privileges.value = ""; return; }else{ for (var i=0;i<10;i++){ if (temp.substring(i,i+1)=="1")document.getElementById("prim"+(i+1)).checked = true; } } }
然后登陆时采用拦截器,代码如下:
public class LoginedCheckInterceptor extends AbstractInterceptor {
/** 拦截请求并进行登录有效性验证 */
public String intercept(ActionInvocation ai) throws Exception {
//取得请求的URL
String url = ServletActionContext.getRequest().getRequestURL().toString();
System.out.println(url);
String prim = null;
Admin admin = null;
int index = 0;
//验证Session是否过期
if(!ServletActionContext.getRequest().isRequestedSessionIdValid()){
//session过期,转向session过期提示页,最终跳转至登录页面
return "tologin";
}else{
//对登录与注销请求直接放行,不予拦截
if (url.indexOf("admin_login.action")!=-1 || url.indexOf("admin_logout.action")!=-1){
return ai.invoke();
}else{
admin = (Admin)ServletActionContext.getRequest().getSession().getAttribute("admin");
//验证是否已经登录
if (admin==null){
//尚未登录,跳转至登录页面
return "tologin";
}else{
//功能模块与权限位映射,部分可能与前台请求重名的请求加上命名空间"/admin"以示区别
if (url.indexOf("/admin_")!=-1 || url.indexOf("/updateAdmin")!=-1){//系统用户管理
index = 2; //权限位为2
}else if (url.indexOf("/columns_")!=-1 || url.indexOf("/updateColumns")!=-1){//新闻栏目管理
index = 3; //权限位为3
}else if (url.indexOf("/news_")!=-1 || url.indexOf("/preAddNews")!=-1 || url.indexOf("/updateNews")!=-1 || url.indexOf("/publisNews")!=-1){//新闻管理
index = 4; //权限位为4
}else if (url.indexOf("/rule_")!=-1 || url.indexOf("/preAddNewsrule")!=-1 || url.indexOf("/updateNewsrule")!=-1){ //新闻采集
index = 5; //权限位为5
}else if (url.indexOf("/level_")!=-1 || url.indexOf("/updateMemberlevel")!=-1){//会员级别管理
index = 6; //权限位为6
}else if (url.indexOf("/member_")!=-1 || url.indexOf("/preAddMember")!=-1 || url.indexOf("/admin/updateMember")!=-1){//会员管理
index = 6; //权限位为6
}else if (url.indexOf("/cate_")!=-1 || url.indexOf("/updateCategory")!=-1){//商品分类管理
index = 7; //权限位为7
}else if (url.indexOf("/mer_")!=-1 || url.indexOf("/preAddMerchandise")!=-1 || url.indexOf("/updateMerchandise")!=-1 || url.indexOf("/publisMerchandise")!=-1){//商品管理
index = 8; //权限位为8
}else if (url.indexOf("/orders_")!=-1 || url.indexOf("/admin/updateOrdersStatus")!=-1){//订单管理
index = 9; //权限位为9
}else if (url.indexOf("/traffic_")!=-1){//流量统计
index = 10; //权限位为10
}
//取得当前用户的操作权限
prim = admin.getPrivileges().trim();
if (index>0){
if (prim.substring(0,1).equals("1") || prim.substring(index-1,index).equals("1")){
return ai.invoke();
}else{
return "nopm";
}
}else{
return ai.invoke();
}
}
}
}
}
}
这是我遇到最简单的形式,但是不够灵活,我觉得如果页面链接全部写死了用这个也可以!
下面详细谈谈这个论坛用的权限模型:
再贴一次图吧:
这个图有一点问题,Board和BoardPermission之间最后实现时没有对应关系,BoardPermission类的结构如下,它只是保存了Board和UserGroup的对应关系,并不是以对象的形式声明的.只是和Permission类有关联关系.而且大家也看出来了,这个系统的Board类的id是long类型的,group是String类型的,印证了我在[三]里面的结论.
public class BoardPermission implements Serializable {
private String id;
private long boardID;
private String groupID;
private Set permissions;
}
这个论坛权限分为两条主线,今天来谈谈第一条线,也就是上面那条线:
主要涉及版区权限,下次谈个人权限.先看看创建一个版区时的代码:
public void createBoard(Board board) {
this.getDao().saveOrUpdate(board);
List uglist = this.getDao().listAll("UserGroup", "id", Constant.ASC);
BoardPermission bp;
for (int i = 0; i < uglist.size(); i++) {
UserGroup ug = (UserGroup) uglist.get(i);
bp = new BoardPermission();
bp.setBoardID(board.getId());
bp.setGroupID(ug.getId());
switch (ug.getType()) {
case 0://游客
bp.setPermissions(SystemInit.gbp0);
break;
case 1://注册用户
bp.setPermissions(SystemInit.gbp1);
break;
case 2://等待验证用户组
bp.setPermissions(SystemInit.gbp2);
break;
case 3:// 超级版主
bp.setPermissions(SystemInit.gbp3);
break;
case 4:// 系统管理员
bp.setPermissions(SystemInit.gbp4);
break;
case 5:// 封禁用户
bp.setPermissions(SystemInit.gbp5);
break;
default:
bp.setPermissions(SystemInit.gbp0);
}
this.getDao().saveOrUpdate(bp);
}
}
一句话解释:对于每个用户组,在创建版区时都为其创建了版区对应的权限.下面是SystemInit的一段代码,便于对上面代码理解.
public class SystemInit {
private static PermissionService permissionService;
static{
ApplicationContext acx = new ClassPathXmlApplicationContext("app*.xml");
permissionService = (PermissionService) acx.getBean("permissionService");
}
// resource,action
public static Permission p000 = new Permission("注册权限","/userInfo", "reg,regSave",2);
public static Permission p101 = new Permission("发帖权限", "/forum", "post,postSave", 2);
public static Permission p102 = new Permission("查看主帖列表权限", "/forum", "listForums", 2);
public static Permission p103 = new Permission("查看精华帖列表权限", "/forum", "listForumsElite", 2);
public static Permission p201 = new Permission("查看帖子权限", "/forum", "readForum", 2);
public static Permission p202 = new Permission("回复帖子权限", "/forum", "reSave,reQuote", 2);
public static Permission p203 = new Permission("编辑自己帖子权限", "/forum", "editForum,editForumSave" , 2);
public static Permission p401 = new Permission("版主管理权限", "/forum", "manager", 2);
public static Permission p402 = new Permission("版主发布公告权限", "/forum", "postBulletin,postBulletinSave", 2);
public static Permission p403 = new Permission("版主删除帖子权限", "/forum", "deleteForum", 2);
public static Permission p404 = new Permission("版主加精帖子权限", "/forum", "doElite", 2);
//用户组版区权限
public static Set gbp0= new HashSet();//未注册用户
public static Set gbp1= new HashSet();//注册用户
public static Set gbp2= new HashSet();//带验证用户
public static Set gbp3= new HashSet();//超级版主
public static Set gbp4= new HashSet();//超级管理员
public static Set gbp5= new HashSet();//封禁用户
static{
//对权限保存代码省略
gbp0.add(p000);//未注册用户
gbp0.add(p102);
gbp0.add(p201);
//
gbp1.add(p000);//注册用户
gbp1.add(p101);
gbp1.add(p102);
gbp1.add(p103);
gbp1.add(p201);
gbp1.add(p202);
gbp1.add(p203);
//
gbp3.add(p401);//超级版主
gbp3.add(p402);
gbp3.add(p403);
gbp3.add(p404);
gbp4.add(p000);//系统管理员
gbp4.add(p101);
gbp4.add(p102);
gbp4.add(p103);
gbp4.add(p201);
gbp4.add(p202);
gbp4.add(p203);
gbp4.add(p401);
gbp4.add(p402);
gbp4.add(p403);
gbp4.add(p404);
}
}
权限类型有四种,个人权限(0),个人特殊权限(1),版区权限(2),版区特殊权限(3),括号里为权限类型.这样就会有一个表来保存board和permission之间多对多的关系,访问页面时使用拦截器获取访问者所在用户组和用户要访问的版区id,这样就可以获取到对应的版区权限,代码如下:
public class BoardInterceptor extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext ac = invocation.getInvocationContext();
Object action = invocation.getAction();
String actionName = "/"+ac.getName();
String saction = "";
Map map = ac.getParameters();
String[] _saction = (String[]) map.get("action");
if (_saction != null) {
saction = _saction[0];
}
long bid = 0;
String[] _bid = (String[]) map.get("bid");
if (_bid != null) {
bid = NumberUtils.toLong(_bid[0], 0);
}
ServletContext servletContext = (ServletContext) ac.get(ServletActionContext.SERVLET_CONTEXT);
WebApplicationContext wc = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if(bid==0){//版区错误
System.out.println("版区错误bid=0");
}
BoardService boardService = (BoardService)wc.getBean("boardService");
Board board = boardService.getBoardByID(bid);
if(board==null){
System.out.println("版区错误board=null");
}
HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);
HttpServletResponse response = (HttpServletResponse) ac.get(ServletActionContext.HTTP_RESPONSE);
UserSession us = (UserSession) ac.getSession().get(Constant.USERSESSION);
if(us.getBid()!=bid){
us.getBoardPermission().clear();
us.getSpecialPermission().clear();
us.setBid(bid);
System.out.println("BoardInterceptor.bid:"+bid);
System.out.println("BoardInterceptor.groupID:"+us.getGroupID());
Map[] maps = boardService.getBoardPermissions(bid, us.getGroupID());
us.setBoardPermissionArray(maps);
Iterator it = board.getBoardMaster().iterator();
BoardMaster bmt;//版主
while(it.hasNext()){
bmt = (BoardMaster) it.next();
if(bmt.getUserName().equals(us.getUserName())){//是斑竹
us.setIsBoardMaster(1);
Map[] bmpMap = boardService.getBoardMasterPermission(bmt.getRole());
System.out.println(bmpMap[0].entrySet().size());
us.setBoardPermissionArray(bmpMap);
System.out.println("设置成功");
}
}
//此处省略父版区权限
ac.getSession().put(Constant.USERSESSION, us);
//BoardMaster bm = board.getBoardMaster().addAll(c);
}
boolean havaPermission = false;
Permission permission = (Permission) us.getBoardPermission().get(actionName + "?action=*");
if(permission!=null){
havaPermission = true;
}else{
permission = (Permission) us.getBoardPermission().get(actionName+"?action="+saction);
if(permission!=null){
havaPermission = true;
}else{
System.out.println("saction 为获得");
}
}
if(havaPermission){
//此处添加BoardAware
return invocation.invoke();
}else{
StringBuffer sb = new StringBuffer();
sb.append(BBSNSUtil.getWebRealPath(request));
sb.append(request.getContextPath());
sb.append("/");
sb.append(BBSNSUtil.getActionMappingURLWithoutPrefix(ac.getName()));
UrlHelper.buildParametersString(map, sb, "&");
String curl = sb.toString();
System.out.println("curl:"+curl);
ac.getValueStack().set("tourl", curl);
return "nopermission";
}
}
}
拦截器中最重要的一个方法代码如下:
private Map[] getPermissions(long bid, String groupid) {
Map[ ] boardPermission = { new HashMap(), new HashMap() };
Map mps = new HashMap();
mps.put("boardID", bid);
mps.put("groupID", groupid);
List _list = this.getDao().findByValue("BoardPermission", mps, "id", Constant.ASC);
BoardPermission bp = null;
if (!_list.isEmpty()&&_list!=null) {
bp = (BoardPermission) _list.get(0);
}
Set bps = bp.getPermissions();
if (bps != null) {
Iterator it = bps.iterator();
while (it.hasNext()) {
Permission permission = (Permission) it.next();
if (permission.getTypeID() == 2) {//版区权限
boardPermission[0].put(permission.getResource() + ","+ permission.getAction(), permission);
}
if (permission.getTypeID() == 3) {//版区特殊权限
boardPermission[1].put(permission.getId(), permission);
}
}
} else {
System.out.println("boardPermission 为空!!!"+bps.size());
}
return boardPermission;
}
这个getPermissions方法就是采用我在[二]里面说的那种无耦合的传Map的方法.这样
如果得到对应的版区权限就表明用户所在用户组有这个版区的访问权限.
好了,快下班了,也不知道说清楚了没.如果有什么问题,欢迎留言讨论,个人权限下次再讨论.
原创首发,谢谢支持!