Java实现⽤户权限控制功能
项⽬中使⽤数据库表+视图+存储过程+缓存的⽅式实现⽤户权限的控制。通过⽤户表、⾓⾊表、权限表以及⽤户⾓⾊表、⾓⾊权限表两个中间表可以得到⼀个⽤户对应的权限有哪些。创建⼀个视图将这五个表连接起来,可以查询出每个⽤户对应的权限有哪些。Java层通过调⽤存储过程,存储过程再查询该视图,⽤户权限可以传递到Java层。Java 层将⽤户权限缓存起来,可以提⾼效率。本⽂主要介绍:“涉及到的数据库表”、“创建连接表的视图”、“调⽤视图的存储过程”、“Java层加载权限信息”、“Java层判断⽤户是否有权限”、“可以使⽤Excel管理⾓⾊权限信息”。
1、涉及到的数据库表
涉及5个数据库表:
(1)⽤户表t_staff
DROP TABLE IF EXISTS T_Staff;
CREATE TABLE T_Staff                  -- 门店⽤户
(
F_ID INT NOT NULL AUTO_INCREMENT,      -- ID
F_Name VARCHAR(12) NOT NULL,      -- 店员名称
F_Phone VARCHAR(32) NOT NULL,      -- ⼿机号码
F_ICID VARCHAR(20) NULL,      -- ⾝份证号码
F_WeChat VARCHAR(20) NULL,      -- 号
F_OpenID VARCHAR(100)  NULL,      -- ⽤户的唯⼀标识。
F_Unionid VARCHAR(100) NULL,      -- 只有将绑定到开放平台帐号后,才会出现该字段,同⼀⽤户的unionid是唯⼀的。
F_pwdEncrypted VARCHAR(0) NULL,      -- 公钥加密后的⽤户密码
F_Salt VARCHAR(32) NOT NULL,          -- 加盐后的MD5值
F_PasswordExpireDate DATETIME NULL,        -- 密码有效期
F_IsFirstTimeLogin INT NOT NULL DEFAULT 1,    -- ⾸次登录成功?
F_ShopID INT NOT NULL,        -- 门店ID
F_DepartmentID INT NOT NULL,      -- 所属的部门。默认为1
F_Status INT NOT NULL,        -- 0,在职。1,离职。
F_CreateDatetime DATETIME NOT NULL DEFAULT now(),    -- 创建时间
F_UpdateDatetime DATETIME NOT NULL DEFAULT now(),  -- 修改时间
PRIMARY KEY (F_ID),
FOREIGN KEY (F_ShopID) REFERENCES T_Shop(F_ID),
FOREIGN KEY (F_DepartmentID) REFERENCES T_Department(F_ID)
-- FOREIGN KEY (F_IDInPOS) REFERENCES T_POS(F_ID) -- ,
-- UNIQUE KEY F_ICID (F_ICID),
-- UNIQUE KEY F_WeChat (F_WeChat)
-
- UNIQUE KEY F_Phone (F_Phone)    status为1的phone可以重新创建⼀个正常使⽤的staff。
)
ENGINE=InnoDB
DEFAULT CHARACTER SET='utf8' COLLATE='utf8_unicode_ci';
(2)⾓⾊表t_role
DROP TABLE IF EXISTS T_Role;
CREATE TABLE T_Role                    -- ⾓⾊
(
F_ID INT NOT NULL AUTO_INCREMENT,      -- ID
F_Name VARCHAR(20) NOT NULL,      -- ⾓⾊名称
F_CreateDatetime DATETIME NOT NULL DEFAULT now(),    -- 创建时间
F_UpdateDatetime DATETIME NOT NULL DEFAULT now(),  -- 修改时间
PRIMARY KEY (F_ID)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET='utf8' COLLATE='utf8_unicode_ci';
(3)权限表t_permission
关键字段F_SP
DROP TABLE IF EXISTS T_Permission;
CREATE TABLE T_Permission                -- 操作权限表
(
F_ID INT NOT NULL AUTO_INCREMENT,      -- ID
F_SP VARCHAR(80) NOT NULL,        -- 对应操作的SP
F_Name VARCHAR(20) NOT NULL,            -- 操作名称
F_Domain VARCHAR(16) NOT NULL ,          -- 领域名称
F_Remark VARCHAR(32) NOT NULL,      -- 操作备注
F_CreateDatetime DATETIME NOT NULL DEFAULT now(),    -- 创建时间
PRIMARY KEY (F_ID)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET='utf8' COLLATE='utf8_unicode_ci';
(4)⽤户⾓⾊表t_staffrole
DROP TABLE IF EXISTS T_StaffRole;
CREATE TABLE T_StaffRole                -- ⽤户⾓⾊表
(
F_ID INT NOT NULL AUTO_INCREMENT,      -- ID
F_StaffID INT NOT NULL,        -- ⽤户ID
F_RoleID INT NOT NULL,        -- ⾓⾊ID
PRIMARY KEY (F_ID),
FOREIGN KEY (F_StaffID ) REFERENCES T_Staff(F_ID),
FOREIGN KEY (F_RoleID ) REFERENCES T_Role(F_ID),
UNIQUE KEY F_StaffID(F_StaffID)  -- ... ⼀个员⼯有且只有⼀个⾓⾊
)
ENGINE=InnoDB
DEFAULT CHARACTER SET='utf8' COLLATE='utf8_unicode_ci';
(5)⾓⾊权限表t_role_permission
DROP TABLE IF EXISTS T_Role_Permission;
CREATE TABLE T_Role_Permission                -- ⾓⾊操作中间表
(
F_ID INT NOT NULL AUTO_INCREMENT,      -- ID
F_RoleID INT NOT NULL,          -- ⾓⾊ID
F_PermissionID INT NOT NULL,      -- 操作ID
PRIMARY KEY (F_ID),
FOREIGN KEY (F_RoleID ) REFERENCES T_Role(F_ID),
FOREIGN KEY (F_PermissionID ) REFERENCES T_Permission(F_ID)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET='utf8' COLLATE='utf8_unicode_ci';
2、创建连接表的视图
根据5张数据库表创建⽤户-⾓⾊-权限视图v_staff_permission:
DROP VIEW IF EXISTS V_Staff_Permission;      -- ⽤户, ⾓⾊, 权限视图
CREATE VIEW V_Staff_Permission
AS
SELECT s.F_ID AS StaffID, s.F_Name AS StaffName,r.F_ID AS RoleID, r.F_Name AS RoleName, p.F_SP, p.F_Name PermissionName, p.F_Remark FROM t_staff s
LEFT JOIN t_staffrole sr ON sr.F_StaffID = s.F_ID
JOIN t_role r ON sr.F_RoleID = r.F_ID
JOIN t_role_permission ap ON ap.F_RoleID = r.F_ID
JOIN t_permission p ON p.F_ID = ap.F_PermissionID
WHERE s.F_Status = 0;
3、调⽤视图的存储过程
存储过程SP_Staff_RetrieveNPermission调⽤视图,Java层调⽤存储过程获取所有的⽤户权限:
DROP PROCEDURE IF EXISTS `SP_Staff_RetrieveNPermission`;
CREATE DEFINER=`root`@`localhost` PROCEDURE `SP_Staff_RetrieveNPermission` (
OUT iErrorCode INT,
OUT sErrorMsg VARCHAR(64),
IN iPageIndex INT,
IN iPageSize INT,
OUT iTotalRecord INT
)
BEGIN
DECLARE recordIndex INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET iErrorCode := 3;
SET sErrorMsg := 'Êý¾Ý¿â´íÎó';
ROLLBACK;
END;
START TRANSACTION;
SET iPageIndex = iPageIndex - 1;
SET recordIndex = iPageIndex * iPageSize;
SET @i = 1;
SELECT @i:=@i+1 AS F_ID, StaffID, StaffName, RoleID, RoleName, F_SP, PermissionName, F_Remark
FROM V_Staff_Permission
ORDER BY F_ID DESC LIMIT recordIndex,iPageSize;
SELECT count(1) into iTotalRecord
FROM V_Staff_Permission;
SET iErrorCode := 0;
SET sErrorMsg := '';
COMMIT;
END;
4、Java层加载权限信息
StaffPermissionCache类⽤来缓存⽤户权限信息,存放到hashtable中:
/** 权限的查询需要⽀持根据staffID查询,也要⽀持根据staffID和sp名查询。后者⽤于检查特定⽤户有⽆特定权限,操作频繁,需要快速查询到 */
@Component("staffPermissionCache")
@Scope("prototype")
public class StaffPermissionCache extends BaseCache {
private Log logger = Log(BaseCache.class);
/** key:staffID+sp名 <br />
* value:StaffPermission */
public Hashtable<String, BaseModel> ht;
@Resource
private StaffPermissionBO staffPermissionBO;
……
在程序启动时,开始加载⽤户权限信息:
/** 加载公共DB中的公司缓存和每个私有DB的普通缓存和同步缓存。 */
@PostConstruct
private void load() {
resolveCurrentEnvAndDomain();
……
staffPermissionCache.DbName());
load调⽤doLoad⽅法:
public void load(String dbName) {
doLoad(dbName);
register(dbName);
}
doLoad⽅法调⽤staffPermissionBO的retrieveNObject,最后调⽤了SP_Staff_RetrieveNPermission存储过程,获取到所有的⽤户和他们对应的权限信息,存放到List<?> ls:@SuppressWarnings("unchecked")
protected void doLoad(String dbName) {
logger.info("加载缓存(" + sCacheName + ")...");
BaseModel b = getMasterModel(dbName);
DataSourceContextHolder.setDbName(dbName);
List<?> ls = getMasterBO().retrieveNObject(BaseBO.SYSTEM, BaseBO.INVALID_CASE_ID, b);
if (getMasterBO().getLastErrorCode() != EnumErrorCode.EC_NoError) {
<("加载缓存(" + sCacheName + ")失败!请重启服务器!!错误信息:" + getMasterBO().printErrorInfo());
throw new RuntimeException(BaseModel.ERROR_Tag);
}
doLoadSlave(ls, dbName);
logger.info("加载缓存(" + sCacheName + ")成功!");
writeN((List<BaseModel>) ls); // 将DB的数据缓存进本对象的hashtable中
}
writeN((List<BaseModel>) ls) ⽅法把ls写⼊到了hashtable容器中:
/** 清除所有旧的缓存,写⼊新的数据 <br />
* TODO:⽬前存在的问题:load()中缓存加载时,并不能确定要加载多少个对象。 */
public void writeN(List<BaseModel> ls) {
System.out.println("writeN正在加锁...." + lock.writeLock().getHoldCount());
lock.writeLock().lock();
try {
doWriteN(ls);
} catch (Exception e) {
<("writeN异常:" + e.getMessage());
}
lock.writeLock().unlock();
System.out.println("writeN已经解锁" + lock.writeLock().getHoldCount());
}
protected void doWriteN(List<BaseModel> ls) {
setCache(ls);
}
@Override
protected void setCache(List<BaseModel> list) {
listToHashtable(list);
}
以staffID+Sp为key,bm为Value存放到HashTable容器缓存起来:
@Override
protected Hashtable<String, BaseModel> listToHashtable(List<BaseModel> ls) {
if (ls == null) {
throw new RuntimeException("参数ls为null!");
}
ht = new Hashtable<String, BaseModel>(ls.size()); // 每⼀次设置缓存,都不会、也不能污染到本对象的副本中的ht数据成员
for (BaseModel bm : ls) {
ht.put(String.valueOf(((StaffPermission) bm).getStaffID()) + ((StaffPermission) bm).getSp(), bm);
}
return ht;
}
5、Java层判断⽤户是否有权限
所有⽤户的对数据库的操作都是通过调⽤存储过程来实现的,在Action层调⽤BO层的⽅法的时候,已经指定了操作对应的存储过程,如下图的第⼆个参数BaseBO.CASE_ResetMyPassword,它对应了⼀
个存储过程:
Staff staffUpdated = (Staff) staffBO.updateObject(getStaffFromSession(session).getID(), BaseBO.INVALID_CASE_ID, staff);
调⽤过程如下:
@SuppressWarnings("static-access")
protected BaseModel update(int staffID, int iUseCaseID, BaseModel s) {
checkMapper();
if (!checkUpdatePermission(staffID, iUseCaseID, s)) {
lastErrorCode = EnumErrorCode.EC_NoPermission;
lastErrorMessage = "权限不⾜";
return null;
}
……
@Override
protected boolean checkUpdatePermission(int staffID, int iUseCaseID, BaseModel s) {
switch (iUseCaseID) {
case CASE_ResetMyPassword:
return checkStaffPermission(staffID, SP_Staff_ResetPassword);
case CASE_ResetOtherPassword:
return checkStaffPermission(staffID, SP_Staff_ResetPassword);
case CASE_Staff_Update_OpenidAndUnionid:
return checkStaffPermission(staffID, SP_Staff_Update_OpenidAndUnionid);
case CASE_Staff_Update_Unsubscribe:
return checkStaffPermission(staffID, SP_Staff_Update_Unsubscribe);
default:
return checkStaffPermission(staffID, SP_Staff_Update);
}
}
在checkStaffPermission⽅法检查是否有权限:
/** 检查1个staff有⽆权限permission */
protected boolean checkStaffPermission(int staffID, String permission) {
if (staffID == SYSTEM) {
foreign key references用法return true;
}
StaffPermissionCache spc = (StaffPermissionCache) DbName(), EnumCacheType.ECT_StaffPermission);
ErrorInfo ecOut = new ErrorInfo();
StaffPermission sp = ad1(staffID, permission, ecOut);
if (sp == null) {
lastErrorMessage = "Staff(" + staffID + ")没有权限(" + permission + ")";
logger.debug(lastErrorMessage);
return false;
}
return true;
}
在read1⽅法中,根据⽤户ID和存储过程拼接key,查hashtable是否有这个key,从⽽判断⽤户是否有权限:
public StaffPermission read1(int staffID, String permissionName, ErrorInfo ecOut) {
StaffPermission bmToRead = null;
String key = String.valueOf(staffID) + permissionName;
if (ht.containsKey(key)) {
bmToRead = (StaffPermission) ht.get(key);
ecOut.setErrorCode(EnumErrorCode.EC_NoError);
} else {
bmToRead = null;
ecOut.setErrorCode(EnumErrorCode.EC_NoSuchData);
logger.debug("⽆此权限。key=" + key);
}
return bmToRead;
}
6、可以使⽤Excel管理⾓⾊权限信息
使⽤excel可以⽅便管理⾓⾊权限信息:
后⾯⼏个⼯作表配置⾓⾊的权限:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。