權(quán)限控制
引言
早在剛實(shí)習(xí)的時(shí)候,接觸到的一個(gè)項(xiàng)目中涉及到了用戶的權(quán)限管理控制,當(dāng)時(shí)覺得很厲害,并且完全看不懂,當(dāng)時(shí)的那個(gè)項(xiàng)目采用的是Jfinal框架,但如今自己要做一個(gè)項(xiàng)目了,打算采用SpringBoot來做,其中后臺(tái)管理也需要權(quán)限來控制,因此在積攢了一年的經(jīng)驗(yàn)后開始嘗試接觸這些東西了,今天就來說一下后臺(tái)管理的權(quán)限控制吧!
思路
我認(rèn)為權(quán)限管理總共分為三個(gè)步驟:
1、項(xiàng)目啟動(dòng)的時(shí)候
2、用戶登錄的時(shí)候
3、用戶操作相應(yīng)模塊的時(shí)候


權(quán)限設(shè)計(jì)思路流程
業(yè)務(wù)流程
如上面兩張圖所示為我的思路,下面講一下具體的實(shí)現(xiàn)過程!
一、新建一個(gè)PermissionScanner類繼承CommandLineRunner
@Component
public class PermissionScanner implements CommandLineRunner {
@Autowired
private ModuleMapper moduleMap;
@Autowired
private MethodMapper methodMapper;
@Override
public void run(String... args) throws Exception {
//掃描所有controller,注冊(cè)權(quán)限模塊和方法
new AddPermission().scanner(moduleMap, methodMapper);
System.out.println("掃描了所有controller,項(xiàng)目啟動(dòng)了!");
}
}
二、新建一個(gè)添加權(quán)限的類
@Component
public class AddPermission {
private List<Class<? extends Controller>> excludeClasses = Lists.newArrayList();
private boolean includeAllJarsInLib;
private List<String> includeJars = Lists.newArrayList();
private String suffix = "Controller";
public AddPermission addExcludeClasses(Class<? extends Controller>... clazzes) {
if (clazzes != null) {
for (Class<? extends Controller> clazz : clazzes) {
excludeClasses.add(clazz);
}
}
return this;
}
public AddPermission addExcludeClasses(List<Class<? extends Controller>> clazzes) {
excludeClasses.addAll(clazzes);
return this;
}
public AddPermission addJars(String... jars) {
if (jars != null) {
for (String jar : jars) {
includeJars.add(jar);
}
}
return this;
}
public AddPermission includeAllJarsInLib(boolean includeAllJarsInLib) {
this.includeAllJarsInLib = includeAllJarsInLib;
return this;
}
public AddPermission suffix(String suffix) {
this.suffix = suffix;
return this;
}
//掃描controller包下面的所有方法
public void scanner(ModuleMapper moduleMap, MethodMapper methodMapper){
List<Class<? extends RestController>> controllerClasses = ClassSearcher.of(RestController.class)
.includeAllJarsInLib(includeAllJarsInLib).injars(includeJars).search();
PermissionModule permissionController = null;
for (Class controller : controllerClasses) {
if (excludeClasses.contains(controller)) {
continue;
}
permissionController = (PermissionModule) controller.getAnnotation(PermissionModule.class);
if (permissionController != null) {
Module module = new Module();
module.setControllerkey(controllerKey(controller));
module.setModuleName(permissionController.text());
moduleMap.insertSelective(module);
Method[] methods = controller.getMethods();
for (Method method : methods) {
/*
* 根據(jù)注解類型返回方法的指定類型注解
*/
PermissionMethod permissionMethod = method.getAnnotation(PermissionMethod.class);
if (permissionMethod != null) {
com.beardream.model.Method m = new com.beardream.model.Method();
m.setMethodName(permissionMethod.text());
m.setActionkey(controllerKey(controller) + methodKey(method.getName()));
m.setModuleId(module.getModuleId());
System.out.println(methodMapper.insertSelective(m));
}
}
}
}
}
private String controllerKey(Class<Controller> clazz) {
Preconditions.checkArgument(clazz.getSimpleName().endsWith(suffix),
clazz.getName() + " is not annotated with @ControllerBind and not end with " + suffix);
if (clazz.getPackage().getName().startsWith("com.beardream")) {
String controllerKey = "/" + TextUtil.firstCharToLowerCase(clazz.getSimpleName());
controllerKey = controllerKey.substring(0, controllerKey.indexOf(suffix));
return controllerKey;
}
String controllerKey = "/" + TextUtil.firstCharToLowerCase(clazz.getSimpleName());
controllerKey = controllerKey.substring(0, controllerKey.indexOf(suffix));
return controllerKey;
}
private String methodKey(String methodname) {
String controllerKey = "/" + TextUtil.firstCharToLowerCase(methodname);
return controllerKey;
}
}
三、掃描包下所有Controller類,將其加到集合中
- 核心代碼
public <T> List<Class<? extends T>> search() {
List<String> classFileList = Lists.newArrayList();
if (scanPackages.isEmpty()) {
classFileList = findFiles(classpath, "*.class");
} else {
for (String scanPackage : scanPackages) {
classFileList = findFiles(classpath + File.separator + scanPackage.replaceAll("\\.", "\\" + File.separator), "*.class");
}
}
classFileList.addAll(findjarFiles(libDir));
return extraction(target, classFileList);
}
private static List<String> findFiles(String baseDirName, String targetFileName) {
/**
* 算法簡(jiǎn)述: 從某個(gè)給定的需查找的文件夾出發(fā),搜索該文件夾的所有子文件夾及文件, 若為文件,則進(jìn)行匹配,匹配成功則加入結(jié)果集,若為子文件夾,則進(jìn)隊(duì)列。 隊(duì)列不空,重復(fù)上述操作,隊(duì)列為空,程序結(jié)束,返回結(jié)果。
*/
List<String> classFiles = Lists.newArrayList();
File baseDir = new File(baseDirName);
if (!baseDir.exists() || !baseDir.isDirectory()) {
} else {
String[] files = baseDir.list();
for (int i = 0; i < files.length; i++) {
File file = new File(baseDirName + File.separator + files[i]);
if (file.isDirectory()) {
classFiles.addAll(findFiles(baseDirName + File.separator + files[i], targetFileName));
} else {
if (wildcardMatch(targetFileName, file.getName())) {
String fileName = file.getAbsolutePath();
String open = PathKit.getRootClassPath() + File.separator;
String close = ".class";
int start = fileName.indexOf(open);
int end = fileName.indexOf(close, start + open.length());
System.out.println("++++++++++++++++++++"+fileName.substring(start + open.length(), end)+":"+File.separator);
String guize="";
if ("\\".equals(File.separator)) {
guize="\\\\";
}else{
guize=File.separator;
}
String className = fileName.substring(start + open.length(), end).replaceAll(guize, ".");
classFiles.add(className);
}
}
}
}
return classFiles;
}
重點(diǎn)注意一下面這個(gè)方法
private static <T> List<Class<? extends T>> extraction(Class<T> clazz, List<String> classFileList) {
List<Class<? extends T>> classList = Lists.newArrayList();
for (String classFile : classFileList) {
Class<?> classInFile = Reflect.on(classFile).get();
//在Jfinal中,通過判斷這個(gè)類是否集成了Controller來判斷他是不是控制器進(jìn)行篩選
// 但在這里是spring環(huán)境,因此應(yīng)該判斷他是否有RestController
/* 這段是Jfinal中的代碼
if (classInFile.isAssignableFrom(clazz) && classInFile == clazz){
classList.add((Class<? extends T>) classInFile);
}
*/
Annotation[] annotations=classInFile.getAnnotations();
for (int i=0;i<annotations.length;i++){
if (annotations[i].annotationType().getTypeName().hashCode() == clazz.getTypeName().hashCode()){
classList.add((Class<? extends T>) classInFile);
}
}
}
return classList;
}
在注釋中已經(jīng)標(biāo)明,Jfinal的所有控制器都是繼承了Controller,因此它在這里判斷這個(gè)類是不是Controller的子類來決定他是不是Controller,但在Spring或SpringBoot中,它是通過注解@RestController或者@Controller來表明這個(gè)類是一個(gè)控制器,因此我們稍作修改,判斷了它是否有RestController注解來判斷。也對(duì)應(yīng)了上面AddPermission類中的下面這行代碼
List<Class<? extends RestController>> controllerClasses = ClassSearcher.of(RestController.class)
.includeAllJarsInLib(includeAllJarsInLib).injars(includeJars).search();
介紹到這里差不多核心的東西也已經(jīng)介紹完了,剩下的就只有當(dāng)用戶登錄時(shí)查詢他擁有的權(quán)限以及在操作的時(shí)候來判斷用戶的權(quán)限是否對(duì)應(yīng)了。
后續(xù)
第一次寫簡(jiǎn)書,以前都是看其他大牛的博客文章,如今是自己第一次來寫,可能表述的不是很清楚,希望各位童鞋指正,也可以在下方評(píng)論留言。
如果大家需要的話后面又介紹用戶登錄時(shí)查詢他擁有的權(quán)限以及在操作的時(shí)候來判斷用戶的權(quán)限是否對(duì)應(yīng)。
代碼參考了JFinal中的一些代碼...