Java Springboot的后臺(tái)管理系統(tǒng)的權(quán)限控制流程

權(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)限操作.png
權(quán)限思路流程.png
權(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中的一些代碼...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,500評(píng)論 19 139
  • P2P網(wǎng)絡(luò)借貸雖屬小眾投資行為,但具備了人人可以操作、人人可以參與的普眾性質(zhì),只要手中有錢,不分多少,都可以成為...
    壹點(diǎn)健康生活閱讀 139評(píng)論 0 0
  • 1.推薦單例寫法 封裝成宏定義 調(diào)用實(shí)例: 參考鏈接
    司馬捷閱讀 339評(píng)論 0 1
  • 揚(yáng)馬蹄落飛花一簫一劍天涯 一人一馬成畫寒星點(diǎn)點(diǎn)原是燈火幾家
    只為花開閱讀 374評(píng)論 0 4
  • 你知道的,我們懷念一段時(shí)光,常常不是懷念它的風(fēng)景有多端麗,而是懷念時(shí)光里的自己。 我們?cè)谇啻褐凶哌^多少個(gè)心酸、徘徊...
    Tempt_閱讀 438評(píng)論 0 0

友情鏈接更多精彩內(nèi)容