权限管理的设计方法
權限管理的設計方法是非常重要的,在項目開發中,也是最最基本的。
首先,設置三種要素:用戶、群組、角色。
用戶為登錄用,對應到人。群組對應為用戶的集合,是一種特殊的用戶。角色為一組權限項的集合,用戶(群組)都有各自的角色。
權限的實現通過Permission類和Rule類來實現。
Permission供外部調用,Rule為一個接口,為權限判斷規則。
Permission是一個抽象類,有以下方法
public boolean hasPermission(User user,HashMap oldData,Input input);
public String getPermissionName();
public abstract Rule[] getDenyRule();
public abstract Rule[] getAcceptRule();
hasPermission方法供外部調用,已實現,實現方法為先根據getDenyRule()得到的規則判斷權限是否被阻攔,再根據getAcceptRule來判斷是否有權限。
而Rule接口的接品則由用戶自行定義,隨包附帶了一個已實現的Rule,實現的功能如下:
先尋找User的所有角色,然后判斷角色是否有權限,如果無權限則尋找其父級群組,再取父級群組的所有角色進行判斷是否有權限,如果無權限則再往上級群組找,直到找最上一級還是無權限才判斷為無權限。
現實現判斷權限有無權限的方式已可以達成的有以下三種:
1、 是否有操作的權限。
2、 是否有操作的子操作的權限。
3、 在數據為某條件時有操作(子操作)的權限。
在進行程序開發時,第一步,編寫User,Group,Role的實現類,已提供了一套XML的實現類。
第二步,寫配置文件,進行權限項的配置。
第三步,在程序中要進行權限判斷的地方調用Permission.hasPermission方法即可。
以后我會把代碼逐天貼出來。
如果大家有什么別的種類權限要進行判斷的,請給我發送郵件,我繼續改善,歡迎大家多提意見。
==================================================
權限組件之一(用戶)
----------------------
首先,我定義了一個用戶接口,可以從其中取出其的各種屬性.代碼如后面所示.用戶除了各種不同的屬性以外還必須設置其角色以及所屬的群組.
然后定義一個AbstractUser把User共性的東西進行處理.所有的取屬性的方法都已實現.用戶只要根據實現情況繼承AbstractUser把自己要定義的屬性進行處理即可.(因為每個系統的用戶都會有不同的屬性,所以留成抽象類供使用者自己擴展). 只要初始化變量description, name ,group ,id, prop,role,propMap即可.
最后定義了一個類UserImpl來實現具體的User作隨包的一個User供一般使用者使用.不建議直接使用UserImpl,因為以后的版本可能會重寫該類.如果要使用的話可以把該類改名為自己的類即可.
這部分涉及到了一個ResourceLib類,該類中存是我自己定義的存儲各種資源配置用的類,可以直接使用.
======================User.java===============================
package org.fswan.permission;
import org.fswan.Identity;
import java.util.Properties;
import org.fswan.workflow.exception.CantFoundIDException;
public interface User
{
/**
* 獲取用戶名
* @return 用戶名
*/
public String getName();
/**
* 獲取用戶描述
* @return 描述
*/
public String getDescription();
/**
* 獲取用戶標識
* @return 用戶標識
*/
public Identity getId();
/**
* 獲取屬性
* @return 屬性值
*/
public Properties getProperties();
/**
* 獲取所屬組
* @return 組列表
*/
public Group[] getGroups() throws CantFoundIDException;
/**
* 獲取所有角色
* @return 角色數組
*/
public Role[] getRoles() throws CantFoundIDException;
/**
* 獲取用戶用作權限判斷的屬性
* @param prop 權限屬性
* @return 屬性值
*/
public Object getPermissionProp(String prop);
}
=======================AbstractUser===================================
package org.fswan.permission;
import java.util.HashMap;
import java.util.Properties;
import org.fswan.Identity;
import org.fswan.workflow.exception.IdentityMappingError;
/**
*
* 實現AbstractUser的類構造必須做的一些事
* 初始化變量description,name,group,id,prop,role,propMap
*/
public class AbstractUser implements User
{
/**
* 描述
*/
protected String description;
/**
* 名稱
*/
protected String name;
/**
* 所屬的組的標識
*/
protected Identity[] group;
/**
* 用戶標識
*/
protected Identity id;
/**
* 屬性
*/
protected Properties prop;
/**
* 角色
*/
protected Identity[] role;
/**
* 權限屬性存儲的位置
*/
protected HashMap propMap;
/* (non-Javadoc)
* @see org.fswan.permission.User#getDescription()
*/
public String getDescription()
{
return description;
}
/* (non-Javadoc)
* @see org.fswan.permission.User#getGroups()
*/
public Group[] getGroups()
{
Group[] groups = new Group[group.length];
for (int i = 0; i < groups.length; i++)
{
try
{
groups[i] = (Group) group[i].newInstance();
} catch (IdentityMappingError e)
{
e.printStackTrace();
}
}
return groups;
}
/* (non-Javadoc)
* @see org.fswan.permission.User#getId()
*/
public Identity getId()
{
return id;
}
/* (non-Javadoc)
* @see org.fswan.permission.User#getName()
*/
public String getName()
{
return name;
}
/* (non-Javadoc)
* @see org.fswan.permission.User#getProperties()
*/
public Properties getProperties()
{
return prop;
}
/* (non-Javadoc)
* @see org.fswan.permission.User#getRoles()
*/
public Role[] getRoles()
{
Role[] roles = new Role[role.length];
for (int i = 0; i < roles.length; i++)
{
try
{
roles[i] = (Role) role[i].newInstance();
} catch (IdentityMappingError e)
{
e.printStackTrace();
}
}
return roles;
}
public String toString()
{
String retStr = id.getIdName();
retStr += ":" + name;
retStr += " " + description + "\n";
retStr += "Group:";
Group[] groups = this.getGroups();
if (groups != null)
{
for (int i = 0; i < groups.length; i++)
{
if (groups[i] != null)
retStr += groups[i].getId().getIdName() + "\t";
}
}
Properties p = getProperties();
Object[] obj = p.keySet().toArray();
for (int i = 0; i < obj.length; i++)
{
retStr+=obj[i]+":"+p.getProperty(obj[i].toString())+"\n";
}
return retStr;
}
/* (non-Javadoc)
* @see org.fswan.permission.User#getPermissionProp(java.lang.String)
*/
public Object getPermissionProp(String prop)
{
return propMap.get(prop);
}
}
==============================UserImpl.java===========================
package org.fswan.permission;
import java.util.ArrayList;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilderFactory;
import org.fswan.Identity;
import org.fswan.IdentityImpl;
import org.fswan.ImplementIdentity;
import org.fswan.ResourceLib;
import org.fswan.workflow.exception.IdentityMappingError;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class XMLUser extends AbstractUser implements ImplementIdentity
{
/**
* XML中定義用戶的標簽
*/
public static final String USER = "User";
public Object newInstance(Identity id)
{
ArrayList sources = ResourceLib.getXmlResource();
for (int i = 0; i < sources.size(); i++)
{
try
{
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(sources.get(i).toString());
NodeList nl = doc.getElementsByTagName(USER);
for (int j = 0; j < nl.getLength(); j++)
{
Element tempEl = (Element) nl.item(j);
String idStr = tempEl.getAttribute("id");
if (idStr != null && !idStr.equals(""))
{
if (idStr.equals(id.getIdName()))
{
this.id = new IdentityImpl(Identity.SWITCH, Identity.XMLUSER, idStr);
NodeList tempNl = tempEl.getElementsByTagName("Name");
name = tempNl.item(0).getChildNodes().item(0).getNodeValue();
tempNl = tempEl.getElementsByTagName("Description");
description = tempNl.item(0).getChildNodes().item(0).getNodeValue();
tempNl = tempEl.getElementsByTagName("ParentGroup");
Identity[] groups = new Identity[tempNl.getLength()];
for (int iGroup = 0; iGroup < groups.length; iGroup++)
{
Element tempEl2 = (Element) tempNl.item(iGroup);
String subType = tempEl.getAttribute("subType");
if(subType==null || subType.equals(""))subType = Identity.XMLGROUP;
groups[iGroup] = new IdentityImpl(Identity.GROUP, subType, tempEl2.getAttribute("id"));
}
this.group = groups;
tempNl = tempEl.getElementsByTagName("Role");
Identity[] roles = new Identity[tempNl.getLength()];
for (int iRole = 0; iRole < tempNl.getLength(); iRole++)
{
Element tempEl2 = (Element) tempNl.item(iRole);
String subType = tempEl.getAttribute("subType");
if(subType==null || subType.equals(""))subType = Identity.XMLROLE;
roles[iRole] = new IdentityImpl(Identity.ROLE, subType, tempEl2.getAttribute("id"));
}
this.role = roles;
this.prop = new Properties();
tempNl = tempEl.getElementsByTagName("Property");
if (tempNl != null)
for (int k = 0; k < tempNl.getLength(); k++)
{
Element tempElement = (Element) tempNl.item(k);
System.out.println(tempElement.getAttribute("name"));
this.prop.setProperty(tempElement.getAttribute("name"), tempElement.getAttribute("value"));
}
return this;
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
}
return null;
}
public static void main(String[] args)
{
try
{
ResourceLib.addResource("D:\\eclipse\\workspace\\workflow\\workflow.xml");
User u = (User) new IdentityImpl(Identity.USER, Identity.XMLUSER, "U01").newInstance();
System.out.println(u);
} catch (IdentityMappingError e)
{
e.printStackTrace();
}
}
}
========================XML格式=====================
<User id="U01">
<Name>Swan</Name>
<Description>方志文</Description>
<ParentGroup id="G01"/>
<Property name="SEX" value="male"/>
<Property name="AGE" value="20"/>
</User>
========================XML描述文件==================
<xs:complexType name="User">
<xs:annotation>
<xs:documentation>用戶名</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="Name"/>
<xs:element name="Description"/>
<xs:element name="ParentGroup" type="Identity" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Role" type="Identity" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Property" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" use="required"/>
<xs:attribute name="value" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" use="required"/>
</xs:complexType>
=========================================================
==========================================================
==========================================================
權限組件之二(群組)
-------------------------
首先,我定義了一個群組接口,除了繼承用戶的方法以外還有兩個方法,getUsers,getSubGroup.代碼如后面所示.用戶除了各種不同的屬性以外還必須設置其角色以及所屬的群組.
然后定義一個AbstractGroup,他繼承了Group以及AbstractUser,并實現了Group接口定義的兩個方法.用戶只要根據實現情況繼承AbstractGroup把自己要定義的屬性進行處理即可.(因為每個系統的用戶都會有不同的屬性,所以留成抽象類供使用者自己擴展). 只要初始化變量description, name ,group ,id, prop,role,propMap,subGroup,user即可.
最后定義了一個類XMLGroup來實現具體的Group作隨包的一個Group供一般使用者使用.不建議直接使用XMLGroup,因為以后的版本可能會重寫該類.如果要使用的話可以把該類改名為自己的類即可.
這部分涉及到了一個ResourceLib類,該類中存是我自己定義的存儲各種資源配置用的類,可以直接使用.
======================Group.java===============================
package org.fswan.permission;
public interface Group extends User
{
/**
* 獲取組下所有的用戶
* @return 用戶數組
*/
public User[] getUsers();
/**
* 獲取組下屬的組
* @return 組的數組
*/
public Group[] getSubGroup();
}
=======================AbstractGroup===================================
package org.fswan.permission;
import org.fswan.Identity;
import org.fswan.workflow.exception.IdentityMappingError;
public class AbstractGroup extends AbstractUser implements Group
{
protected Identity[] subGroup;
protected Identity[] user;
/* (non-Javadoc)
* @see org.fswan.permission.Group#getSubGroup()
*/
public Group[] getSubGroup()
{
Group[] groups = new Group[subGroup.length];
for (int i = 0; i < groups.length; i++)
{
try
{
groups[i] = (Group)subGroup[i].newInstance();
} catch (IdentityMappingError e)
{
e.printStackTrace();
}
}
return groups;
}
/* (non-Javadoc)
* @see org.fswan.permission.Group#getUsers()
*/
public User[] getUsers()
{
User[] users = new User[user.length];
for (int i = 0; i < users.length; i++)
{
try
{
users[i] = (User)user[i].newInstance();
} catch (IdentityMappingError e)
{
e.printStackTrace();
}
}
return users;
}
public String toString()
{
String retStr = id.getIdName();
retStr +=":"+name;
retStr += " "+description;
return retStr;
}
}
==============================XMLGroup.java===========================
package org.fswan.permission;
import java.util.ArrayList;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilderFactory;
import org.fswan.Identity;
import org.fswan.IdentityImpl;
import org.fswan.ImplementIdentity;
import org.fswan.ResourceLib;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class XMLGroup extends AbstractGroup implements ImplementIdentity
{
/**
* XML中定義群組的標簽
*/
public static final String GROUP = "Group";
public Object newInstance(Identity id)
{
ArrayList sources = ResourceLib.getXmlResource();
for (int i = 0; i < sources.size(); i++)
{
try
{
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(sources.get(i).toString());
NodeList nl = doc.getElementsByTagName(GROUP);
for (int j = 0; j < nl.getLength(); j++)
{
Element tempEl = (Element)nl.item(j);
String idStr = tempEl.getAttribute("id");
if(idStr!=null && !idStr.equals(""))
{
if(idStr.equals(id.getIdName()))
{
this.id = new IdentityImpl(Identity.SWITCH,Identity.XMLGROUP,idStr);
//加載名稱
NodeList tempNl = tempEl.getElementsByTagName("Name");
name = tempNl.item(0).getChildNodes().item(0).getNodeValue();
//加載描述
tempNl = tempEl.getElementsByTagName("Description");
description = tempNl.item(0).getChildNodes().item(0).getNodeValue();
//加載父群組
tempNl = tempEl.getElementsByTagName("ParentGroup");
Identity[] groups = new Identity[tempNl.getLength()];
for (int iGroup=0; iGroup < tempNl.getLength(); iGroup++)
{
tempEl = (Element)tempNl.item(iGroup);
groups[iGroup] = new IdentityImpl(Identity.GROUP,Identity.XMLGROUP,tempEl.getAttribute("id"));
}
this.subGroup = groups;
//加載角色
tempNl = tempEl.getElementsByTagName("Role");
Identity[] roles = new Identity[tempNl.getLength()];
for (int iRole=0; iRole < tempNl.getLength(); iRole++)
{
tempEl = (Element)tempNl.item(iRole);
roles[iRole] = new IdentityImpl(Identity.ROLE,Identity.XMLROLE,tempEl.getAttribute("id"));
}
this.role = roles;
//加載屬性
this.prop = new Properties();
tempNl = tempEl.getElementsByTagName("Property");
if(tempNl!=null)
for (int k = 0; k < tempNl.getLength(); k++)
{
Element tempElement = (Element)tempNl.item(k);
this.prop.setProperty(tempElement.getAttribute("name"),tempElement.getAttribute("value"));
}
//加載子群組
tempNl = tempEl.getElementsByTagName("SubGroup");
if (tempNl != null)
{
subGroup = new Identity[tempNl.getLength()];
for (int k = 0; k < subGroup.length; k++)
{
Element tempElement = (Element) tempNl.item(k);
subGroup[k] = new IdentityImpl(Identity.GROUP,Identity.XMLGROUP,tempElement.getAttribute("id"));
}
}
//加載用戶
tempNl = tempEl.getElementsByTagName("User");
if (tempNl != null)
{
user = new Identity[tempNl.getLength()];
for (int k = 0; k < subGroup.length; k++)
{
Element tempElement = (Element) tempNl.item(k);
user[k] = new IdentityImpl(Identity.USER,Identity.XMLUSER,tempElement.getAttribute("id"));
}
}
return this;
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
}
return null;
}
}
========================XML格式=====================
<Group id="G01">
<Name>系統部</Name>
<Description>系統部</Description>
</Group>
========================XML描述文件==================
<xs:complexType name="Group">
<xs:annotation>
<xs:documentation>群組</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="User">
<xs:sequence>
<xs:element name="SubGroup" type="Identity" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="User" type="Identity" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
================================================
====================================================
======================================================
權限組件之三(角色)
---------------------------------
首先,我定義了一個角色接口,可以從其中取出其的各種屬性.代碼如后面所示.
然后定義一個AbstractRole把Role共性的東西進行處理.所有的取屬性的方法都已實現.用戶只要根據實現情況繼承AbstractRole把自己要定義的屬性進行處理即可.(因為每個系統的用戶都會有不同的屬性,所以留成抽象類供使用者自己擴展). 只要初始化變量description, name ,id, prop,users(可能是群組), permissionMap即可.
最后定義了一個類XMLRole來實現具體的Role作隨包的一個Role供一般使用者使用.不建議直接使用XMLRole,因為以后的版本可能會重寫該類.如果要使用的話可以把該類改名為自己的類即可.
這部分涉及到了一個ResourceLib類,該類中存是我自己定義的存儲各種資源配置用的類,可以直接使用.
======================Role.java===============================
package org.fswan.permission;
import org.fswan.Identity;
import java.util.Properties;
import org.fswan.workflow.exception.CantFoundIDException;
public interface Role
{
/**
* 獲取角色ID
* @return
*/
public Identity getId();
/**
* 獲取角色的名稱
* @return 名稱
*/
public String getName();
/**
* 獲取角色的描述
* @return 描述
*/
public String getDescription();
/**
* 獲取角色的所有的用戶(組)
* @return 用戶
*/
public User[] getUsers() throws CantFoundIDException;
/**
* 角色的屬性
* @return 屬性值
*/
public Properties getProperties();
/**
* 獲取用戶用作權限判斷的屬性
* @param prop 權限屬性
* @return 屬性值
*/
public Object getPermissionProp(String prop);
}
=======================AbstractRole===================================
package org.fswan.permission;
import java.util.HashMap;
import java.util.Properties;
import org.fswan.Identity;
import org.fswan.workflow.exception.IdentityMappingError;
public class AbstractRole implements Role
{
/**
* 描述
*/
protected String description ;
/**
* 名稱
*/
protected String name;
/**
* 標識
*/
protected Identity id;
/**
* 屬性
*/
protected Properties prop;
/**
* 該角色所有用戶的標識
*/
protected Identity[] users;
/**
* 權限屬性表
*/
protected HashMap permissionMap;
/* (non-Javadoc)
* @see org.fswan.permission.Role#getDescription()
*/
public String getDescription()
{
return description;
}
/* (non-Javadoc)
* @see org.fswan.permission.Role#getId()
*/
public Identity getId()
{
return id;
}
/* (non-Javadoc)
* @see org.fswan.permission.Role#getName()
*/
public String getName()
{
return name;
}
/* (non-Javadoc)
* @see org.fswan.permission.Role#getProperties()
*/
public Properties getProperties()
{
return prop;
}
/* (non-Javadoc)
* @see org.fswan.permission.Role#getUsers()
*/
public User[] getUsers()
{
User[] retUser = new User[users.length];
for (int i = 0; i < retUser.length; i++)
{
try
{
retUser[i] = (User)users[i].newInstance();
} catch (IdentityMappingError e)
{
e.printStackTrace();
}
}
return retUser;
}
public String toString()
{
String retStr = id.getIdName();
retStr +=":"+name;
retStr += " "+description;
return retStr;
}
/* (non-Javadoc)
* @see org.fswan.permission.Role#getPermissionProp(java.lang.String)
*/
public Object getPermissionProp(String prop)
{
return permissionMap.get(prop);
}
}
==============================XMLRole.java===========================
/*
* Created on 2004-4-21
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package org.fswan.permission;
import java.util.ArrayList;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilderFactory;
import org.fswan.Identity;
import org.fswan.IdentityImpl;
import org.fswan.ImplementIdentity;
import org.fswan.ResourceLib;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class XMLRole extends AbstractRole implements ImplementIdentity
{
/**
* XML標簽用的字符串
*/
public static final String ROLE = "Role";
public Object newInstance(Identity id)
{
ArrayList sources = ResourceLib.getXmlResource();
for (int i = 0; i < sources.size(); i++)
{
try
{
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(sources.get(i).toString());
NodeList nl = doc.getElementsByTagName(ROLE);
for (int j = 0; j < nl.getLength(); j++)
{
Element tempEl = (Element)nl.item(j);
String idStr = tempEl.getAttribute("id");
if(idStr!=null && !idStr.equals(""))
{
if(idStr.equals(id.getIdName()))
{
this.id = new IdentityImpl(Identity.SWITCH,Identity.XMLROLE,idStr);
NodeList tempNl = tempEl.getElementsByTagName("Name");
name = tempNl.item(0).getChildNodes().item(0).getNodeValue();
tempNl = tempEl.getElementsByTagName("Description");
description = tempNl.item(0).getChildNodes().item(0).getNodeValue();
this.prop = new Properties();
tempNl = tempEl.getElementsByTagName("Property");
if(tempNl!=null)
for (int k = 0; k < tempNl.getLength(); k++)
{
Element tempElement = (Element)tempNl.item(k);
this.prop.setProperty(tempElement.getAttribute("name"),tempElement.getAttribute("value"));
}
return this;
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
}
return null;
}
}
========================XML格式=====================
<Role id="R01">
<Name>系統管理員</Name>
<Description>系統管理員</Description>
</Role>
========================XML描述文件==================
<xs:complexType name="Role">
<xs:annotation>
<xs:documentation>角色名</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="Name"/>
<xs:element name="Description"/>
<xs:element name="Property" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" use="required"/>
</xs:complexType>
======================================================================
=======================================================================
=======================================================================
權限組件之四(規則)
---------------------------
定義好User,Group,Role了以后,下面我定義了權限判斷的規則.
首先定義Rule接口,見Rule.java.Rule只做一件事判斷User是否有權限.
然后我實現了一個Rule,見RuleImpl.java.
這部分涉及到了一個Input類,這個類是一個輸入的類,接口如Input.java.這個通過繼承該接口可以使用各種方式數據(HTTP,XML,SOAP……)作為輸入數據.
Permission為權限類,下面一篇會介紹.
======================Rule.java===============================
package org.fswan.permission;
import java.util.HashMap;
import org.fswan.Input;
/**
* 用來進行判斷的規則.
* 供Permission調用
*/
public interface Rule
{
public boolean pass(Permission permission,User user,HashMap oldData,Input input);
}
=======================RuleImpl===================================
package org.fswan.permission;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import org.fswan.Input;
/**
*
*/
public class RuleImpl implements Rule
{
/* (non-Javadoc)
* @see org.fswan.permission.Rule#pass(org.fswan.permission.Permission, org.fswan.permission.User, org.fswan.permission.Role, org.fswan.permission.Group, java.util.HashMap, org.fswan.Input)
*/
public boolean pass(Permission permission, User user, HashMap oldData, Input input)
{
try
{
Role[] roles = user.getRoles();
if (roles != null)
{
for (int i = 0; i < roles.length; i++)
{
if (hasPermission(permission, user, roles[i], oldData, input)) //有權限
return true;
}
}
Group[] groups = user.getGroups();
if (groups != null)
for (int i = 0; i < groups.length; i++)
{
if (pass(permission, groups[i], oldData, input))
return true;
}
return false;
} catch (Exception ex)
{
ex.printStackTrace();
}
return false;
}
/**
* 判斷用戶的某個角色是否有權限進行某操作
* @param permission 權限
* @param user 用戶
* @param role 角色
* @param oldData 數據
* @param input 輸入
* @return 是否有權限
*/
private boolean hasPermission(Permission permission, User user, Role role, HashMap oldData, Input input)
{
//如角色沒有該權限ID則無權限
if (role.getPermissionProp(permission.getPermissionName()) == null)
return false;
//如果權限還要判斷層別
if (permission.getPermissionItem() != null)
{
ArrayList item = permission.getPermissionItem();
boolean accept = true;
ArrayList roleItem = (ArrayList) role.getPermissionProp(permission.getPermissionName());
ttt : for (int i = 0; i < roleItem.size(); i++)
{
Properties p = (Properties) roleItem.get(i);
accept = true;
for (int j = 0; j < item.size(); j++)
{
if (p.getProperty(item.get(j).toString()) != null
&& p.getProperty(item.get(j).toString()).indexOf(oldData.get(item.get(j).toString()).toString())
!= -1) //如果無權限
continue ttt;
}
return true;
}
return false;
}
return true;
}
}
=============================Input.java=======================
package org.fswan;
/**
* @author 方志文
*
* 輸入數據的接口,作為主類的數據輸入式,
* 現可以從xml,HttpServletRequest和JSPUpload獲取數據
*
*/
public interface Input {
/**
* 獲取參數的值,如果無該參數則返回null,如果該參數對應多個值則返回其第一個值
* @param parameter 參數
* @return 值
*/
public String getParameter(String parameter);
/**
* 獲取參數的值列表
* @param parameter 參數
* @return 值
*/
public String[] getParameterValues(String parameter);
/**
* 獲取參數名的列表
* @return 所有的參數的名
*/
public String[] getParameterNames();
/**
* 獲取數據源
* @return 數據源
*/
public Object getSource();
/**
* 設置參數,如果已存在該參數,則原來的作廢
* @param parameter 參數
* @param value 值
*/
public void setParameter(String parameter,String value);
/**
* 設置參數,如果已存在該參數,則把原來的作廢
* @param parameter 參數
* @param values 值
*/
public void setParameter(String parameter,String[] values);
/**
* 添加參數,如果已存在該參數,則把該值添加到原值后成數組
* @param parameter 參數
* @param value 值
*/
public void addParameter(String parameter,String value);
/**
* 添加參數(多個值),如果已存在該參數,則把該值添加到原值后成數組
* @param parameter 參數
* @param values 值
*/
public void addParameter(String parameter,String[] values);
}
============================================================
===============================================================
===============================================================
權限組件五(權限)(完)
----------------------------
最后,我定義了一個Permission類把所有的元素連接起來形成一個完整權限判斷組件。代碼如下。用戶繼承該類時要完兩個方法getDenyRule,getAcceptRule即可。
當我們進行權限判斷時只要調用hasPermission(User user,HashMap oldData,Input input)即可。其后三個參數分別為用戶,數據,輸入。用戶為在判斷的人,數據為原有數據,比較進行更新操作時,oldData為老數據,input在前一部分有介紹,為一個通用的輸入方式。比WEB輸入Post或Get方式等,可以轉換成一個Input類,也可以手動新建一個Input.為操作以及其數據的集合。
在Permission類中進行了DenyRule和AcceptRule的判斷,只要有一個DenyRule規則符合要求,則認為該用戶無權限進行操作,如果用戶的沒被DenyRule排除則進行AcceptRule的判斷,只要有一個規則符合要求則認為該用戶有權限。
因為本人比較懶所以Permission的實現還沒有寫出一個通用的。
綜上所述,使用此權限組件要進行以下幾項工作:
1、 編寫User的實現。
2、 編寫Group的實現。
3、 編寫Role的實現。
4、 編寫Permission的實現。(過段時間我會擺個通用的上來)
5、 編寫N個Rule(同你的系統要進行權限判斷相對應)。
6、 在程序中調用Permission.hasPermission判斷。
======================Permission.java===============================
package org.fswan.permission;
import java.util.ArrayList;
import java.util.HashMap;
import org.fswan.Input;
/**
* 權限判斷類
*/
public abstract class Permission
{
public String permission;
public String subPermission;
protected ArrayList prop;
public boolean hasPermission(User user,HashMap oldData,Input input)
{
Rule[] rules = getDenyRule();
boolean accept = true;
for (int i = 0; i < rules.length; i++)
{
if(!rules[i].pass(this,user,oldData,input))
{
accept = false;
break;
}
}
if(accept)return false;
rules = getAcceptRule();
for (int i = 0; i < rules.length; i++)
{
if(!rules[i].pass(this,user,oldData,input))
return false;
}
return true;
}
public String getPermissionName()
{
if(subPermission==null)return permission;
return permission+":"+subPermission;
}
public ArrayList getPermissionItem()
{
return prop;
}
public abstract Rule[] getDenyRule();
public abstract Rule[] getAcceptRule();
}
==================================================
==================================================
==================================================
權限組件所缺的文件
原意是給大家看一下結構,沒想大家都那么關心,我現在把所有的代碼貼上來,謝謝大家關心.
唉,BLOG好煩啊,無法直接上傳文件要一個一個貼
================ResourceLib.java ============
package org.fswan;
import java.util.ArrayList;
import java.util.Properties;
/**
* 用來存儲通用的XML配置文件位置和數據庫連接屬性.
* 作為org.fswan里所有的包中類的基礎配置文件.
*/
public class ResourceLib
{
/**
* 驅動的字符串
*/
public static final String DRIVER = "Driver";
/**
* 數據庫連接字符串
*/
public static final String URL = "URL";
/**
* 用戶
*/
public static final String USER = "User";
/**
* 密碼
*/
public static final String PASSWORD = "Password";
/**
* 所有的XML配置文件的存儲位置,存儲的對象為URL
*/
private static ArrayList resource = new ArrayList();
/**
* 存儲所有的數據庫配置.
* 所有的數據庫存儲位置格式為:
* 對象為Properties對象
* 而properties對象里包含:
* {@link #DRIVER DRIVER},{@link #URL URL},{@link #USER USER},{@link #PASSWORD PASSWORD},{@link #VIEW VIEW},{@link #TYPE TYPE}六個屬性分別對應
* 數據庫的驅動,邊接字符串,用戶名,密碼,視圖,類型
* 或者
* {@link #JNDI JNDI},{@link #VIEW VIEW},{@link #TYPE TYPE}
* JNDI名,視圖,類型
* 類型是以下的一個
* {@link #WORKFLOW WORKFLOW}
* {@link #BRAND BRAND}
* {@link #STATUS STATUS}
* {@link #SWITCH SWITCHER}
* {@link #WORKUNIT WORKUNIT}
* {@link #USER USER}
*/
private static ArrayList prop = new ArrayList();
/**
* 獲取所有的XML資源
* @return XML資源泉
*/
public static ArrayList getXmlResource()
{
return resource;
}
/**
* 獲取所有的數據庫連接屬性
* @return 數據庫鏈接屬性
*/
public static ArrayList getConnection()
{
return prop;
}
/**
* 增加XML資源
* @param source XML資源
*/
public static void addResource(String source)
{
synchronized(resource)
{
resource.add(source);
}
}
/**
* 增加數據庫鏈接屬性
* @param pro 數據庫鏈接屬性
*/
public static void addDataProperty(Properties pro)
{
synchronized(prop)
{
prop.add(pro);
}
}
}
==============Input.java============
/*
* Created on 2003-11-20
*
* Create By 方志文
* email:fswan@yeah.net
*/
package org.fswan;
/**
* @author 方志文
*
* 輸入數據的接口,作為主類的數據輸入式,
* 現可以從xml,HttpServletRequest和JSPUpload獲取數據
*
*/
public interface Input {
/**
* 獲取參數的值,如果無該參數則返回null,如果該參數對應多個值則返回其第一個值
* @param parameter 參數
* @return 值
*/
public String getParameter(String parameter);
/**
* 獲取參數的值列表
* @param parameter 參數
* @return 值
*/
public String[] getParameterValues(String parameter);
/**
* 獲取參數名的列表
* @return 所有的參數的名
*/
public String[] getParameterNames();
/**
* 獲取數據源
* @return 數據源
*/
public Object getSource();
/**
* 設置參數,如果已存在該參數,則原來的作廢
* @param parameter 參數
* @param value 值
*/
public void setParameter(String parameter,String value);
/**
* 設置參數,如果已存在該參數,則把原來的作廢
* @param parameter 參數
* @param values 值
*/
public void setParameter(String parameter,String[] values);
/**
* 添加參數,如果已存在該參數,則把該值添加到原值后成數組
* @param parameter 參數
* @param value 值
*/
public void addParameter(String parameter,String value);
/**
* 添加參數(多個值),如果已存在該參數,則把該值添加到原值后成數組
* @param parameter 參數
* @param values 值
*/
public void addParameter(String parameter,String[] values);
}
================ManualInput.java ==============
package org.fswan;
public class ManualInput extends AbstractInput
{
/* (non-Javadoc)
* @see org.fswan.Input#getSource()
*/
public Object getSource()
{
return null;
}
}
===================Identity.java==========
package org.fswan;
import org.fswan.workflow.exception.IdentityMappingError;
/**
* @author Administrator
* 這個接是一個標識,用來唯一標識所有的工作流,狀態,切換開關,分枝操作
*/
public interface Identity
{
/**
* 工作流的標識
*/
public static final int WORKFLOW = 0;
/**
* 分枝操作的標識
*/
public static final int BRAND = 1;
/**
* 狀態的標識
*/
public static final int STATUS =2;
/**
* 切換開關的標識
*/
public static final int SWITCH = 3;
/**
* 用戶的標識
*/
public static final int USER = 4;
/**
* 工作單元
*/
public static final int WORKUNIT = 5;
/**
* 角色
*/
public static final int ROLE = 6;
/**
* 群組
*/
public static final int GROUP = 7;
public static final int VIEWCLASS = 255;
public static final String XMLWORKFLOW = "XMLWorkFlow";
public static final String XMLBRAND = "XMLBrand";
public static final String XMLSWITCHER = "XMLSwitcher";
public static final String XMLSTATUS = "XMLStatus";
public static final String XMLWORKUNIT = "XMLWorkUnit";
public static final String DBWORKFLOW = "DBWorkFlow";
public static final String DBBRAND = "DBBrand";
public static final String DBSWITCHER = "DBSwitcher";
public static final String DBSTATUS = "DBStatus";
public static final String DBWORKUNIT = "DBWorkUnit";
public static final String XMLUSER = "XMLUser";
public static final String XMLGROUP = "XMLGroup";
public static final String XMLROLE = "XMLRole";
/**
* 獲取標識的類型
* 是以下常量中的一個
* {@link #WORKFLOW WORKFLOW}
* {@link #BRAND BRAND}
* {@link #STATUS STATUS}
* {@link #SWITCH SWITCH}
* {@link #USER USER}
* @return 標識的類型
*/
public int getIdType();
/**
* 獲取標識的名稱
* @return 標識的名稱
*/
public String getIdName();
/**
* 返回該Identity的子類型,該子類型是給newInstance使用的
* @return 子類型
*/
public String getSubType();
/**
* 通過ID生成實例
* @return 實例
* @throws IdentityMappingError 找不到生器異常
*/
public Object newInstance() throws IdentityMappingError;
}
=============IdentityImpl.java===============
package org.fswan;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import org.fswan.workflow.exception.IdentityMapHadExist;
import org.fswan.workflow.exception.IdentityMappingError;
/**
* 唯一標識符類.
* 由唯一標識符可以調用相應的類來生成相對應的接口生成相對應的實例
* 不同種類標識符生成相對應的實例的類在{@link map}中進行映射定義.
* 可調用靜態方法register進行注冊.
*/
public class IdentityImpl implements Identity
{
private String name;
private int type;
private String subType;
/**
* 從ID到類的映射表
*/
private static Hashtable map = new Hashtable();
public IdentityImpl(int type,String subType,String name)
{
this.type = type;
this.subType=subType;
this.name = name;
}
public IdentityImpl genIdentity(int type)
{
SimpleDateFormat f = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String name = f.format(new Date());
return new IdentityImpl(type,"",name);
}
/* (non-Javadoc)
* @see org.fswan.Identity#getIdName()
*/
public String getIdName()
{
return name;
}
/* (non-Javadoc)
* @see org.fswan.Identity#getIdType()
*/
public int getIdType()
{
return type;
}
public String getSubType()
{
return subType;
}
public String toString()
{
return "["+type+":"+name+"]";
}
public boolean equals(Object obj)
{
if(obj instanceof Identity)
{
return this.getIdType()==((Identity)obj).getIdType()
&& getIdName().equals(((Identity)obj).getIdName());
}
return false;
}
/**
* 查找相應對的對象
* @return 實例化后的對象
* @throws IdentityMappingError 映射實現不了,
* 可能是因為注冊的類不存在或者無法實例化該類,或該類不是ImplementIdentity
*/
public Object newInstance() throws IdentityMappingError
{
try
{
if(map.get(subType)==null)return null;
ImplementIdentity ids = (ImplementIdentity)Class.forName(map.get(subType).toString()).newInstance();
return ids.newInstance(this);
} catch (InstantiationException e)
{
e.printStackTrace();
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (ClassNotFoundException e)
{
e.printStackTrace();
}
throw new IdentityMappingError();
}
/**
* 注冊標識種類到生成實例類的映射
* @param type 類型
* @param obj 相對應的類名
* @throws IdentityMapHadExist 異常,(該類型已注冊)
*/
public static void register(String subType,String obj) throws IdentityMapHadExist
{
if(map.containsKey(subType)) throw new IdentityMapHadExist(subType);
map.put(subType,obj);
}
static
{
try
{
IdentityImpl.register(Identity.DBBRAND,"org.fswan.workflow.DBWrokFlow");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.DBSTATUS,"org.fswan.workflow.DBStatus");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.DBSWITCHER,"org.fswan.workflow.DBSwitcher");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.DBWORKFLOW,"org.fswan.workflow.DBWrokFlow");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.DBWORKUNIT,"org.fswan.workflow.DBWorkUnit");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLBRAND,"org.fswan.workflow.XMLBrand");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLGROUP,"org.fswan.permission.XMLGroup");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLROLE,"org.fswan.permission.XMLRole");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLSTATUS,"org.fswan.workflow.XMLStatus");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLSWITCHER,"org.fswan.workflow.XMLSwitcher");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLUSER,"org.fswan.permission.XMLUser");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLWORKFLOW,"org.fswan.workflow.XMLWorkFlow");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
try
{
IdentityImpl.register(Identity.XMLWORKUNIT,"org.fswan.workflow.XMLWorkUnit");
} catch (IdentityMapHadExist e)
{
e.printStackTrace();
}
}
}
==================ImplementIdentity.java=============
package org.fswan;
/**
* @author swan
* 通過ID實例化相應的對象.
* 當用戶自己實現User,Role,Group,Brand等對象時,通過ID來查找相對應的對象,
* 在類IdentityImpl對象中根據注冊各種Identity類型對應的InstanceIdentity對象
* 來實例化對象.
* 注意:該接口的子類的構造方法一定是PUBLIC且不帶任何參數.
*/
public interface ImplementIdentity
{
/**
* 實例化ID相對對應的對象.
* @param identity 要實例化的對象的ID
* @return 對象
*/
public Object newInstance(Identity identity);
}
===========CantFoundIDException.java-==============
package org.fswan.workflow.exception;
import org.fswan.Identity;
/**
* @author Administrator
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class CantFoundIDException extends Exception
{
public CantFoundIDException(Identity id)
{
super("Can't Found ID:["+id.getIdType()+":"+id.getIdName()+"]");
}
}
==================IdentityMapHadExist.java============
package org.fswan.workflow.exception;
/**
* @author swan
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class IdentityMapHadExist extends Exception
{
public IdentityMapHadExist(String subType)
{
super("Mapping Had Exist:"+subType);
}
}
==================IdentityMappingError.java============
package org.fswan.workflow.exception;
/**
* @author swan
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class IdentityMappingError extends Exception
{
public IdentityMappingError()
{
super("The Mapping Class is not InstanceIdentity.");
}
}
=================TypeCastException.java ============
package org.fswan.workflow.exception;
import org.fswan.db.Field;
/**
* 類型轉換異常,當從字符串轉成特定類型出錯時拋出
*/
public class TypeCastException extends Exception
{
/**
* 構造函數
* @param field 字段
* @param str 字符串值
*/
public TypeCastException(Field field,String str)
{
super(str+" cast to "+field + " Error");
}
}
思路很好!!!!!!!!!
author: EdwarddotNet
前言:
權限往往是一個極其復雜的問題,但也可簡單表述為這樣的邏輯表達式:判斷“Who對What(Which)進行How的操作”的邏輯表達式是否為真。針對不同的應用,需要根據項目的實際情況和具體架構,在維護性、靈活性、完整性等N多個方案之間比較權衡,選擇符合的方案。
目標:
直觀,因為系統最終會由最終用戶來維護,權限分配的直觀和容易理解,顯得比較重要,系統不辭勞苦的實現了組的繼承,除了功能的必須,更主要的就是因為它足夠直觀。
簡單,包括概念數量上的簡單和意義上的簡單還有功能上的簡單。想用一個權限系統解決所有的權限問題是不現實的。設計中將常常變化的“定制”特點比較強的部分判斷為業務邏輯,而將常常相同的“通用”特點比較強的部分判斷為權限邏輯就是基于這樣的思路。
擴展,采用可繼承在擴展上的困難。的Group概念在支持權限以組方式定義的同時有效避免了重定義時
現狀:
對于在企業環境中的訪問控制方法,一般有三種:
1.自主型訪問控制方法。目前在我國的大多數的信息系統中的訪問控制模塊中基本是借助于自主型訪問控制方法中的訪問控制列表(ACLs)。
2.強制型訪問控制方法。用于多層次安全級別的軍事應用。
3.基于角色的訪問控制方法(RBAC)。是目前公認的解決大型企業的統一資源訪問控制的有效方法。其顯著的兩大特征是:1.減小授權管理的復雜性,降低管理開銷。2.靈活地支持企業的安全策略,并對企業的變化有很大的伸縮性。
名詞:
粗粒度:表示類別級,即僅考慮對象的類別(the type of object),不考慮對象的某個特
定實例。比如,用戶管理中,創建、刪除,對所有的用戶都一視同仁,并不區分操作的具體對象實例。
細粒度:表示實例級,即需要考慮具體對象的實例(the instance of object),當然,細
粒度是在考慮粗粒度的對象類別之后才再考慮特定實例。比如,合同管理中,列表、刪除,需要區分該合同實例是否為當前用戶所創建。
原則:
權限邏輯配合業務邏輯。即權限系統以為業務邏輯提供服務為目標。相當多細粒度的權限問題因其極其獨特而不具通用意義,它們也能被理解為是“業務邏輯”的一部分。比如,要求:“合同資源只能被它的創建者刪除,與創建者同組的用戶可以修改,所有的用戶能夠瀏覽”。這既可以認為是一個細粒度的權限問題,也可以認為是一個業務邏輯問題。在這里它是業務邏輯問題,在整個權限系統的架構設計之中不予過多考慮。當然,權限系統的架構也必須要能支持這樣的控制判斷。或者說,系統提供足夠多但不是完全的控制能力。即,設計原則歸結為:“系統只提供粗粒度的權限,細粒度的權限被認為是業務邏輯的職責”。
需要再次強調的是,這里表述的權限系統僅是一個“不完全”的權限系統,即,它不提供所有關于權限的問題的解決方法。它提供一個基礎,并解決那些具有“共性”的(或者說粗粒度的)部分。在這個基礎之上,根據“業務邏輯”的獨特權限需求,編碼實現剩余部分(或者說細粒度的)部分,才算完整。回到權限的問題公式,通用的設計僅解決了Who+What+How 的問題,其他的權限問題留給業務邏輯解決。
概念:
Who:權限的擁用者或主體(Principal、User、Group、Role、Actor等等)
What:權限針對的對象或資源(Resource、Class)。
How:具體的權限(Privilege, 正向授權與負向授權)。
Role:是角色,擁有一定數量的權限。
Operator:操作。表明對What的How 操作。
說明:
User:與 Role 相關,用戶僅僅是純粹的用戶,權限是被分離出去了的。User是不能與 Privilege 直接相關的,User 要擁有對某種資源的權限,必須通過Role去關聯。解決 Who 的問題。
Resource:就是系統的資源,比如部門新聞,文檔等各種可以被提供給用戶訪問的對象。資源可以反向包含自身,即樹狀結構,每一個資源節點可以與若干指定權限類別相關可定義是否將其權限應用于子節點。
Privilege:是Resource Related的權限。就是指,這個權限是綁定在特定的資源實例上的。比如說部門新聞的發布權限,叫做"部門新聞發布權限"。這就表明,該Privilege是一個發布權限,而且是針對部門新聞這種資源的一種發布權限。Privilege是由Creator在做開發時就確定的。權限,包括系統定義權限和用戶自定義權限用戶自定義權限之間可以指定排斥和包含關系(如:讀取,修改,管理三個權限,管理 權限 包含 前兩種權限)。Privilege 如"刪除" 是一個抽象的名詞,當它不與任何具體的 Object 或 Resource 綁定在一起時是沒有任何意義的。拿新聞發布來說,發布是一種權限,但是只說發布它是毫無意義的。因為不知道發布可以操作的對象是什么。只有當發布與新聞結合在一起時,才會產生真正的 Privilege。這就是 Privilege Instance。權限系統根據需求的不同可以延伸生很多不同的版本。
Role:是粗粒度和細粒度(業務邏輯)的接口,一個基于粗粒度控制的權限框架軟件,對外的接口應該是Role,具體業務實現可以直接繼承或拓展豐富Role的內容,Role不是如同User或Group的具體實體,它是接口概念,抽象的通稱。
Group:用戶組,權限分配的單位與載體。權限不考慮分配給特定的用戶。組可以包括組(以實現權限的繼承)。組可以包含用戶,組內用戶繼承組的權限。Group要實現繼承。即在創建時必須要指定該Group的Parent是什么Group。在粗粒度控制上,可以認為,只要某用戶直接或者間接的屬于某個Group那么它就具備這個Group的所有操作許可。細粒度控制上,在業務邏輯的判斷中,User僅應關注其直接屬于的Group,用來判斷是否“同組” 。Group是可繼承的,對于一個分級的權限實現,某個Group通過“繼承”就已經直接獲得了其父Group所擁有的所有“權限集合”,對這個Group而言,需要與權限建立直接關聯的,僅是它比起其父Group需要“擴展”的那部分權限。子組繼承父組的所有權限,規則來得更簡單,同時意味著管理更容易。為了更進一步實現權限的繼承,最直接的就是在Group上引入“父子關系”。
User與Group是多對多的關系。即一個User可以屬于多個Group之中,一個Group可以包括多個User。子Group與父Group是多對一的關系。Operator某種意義上類似于Resource + Privilege概念,但這里的Resource僅包括Resource Type不表示Resource Instance。Group 可以直接映射組織結構,Role 可以直接映射組織結構中的業務角色,比較直觀,而且也足夠靈活。Role對系統的貢獻實質上就是提供了一個比較粗顆粒的分配單位。
Group與Operator是多對多的關系。各概念的關系圖示如下:
解釋:
Operator的定義包括了Resource Type和Method概念。即,What和How的概念。之所以將What和How綁定在一起作為一個Operator概念而不是分開建模再建立關聯,這是因為很多的How對于某What才有意義。比如,發布操作對新聞對象才有意義,對用戶對象則沒有意義。
How本身的意義也有所不同,具體來說,對于每一個What可以定義N種操作。比如,對于合同這類對象,可以定義創建操作、提交操作、檢查沖突操作等。可以認為,How概念對應于每一個商業方法。其中,與具體用戶身份相關的操作既可以定義在操作的業務邏輯之中,也可以定義在操作級別。比如,創建者的瀏覽視圖與普通用戶的瀏覽視圖要求內容不同。既可以在外部定義兩個操作方法,也可以在一個操作方法的內部根據具體邏輯進行處理。具體應用哪一種方式應依據實際情況進行處理。
這樣的架構,應能在易于理解和管理的情況下,滿足絕大部分粗粒度權限控制的功能需要。但是除了粗粒度權限,系統中必然還會包括無數對具體Instance的細粒度權限。這些問題,被留給業務邏輯來解決,這樣的考慮基于以下兩點:
一方面,細粒度的權限判斷必須要在資源上建模權限分配的支持信息才可能得以實現。比如,如果要求創建者和普通用戶看到不同的信息內容,那么,資源本身應該有其創建者的信息。另一方面,細粒度的權限常常具有相當大的業務邏輯相關性。對不同的業務邏輯,常常意味著完全不同的權限判定原則和策略。相比之下,粗粒度的權限更具通用性,將其實現為一個架構,更有重用價值;而將細粒度的權限判斷實現為一個架構級別的東西就顯得繁瑣,而且不是那么的有必要,用定制的代碼來實現就更簡潔,更靈活。
所以細粒度控制應該在底層解決,Resource在實例化的時候,必需指定Owner和GroupPrivilege在對Resource進行操作時也必然會確定約束類型:究竟是OwnerOK還是GroupOK還是AllOK。Group應和Role嚴格分離User和Group是多對多的關系,Group只用于對用戶分類,不包含任何Role的意義;Role只授予User,而不是Group。如果用戶需要還沒有的多種Privilege的組合,必須新增Role。Privilege必須能夠訪問Resource,同時帶User參數,這樣權限控制就完備了。
思想:
權限系統的核心由以下三部分構成:1.創造權限,2.分配權限,3.使用權限,然后,系統各部分的主要參與者對照如下:1.創造權限 - Creator創造,2.分配權限 - Administrator 分配,3.使用權限 - User:
1. Creator 創造 Privilege, Creator 在設計和實現系統時會劃分,一個子系統或稱為模塊,應該有哪些權限。這里完成的是 Privilege 與 Resource 的對象聲明,并沒有真正將 Privilege 與具體Resource 實例聯系在一起,形成Operator。
2. Administrator 指定 Privilege 與 Resource Instance 的關聯。在這一步, 權限真正與資源實例聯系到了一起, 產生了Operator(Privilege Instance)。Administrator利用Operator這個基本元素,來創造他理想中的權限模型。如,創建角色,創建用戶組,給用戶組分配用戶,將用戶組與角色關聯等等...這些操作都是由 Administrator 來完成的。
3. User 使用 Administrator 分配給的權限去使用各個子系統。Administrator 是用戶,在他的心目中有一個比較適合他管理和維護的權限模型。于是,程序員只要回答一個問題,就是什么權限可以訪問什么資源,也就是前面說的 Operator。程序員提供 Operator 就意味著給系統穿上了盔甲。Administrator 就可以按照他的意愿來建立他所希望的權限框架可以自行增加,刪除,管理Resource和Privilege之間關系。可以自行設定用戶User和角色Role的對應關系。(如果將 Creator看作是 Basic 的發明者, Administrator 就是 Basic 的使用者,他可以做一些腳本式的編程) Operator是這個系統中最關鍵的部分,它是一個紐帶,一個系在Programmer,Administrator,User之間的紐帶。
用一個功能模塊來舉例子。
一.建立角色功能并做分配:
1.如果現在要做一個員工管理的模塊(即Resources),這個模塊有三個功能,分別是:增加,修改,刪除。給這三個功能各自分配一個ID,這個ID叫做功能代號:
Emp_addEmp,Emp_deleteEmp,Emp_updateEmp。
2.建立一個角色(Role),把上面的功能代碼加到這個角色擁有的權限中,并保存到數據庫中。角色包括系統管理員,測試人員等。
3.建立一個員工的賬號,并把一種或幾種角色賦給這個員工。比如說這個員工既可以是公司管理人員,也可以是測試人員等。這樣他登錄到系統中將會只看到他擁有權限的那些模塊。
二.把身份信息加到Session中。
登錄時,先到數據庫中查找是否存在這個員工,如果存在,再根據員工的sn查找員工的權限信息,把員工所有的權限信息都入到一個Hashmap中,比如就把上面的Emp_addEmp等放到這個Hashmap中。然后把Hashmap保存在一個UserInfoBean中。最后把這個UserInfoBean放到Session中,這樣在整個程序的運行過程中,系統隨時都可以取得這個用戶的身份信息。
三.根據用戶的權限做出不同的顯示。
可以對比當前員工的權限和給這個菜單分配的“功能ID”判斷當前用戶是否有打開這個菜單的權限。例如:如果保存員工權限的Hashmap中沒有這三個ID的任何一個,那這個菜單就不會顯示,如果員工的Hashmap中有任何一個ID,那這個菜單都會顯示。
對于一個新聞系統(Resouce),假設它有這樣的功能(Privilege):查看,發布,刪除,修改;假設對于刪除,有"新聞系統管理者只能刪除一月前發布的,而超級管理員可刪除所有的這樣的限制,這屬于業務邏輯(Business logic),而不屬于用戶權限范圍。也就是說權限負責有沒有刪除的Permission,至于能刪除哪些內容應該根據UserRole or UserGroup來決定(當然給UserRole or UserGroup分配權限時就應該包含上面兩條業務邏輯)。
一個用戶可以擁有多種角色,但同一時刻用戶只能用一種角色進入系統。角色的劃分方法可以根據實際情況劃分,按部門或機構進行劃分的,至于角色擁有多少權限,這就看系統管理員賦給他多少的權限了。用戶—角色—權限的關鍵是角色。用戶登錄時是以用戶和角色兩種屬性進行登錄的(因為一個用戶可以擁有多種角色,但同一時刻只能扮演一種角色),根據角色得到用戶的權限,登錄后進行初始化。這其中的技巧是同一時刻某一用戶只能用一種角色進行登錄。
針對不同的“角色”動態的建立不同的組,每個項目建立一個單獨的Group,對于新的項目,建立新的 Group 即可。在權限判斷部分,應在商業方法上予以控制。比如:不同用戶的“操作能力”是不同的(粗粒度的控制應能滿足要求),不同用戶的“可視區域”是不同的(體現在對被操作的對象的權限數據,是否允許當前用戶訪問,這需要對業務數據建模的時候考慮權限控制需要)。
擴展性:
有了用戶/權限管理的基本框架,Who(User/Group)的概念是不會經常需要擴展的。變化的可能是系統中引入新的 What (新的Resource類型)或者新的How(新的操作方式)。那在三個基本概念中,僅在Permission上進行擴展是不夠的。這樣的設計中Permission實質上解決了How 的問題,即表示了“怎樣”的操作。那么這個“怎樣”是在哪一個層次上的定義呢?將Permission定義在“商業方法”級別比較合適。比如,發布、購買、取消。每一個商業方法可以意味著用戶進行的一個“動作”。定義在商業邏輯的層次上,一方面保證了數據訪問代碼的“純潔性”,另一方面在功能上也是“足夠”的。也就是說,對更低層次,能自由的訪問數據,對更高層次,也能比較精細的控制權限。
確定了Permission定義的合適層次,更進一步,能夠發現Permission實際上還隱含了What的概念。也就是說,對于What的How操作才會是一個完整的Operator。比如,“發布”操作,隱含了“信息”的“發布”概念,而對于“商品”而言發布操作是沒有意義的。同樣的,“購買”操作,隱含了“商品”的“購買”概念。這里的綁定還體現在大量通用的同名的操作上,比如,需要區分“商品的刪除”與“信息的刪除”這兩個同名為“刪除”的不同操作。
提供權限系統的擴展能力是在Operator (Resource + Permission)的概念上進行擴展。Proxy 模式是一個非常合適的實現方式。實現大致如下:在業務邏輯層(EJB Session Facade [Stateful SessionBean]中),取得該商業方法的Methodname,再根據Classname和 Methodname 檢索Operator 數據,然后依據這個Operator信息和Stateful中保存的User信息判斷當前用戶是否具備該方法的操作權限。
應用在 EJB 模式下,可以定義一個很明確的 Business層次,而一個Business 可能意味著不同的視圖,當多個視圖都對應于一個業務邏輯的時候,比如,Swing Client以及 Jsp Client 訪問的是同一個 EJB 實現的 Business。在 Business 層上應用權限較能提供集中的控制能力。實際上,如果權限系統提供了查詢能力,那么會發現,在視圖層次已經可以不去理解權限,它只需要根據查詢結果控制界面就可以了。
靈活性:
Group和Role,只是一種輔助實現的手段,不是必需的。如果系統的Role很多,逐個授權違背了“簡單,方便”的目的,那就引入Group,將權限相同的Role組成一個Group進行集中授權。Role也一樣,是某一類Operator的集合,是為了簡化針對多個Operator的操作。
Role把具體的用戶和組從權限中解放出來。一個用戶可以承擔不同的角色,從而實現授權的靈活性。當然,Group也可以實現類似的功能。但實際業務中,Group劃分多以行政組織結構或業務功能劃分;如果為了權限管理強行將一個用戶加入不同的組,會導致管理的復雜性。
Domain的應用。為了授權更靈活,可以將Where或者Scope抽象出來,稱之為Domain,真正的授權是在Domain的范圍內進行,具體的Resource將分屬于不同的Domain。比如:一個新聞機構有國內與國外兩大分支,兩大分支內又都有不同的資源(體育類、生活類、時事政治類)。假如所有國內新聞的權限規則都是一樣的,所有國外新聞的權限規則也相同。則可以建立兩個域,分別授權,然后只要將各類新聞與不同的域關聯,受域上的權限控制,從而使之簡化。
權限系統還應該考慮將功能性的授權與資源性的授權分開。很多系統都只有對系統中的數據(資源)的維護有權限控制,但沒有對系統功能的權限控制。
權限系統最好是可以分層管理而不是集中管理。大多客戶希望不同的部門能且僅能管理其部門內部的事務,而不是什么都需要一個集中的Administrator或Administrators組來管理。雖然你可以將不同部門的人都加入Administrators組,但他們的權限過大,可以管理整個系統資源而不是該部門資源。
正向授權與負向授權:正向授權在開始時假定主體沒有任何權限,然后根據需要授予權限,適合于權限要求嚴格的系統。負向授權在開始時假定主體有所有權限,然后將某些特殊權限收回。
權限計算策略:系統中User,Group,Role都可以授權,權限可以有正負向之分,在計算用戶的凈權限時定義一套策略。
系統中應該有一個集中管理權限的AccessService,負責權限的維護(業務管理員、安全管理模塊)與使用(最終用戶、各功能模塊),該AccessService在實現時要同時考慮一般權限與特殊權限。雖然在具體實現上可以有很多,比如用Proxy模式,但應該使這些Proxy依賴于AccessService。各模塊功能中調用AccessService來檢查是否有相應的權限。所以說,權限管理不是安全管理模塊自己一個人的事情,而是與系統各功能模塊都有關系。每個功能模塊的開發人員都應該熟悉安全管理模塊,當然,也要從業務上熟悉本模塊的安全規則。
技術實現:
1.表單式認證,這是常用的,但用戶到達一個不被授權訪問的資源時,Web容器就發
出一個html頁面,要求輸入用戶名和密碼。
2.一個基于Servlet Sign in/Sign out來集中處理所有的Request,缺點是必須由應用程序自己來處理。
3.用Filter防止用戶訪問一些未被授權的資源,Filter會截取所有Request/Response,
然后放置一個驗證通過的標識在用戶的Session中,然后Filter每次依靠這個標識來決定是否放行Response。
這個模式分為:
Gatekeeper :采取Filter或統一Servlet的方式。
Authenticator: 在Web中使用JAAS自己來實現。
用戶資格存儲LDAP或數據庫:
1. Gatekeeper攔截檢查每個到達受保護的資源。首先檢查這個用戶是否有已經創建
好的Login Session,如果沒有,Gatekeeper 檢查是否有一個全局的和Authenticator相關的session?
2. 如果沒有全局的session,這個用戶被導向到Authenticator的Sign-on 頁面,
要求提供用戶名和密碼。
3. Authenticator接受用戶名和密碼,通過用戶的資格系統驗證用戶。
4. 如果驗證成功,Authenticator將創建一個全局Login session,并且導向Gatekeeper
來為這個用戶在他的web應用中創建一個Login Session。
5. Authenticator和Gatekeepers聯合分享Cookie,或者使用Tokens在Query字符里。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/bndxgae/archive/2006/08/01/1006821.aspx
轉載于:https://www.cnblogs.com/hsapphire/archive/2010/05/20/1739774.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: 基于Visual Studio2010讲
- 下一篇: 跨浏览器的CSS固定定位{positio