【前言】
原来的后台(前辈搭好的)权限管理已经能自动生成了,不过必须要有操作者响应某个操作(方法级别的),拦截器检查该操作是否有权限注解,有的话查询该注解的value(权限名称)是否已经存在,如果不存在则新建该权限,权限生成好了才能授予用户相应权限。这个过程其实用用也没啥问题,就是别扭,权限不能提前授予用户,而是让第一个操作者先“踩坑”然后再填坑。
基于前面的问题,本文在原先的注解基础上设计了项目启动时自动生成的权限。
注:用户-角色-权限关系,权限的拦截检测等不在本文讨论范围。
【原理】
1. 扫描指定路径(放置Controller类的包的全路径)
2. 获取该路径下(递归子孙包)所有的Controller类的Class对象
3. 获取数据库已有的权限(父权限+对应的子权限)
4. 遍历2中的所有Class对象,判断是否有权限注解,再与3中的父权限比较是否存在
4.1不存在。创建父权限,创建Class对象里的含权限注解的所有Method级别的子权限
4.2存在。不创建父权限,遍历Class对象里的含权限注解的所有Method级别的子权限,判断是否已经存在该子权限,不存在则新建权限
5.项目启动后即执行以上过程。
【正文】
创建权限注解类
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionOwn {
String name();//权限名称
}
Controller和其方法Method加注解。例如:
@PermissionOwn(name = "权限管理")
public class PermissionController extends BaseController {
@PermissionOwn(name = "权限列表")
public void perms() {
......
}
......
核心工具类,有点长,还可以优化的
public class PermissionAutoBuild {
private static final Logger LOG = LoggerFactory.getLogger(PermissionAutoBuild.class);
public void build(String packageUrl) {
try {
// 是否循环遍历
boolean recursive = false;
// 获取包的名字和文件路径
String packageName = packageUrl;
String packagePath = packageName.replace('.', '/');
// 存放获取的class文件
Map<String, Class<?>> classes = new HashMap<>();
// 获取指定路径下所有class文件
findAndAddClassesInPackageByFile(packageName, packagePath, recursive, classes);
if (classes.isEmpty()) {
LOG.error("PermissionAutoBuild [Set<Class<?>> classes = NULL]");
return;
}
// 查询数据库中已有的权限
//获取父权限
List<Record> parentRrcord = Db.find("SELECT * FROM auth_permission WHERE parent_id=0");
Map<String, List<Record>> permissionMap = new HashMap<String, List<Record>>();
//获取父权限对应的子权限
for (Record r : parentRrcord) {
List<Record> kidRecord = Db
.find("SELECT * FROM auth_permission WHERE parent_id=" + r.get("id"));
permissionMap.put(r.getStr("name"), kidRecord);
}
// 获取每个class里的注解和方法注解
Set<Entry<String, Class<?>>> entrySet = classes.entrySet();
Iterator<Entry<String, Class<?>>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Entry<String, Class<?>> next = iterator.next();
String controllerAlias = next.getKey();
Class<?> clazz = next.getValue();
PermissionOwn permissionOwn = clazz.getAnnotation(PermissionOwn.class);
if (permissionOwn != null) {
String name = permissionOwn.name();
List<Record> list = permissionMap.get(name);
//判断父权限是否存在
if (!permissionMap.containsKey(name)) {
// 创建父权限
Db.save("auth_permission", new Record().set("name", name).set("alias", controllerAlias)
.set("parent_id", 0).set("permission", "").set("add_time", new Date()));
LOG.info(String.format("自动创建权限[controllerAlias=%s,controllerName=%s]", controllerAlias, name));
}
Record controllerPermission = getControllerPermission(controllerAlias);
if (controllerPermission == null) {
throw new RuntimeException("controller权限不存在,无法建立权限!");
}
//遍历类中的方法,判断是否有权限注解,再判断权限是否已经存在
Method[] methods = clazz.getMethods();
for (Method m : methods) {
boolean flag = false;
PermissionOwn methodPermission = m.getAnnotation(PermissionOwn.class);
if (methodPermission != null) {
String mAlias = m.getName();
String mName = methodPermission.name();
if(CollectionUtils.isEmpty(list)){
//首次创建
flag=true;
}else{
for (Record r : list) {
if (mName.equals(r.getStr("name"))) {
flag=false;
break;
}
flag=true;
}
}
if(flag){
// 创建子权限
Db.save("auth_permission",
new Record().set("name", mName).set("alias", mAlias)
.set("parent_id", controllerPermission.get("id"))
.set("permission", controllerAlias + "-" + mAlias)
.set("add_time", new Date()));
LOG.info(String.format(
"自动创建权限[controllerAlias=%s,methodAlias=%s,methodName=%s,permission=%s]",
controllerAlias, mAlias, mName, controllerAlias + "-" + mAlias));
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
LOG.error("创建权限失败:e==" + e);
return;
}
LOG.info("创建权限成功");
}
/**
* 以文件的形式来获取包下的所有Class
* 注:该方法源自网友,做了部分修改,增加了获取Class目录
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
private void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
Map<String, Class<?>> classes) {
//获取Class目录
String path = PermissionAutoBuild.class.getResource("/").getPath();
// 获取此包的目录 建立一个File
File dir = new File(path+packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
LOG.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
classes.put(className,
Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
LOG.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
private Record getControllerPermission(String controllerAlias) {
Record controllerRecord = Db.findFirst(
"select * from auth_permission where alias = ? and permission = ? and parent_id = 0",
controllerAlias, "");
return controllerRecord;
}
}
在JFinal的config类里的afterJFinalStart()中添加
//构建权限
LOG.info("构建权限启动");
PermissionAutoBuild builder = new PermissionAutoBuild();
builder.build("top.rushpeak.edu06.admin");//此处填写你的Controller文件所在包的全名
权限表
CREATE TABLE `auth_permission` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(50) NOT NULL COMMENT '权限名字',
`alias` varchar(50) NOT NULL COMMENT '权限别名',
`parent_id` int(10) unsigned NOT NULL COMMENT '父级权限',
`permission` varchar(100) NOT NULL COMMENT '权限值',
`add_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '添加的时间',
PRIMARY KEY (`id`),
UNIQUE KEY `auth_permission_alias_parent_id` (`alias`,`parent_id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='后台权限';
以上仅完成了项目启动时,自动构建权限。