后台管理框架搭建

搭建一套通用的后台管理框架对于以后的快速开发时是非常重要的。通常框架需要包含权限验证、日志、及一些基础数据的增删改查功能。

本框架采用Spring MVC+Mybatis+Freemarker+Adminlte前端 组合在一起搭建一个管理系统。

大概的样子如下:
后台管理框架搭建_第1张图片

后台管理框架搭建_第2张图片

后台管理框架搭建_第3张图片

1.权限

角色->应用->模块->功能
后台管理框架搭建_第4张图片

数据库专门建立了一张功能表sys_Function,它属于某个模块,它有个权限值字段(该值为2的指数倍,为什么,下面再解释?)。
后台管理框架搭建_第5张图片

如何判断某个角色是否可以进行某个功能操作(例如删除模块)?
如上图角色权限表sys_RolePermission所示,该表有个字段P_Value等于P_ModuleCode模块所选功能的权限值之和。
例如功能管理模块定义了4个功能,分别为查看、添加、修改、删除功能,这4个功能的权限值分别为1、2、4、8,那么sys_RolePermission的该模块的权限值P_Value如果等于1,则表示只有查看功能,如果等于3,则表示具有查看和添加功能,如果要具有所有功能,则值要等于1+2+4+8=15。

那如果某个用户多个角色对同一个模块有不同的权限值,例如用户x具有角色1和角色2,角色1对模块a的权限值为3,角色2对模块a的权限值为9,那用户对模块a的权限应该是多少呢?其实只要做一个按位与操作即可,即1&9=11,并不是1+9=10.这就是为什么让功能值设置为2的指数倍形式的原因,其实质是为了进行二进制的操作。假如有4个功能,则将权限值用4位的二进制形式表示,每一位分别表示一个功能,0表示无权限,1表示有权限。同理3的二进制形式为0011,9的二进制形式为1001,作与运算后的结果为1011,即权限值为11。
最后判断是否具有某功能,则用该功能权限值与计算后的角色权限值作与运算,例如上面判断是否具有修改功能(权限值为4),将4与11作与运算,4&11=0,则表示无权限,而添加功能(权限值2),2&11=2,则表示有权限,同理删除功能(权限值8),8&11=8表示有权限。

2.日志

写日志功能用到的是AOP切面技术,这样可以与实际的业务代码相分离,互不影响。

<beans:bean id="aspectEventLog" class="com.jykj.check.filter.EventLogAspect" />
    
     
    <aop:config proxy-target-class="true">  
        <aop:aspect  ref="aspectEventLog">  
            <aop:pointcut id="myService"  
                expression="@annotation(com.jykj.check.annotation.Operation) and execution(* com.jykj.check.service..*.*(..)) " />  
            <aop:after-returning pointcut-ref="myService" method="doAfterReturning"/>  
            <aop:after-throwing pointcut-ref="myService" method="doAfterThrowing" throwing="ex"/>
        aop:aspect>  
    aop:config>
EventLogAspect.java
package com.jykj.check.filter;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSON;
import com.jykj.check.annotation.Operation;
import com.jykj.check.exception.AuthorizationException;
import com.jykj.check.service.SysEventService;

//事件日志 切面,凡是带有 @Operation 注解的service方法都将会写日志
public class EventLogAspect {
    @Autowired
    SysEventService sysEventService;

    public void doAfterReturning(JoinPoint jp) throws AuthorizationException {
        Method soruceMethod = getSourceMethod(jp);
        if(soruceMethod!=null){
            Operation oper = soruceMethod.getAnnotation(Operation.class);
            if (oper != null) {
                sysEventService.insertEventLog(oper.type(),oper.desc()+"("+extractParam(jp.getArgs(),oper.arguDesc())+") 成功");
                System.out.println("切面日志:"+oper.desc()+"("+extractParam(jp.getArgs(),oper.arguDesc())+") 成功");
            }
        }
    }
    public void doAfterThrowing(JoinPoint jp, Throwable ex) throws AuthorizationException {
        Method soruceMethod = getSourceMethod(jp);
        if(soruceMethod!=null){
            Operation oper = soruceMethod.getAnnotation(Operation.class);
            if (oper != null) {
                sysEventService.insertEventLog(oper.type(),oper.desc()+
                        "("+extractParam(jp.getArgs(),oper.arguDesc())+" 出现异常:"+ex.getMessage());
            }
        }
    }
    private Method getSourceMethod(JoinPoint jp){
        Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod();
        try {
            return jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    private String extractParam(Object[] objParam, String[] arguDesc) {
        StringBuilder sb = new StringBuilder();
        int len = objParam.length.length?objParam.length:arguDesc.length;//取最小值
        for (int i = 0; i < len; i++) {
            //空字符串将不解析,8个原生数据类型以及字符串直接输出,对象用json输出
            if(arguDesc[i]!=null && !arguDesc[i].trim().isEmpty()){
                Object obj = objParam[i];
                if(obj instanceof String)
                    sb.append(arguDesc[i]+":"+objParam[i]+",");
                else if(obj instanceof Integer || obj instanceof Byte || obj instanceof Short || obj instanceof Character
                        || obj instanceof Long || obj instanceof Double || obj instanceof Float || obj instanceof Boolean){
                    sb.append(arguDesc[i]+":"+objParam[i]+",");
                }
                else{
                    sb.append(arguDesc[i]+":"+JSON.toJSONString(obj)+",");
                }
            }
        }
        String rs = sb.toString(); 
        rs = rs.substring(0,rs.length()-1);
        return rs.length()<=400?rs:rs.substring(0,400);
    }
}

再需要写日志的方法(通常是service方法)上加一个自定义的注解@Operation即可

package com.jykj.check.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Descrption该注解描述方法的操作类型和方法的参数意义
 */
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface Operation {
    /**
     * @Description描述操作类型   为必填项,1:登录日志2:操作日志
     */
    int type();

    /**
     * @Description描述操作意义   比如申报通过或者不通过等
     */
    String desc() default ""; 

    /**
     * @Description描述操作方法的参数意义 数组长度需与参数长度一致,否则会参数与描述不一致的情况
     */
    String[] arguDesc() default {};
}

3.前端框架

前端框架使用的是AdminLTE,其包含了很多技术例如bootstrap、datatables等等,非常繁杂。
可以从官网下载该框架:AdminLTE官网

4.其他

框架涉及到的东西实在是太多太多,这里就不一 一列举了。
开发环境:
Spring tool suite 3.9+JDK8+Sqlserver2008
Import项目后如果发现一大堆错误,通常是pom.xml文件中所依赖的jar包未下载,需要手动或在线下载jar包到你本地的mven仓库中。
下面附上框架项目的下载地址,里面包含项目以及Sqlserver的数据库Check的备份包。

框架项目下载地址:框架项目下载

《道德经》第三章:
不上贤,使民不争;不贵难得之货,使民不为盗;不见可欲,使民不乱。是以圣人之治也,虚其心,实其腹,弱其志,强其骨,恒使民无知、无欲也。使夫知不敢、弗为而已,则无不治矣。

译文:不推崇有才德的人,导使老百姓不互相争夺;不珍爱难得的财物,导使老百姓不去偷窃;不显耀足以引起贪心的事物,导使民心不被迷乱。因此,圣人的治理原则是:排空百姓的心机,填饱百姓的肚腹,减弱百姓的竞争意图,增强百姓的筋骨体魄,经常使老百姓没有智巧,没有欲望。致使那些有才智的人也不敢妄为造事。圣人按照“无为”的原则去做,办事顺应自然,那么,天才就不会不太平了。

无为并非啥事都不做,无为是执政者不为自己去攫取民众的利益。

你可能感兴趣的:(SpringMVC)