你知道權限管理的角色受權與認證嗎?

你知道權限管理的角色受權與認證嗎?

權限管理中,角色受權與認證屬於權限模塊中的關鍵模塊,角色受權便是將角色可以操做的菜單資源分配給指定角色的行爲,角色認證便是當用戶扮演指定角色登陸系統後系統對於用戶操做的資源進行權限校驗的操做,意思這裏說明白了,那麼在代碼中應該具體怎麼實現呢?javascript

角色受權與認證的方式

  • 前端頁面展現控制
  • 後端權限訪問控制

案例實操

角色受權

樹形數據展現

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PWj63zYa-1599723384785)(https://imgkr.cn-bj.ufileos.c...]php

完成角色記錄基本 crud 功能以後,接下來實現角色受權功能,這裏實現角色受權首先完成待受權資源顯示功能。對於資源的顯示,這裏使用開源的 tree 插件 ztreecss

資源數據查詢後端實現

前端 ztree 顯示的資源數據格式參考這裏html

  • ModuleMapper.xml
<select id="queryAllModules" resultType="com.xxxx.crm.dto.TreeDto">
    select
    id,
    IFNULL(parent_id,0) as pId,
    module_name AS name
    from t_module
    where is_valid=1
</select>
  • ModuleService.java
public List<TreeDto> queryAllModules(){
    return moduleMapper.queryAllModules();
}
  • ModuleController.java
@RequestMapping("queryAllModules")
@ResponseBody
public List<TreeDto> queryAllModules(){
    return moduleService.queryAllModules();
}
資源數據 ztree 顯示
  • role.js 添加受權點擊事件
//頭工具欄事件
table.on('toolbar(roles)', function(obj){
    var checkStatus = table.checkStatus(obj.config.id);
    switch(obj.event){
        case "add":
            openAddOrUpdateRoleDialog();
            break;
        case "grant":
            openAddGrantDailog(checkStatus.data);
            break;
    };
});

function openAddGrantDailog(datas){
    if(datas.length==0){
        layer.msg("請選擇待受權角色記錄!", {icon: 5});
        return;
    }
    if(datas.length>1){
        layer.msg("暫不支持批量角色受權!", {icon: 5});
        return;
    }
    var url  =  ctx+"/role/toAddGrantPage?roleId="+datas[0].id;
    var title="角色管理-角色受權";
    layui.layer.open({
        title : title,
        type : 2,
        area:["600px","280px"],
        maxmin:true,
        content : url
    });
}
  • RoleController.java 添加視圖轉發方法
@RequestMapping("toAddGrantPage")
public String toAddGrantPage(Integer roleId,Model model){
    model.addAttribute("roleId",roleId);
    return "role/grant";
}
  • 準備顯示資源數據模板

views/role 目錄下添加 grant.ftl 模板文件前端

<html>
<head>
   <link rel="stylesheet" href="${ctx}/static/js/zTree_v3-3.5.32/css/zTreeStyle/zTreeStyle.css" type="text/css">
   <script type="text/javascript" src="${ctx}/static/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
   <script type="text/javascript" src="${ctx}/static/js/zTree_v3-3.5.32/js/jquery.ztree.core.js"></script>
   <script type="text/javascript" src="${ctx}/static/js/zTree_v3-3.5.32/js/jquery.ztree.excheck.js"></script>
</head>
<body>
<div id="test1" class="ztree"></div>
<input id="roleId" value="${roleId!}" type="hidden">
<script type="text/javascript">
   var ctx="${ctx}";
</script>
<script type="text/javascript" src="${ctx}/static/js/role/grant.js"></script>
</body>
</html>
  • 添加 grant.js
var zTreeObj;
$(function () {
    loadModuleInfo();
});
function loadModuleInfo() {
    $.ajax({
        type:"post",
        url:ctx+"/module/queryAllModules"
        dataType:"json",
        success:function (data) {
            // zTree 的參數配置,深刻使用請參考 API 文檔(setting 配置詳解)
            var setting = {
                data: {
                    simpleData: {
                        enable: true
                    }
                },
                view:{
                    showLine: false
                    // showIcon: false
                },
                check: {
                    enable: true,
                    chkboxType: { "Y": "ps", "N": "ps" }
                }
            };
            var zNodes =data;
            zTreeObj=$.fn.zTree.init($("#test1"), setting, zNodes);
        }
    })
}

角色受權

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fUjjBzjZ-1599723384788)(https://imgkr.cn-bj.ufileos.c...]java

權限記錄添加後端實現
  • RoleService.java
public void addGrant(Integer[] mids, Integer roleId) {
        /**
         * 核心表-t_permission  t_role(校驗角色存在)
         *   若是角色存在原始權限  刪除角色原始權限
         *     而後添加角色新的權限 批量添加權限記錄到t_permission
         */
        Role temp =selectByPrimaryKey(roleId);
        AssertUtil.isTrue(null==roleId||null==temp,"待受權的角色不存在!");
        int count = permissionMapper.countPermissionByRoleId(roleId);
        if(count>0){
            AssertUtil.isTrue(permissionMapper.deletePermissionsByRoleId(roleId)<count,"權限分配失敗!");
        }
        if(null !=mids && mids.length>0){
            List<Permission> permissions=new ArrayList<Permission>();
            for (Integer mid : mids) {
                Permission permission=new Permission();
                permission.setCreateDate(new Date());
                permission.setUpdateDate(new Date());
                permission.setModuleId(mid);
                permission.setRoleId(roleId);
                permission.setAclValue(moduleMapper.selectByPrimaryKey(mid).getOptValue());
                permissions.add(permission);
            }
            permissionMapper.insertBatch(permissions);
        }
}
  • RoleController.java
@RequestMapping("addGrant")
@ResponseBody
public ResultInfo addGrant(Integer[] mids,Integer roleId){
    roleService.addGrant(mids,roleId);
    return success("權限添加成功");
}
權限記錄添加前端核心 js

修改 grant.js 文件 添加 ztree 複選框點擊回調 onCheck 事件。node

var zTreeObj;
$(function () {
    loadModuleInfo();
});
function loadModuleInfo() {
    $.ajax({
        type:"post",
        url:ctx+"/module/queryAllModules",
        dataType:"json",
        success:function (data) {
            // zTree 的參數配置,深刻使用請參考 API 文檔(setting 配置詳解)
            var setting = {
                data: {
                    simpleData: {
                        enable: true
                    }
                },
                view:{
                    showLine: false
                    // showIcon: false
                },
                check: {
                    enable: true,
                    chkboxType: { "Y": "ps", "N": "ps" }
                },
                callback: {
                    onCheck: zTreeOnCheck
                }
            };
            var zNodes =data;
            zTreeObj=$.fn.zTree.init($("#test1"), setting, zNodes);
        }
    })
}

function zTreeOnCheck(event, treeId, treeNode) {
    var nodes= zTreeObj.getCheckedNodes(true);
        var roleId=$("#roleId").val();
        var mids="mids=";
        for(var i=0;i<nodes.length;i++){
            if(i<nodes.length-1){
                mids=mids+nodes[i].id+"&mids=";
            }else{
                mids=mids+nodes[i].id;
            }
        }
    $.ajax({
            type:"post",
            url:ctx+"/role/addGrant",
            data:mids+"&roleId="+roleId,
            dataType:"json",
            success:function (data) {
                console.log(data);
            }
    })
}
角色已添加權限記錄回顯

這裏要實現已添加的角色記錄權限再次查看或受權時顯示原始權限的功能,ztree 複選框是否選擇屬性配置參考這裏jquery

資源查詢後端方法實現
  • ModuleService.java
public List<TreeDto> queryAllModules02(Integer roleId) {
    List<TreeDto> treeDtos=moduleMapper.queryAllModules();
    // 根據角色id 查詢角色擁有的菜單id  List<Integer>
    List<Integer> roleHasMids=permissionMapper.queryRoleHasAllModuleIdsByRoleId(roleId);
    if(null !=roleHasMids && roleHasMids.size()>0){
        treeDtos.forEach(treeDto -> {
            if(roleHasMids.contains(treeDto.getId())){
                //  說明當前角色 分配了該菜單
                treeDto.setChecked(true);
            }
        });
    }
    return  treeDtos;
}
  • 角色擁有權限 sql 查詢
<select id="queryRoleHasAllModuleIdsByRoleId" parameterType="int" resultType="java.lang.Integer">
    select module_id from t_permission where role_id=#{roleId}
</select>
  • ModuleController.java
@RequestMapping("queryAllModules")
@ResponseBody
public List<TreeDto> queryAllModules(Integer roleId){
    return moduleService.queryAllModules02(roleId);
}
權限回顯前端 js

這裏修改 grant.js 查詢資源時傳入當前選擇角色 idajax

function loadModuleInfo() {
    $.ajax({
        type:"post",
        url:ctx+"/module/queryAllModules",
        data:{
            roleId:$("#roleId").val()
        },
        dataType:"json",
        success:function (data) {
            // zTree 的參數配置,深刻使用請參考 API 文檔(setting 配置詳解)
            var setting = {
                data: {
                    simpleData: {
                        enable: true
                    }
                },
                view:{
                    showLine: false
                    // showIcon: false
                },
                check: {
                    enable: true,
                    chkboxType: { "Y": "ps", "N": "ps" }
                },
                callback: {
                    onCheck: zTreeOnCheck
                }
            };
            var zNodes =data;
            zTreeObj=$.fn.zTree.init($("#test1"), setting, zNodes);
        }
    })
}

角色認證

當完成角色權限添加功能後,下一步就是對角色操做的資源進行認證操做,這裏對於認證包含兩塊:sql

  1. 菜單級別顯示控制
  2. 後端方法訪問控制

菜單級別訪問控制實現

系統根據登陸用戶扮演的不一樣角色來對登陸用戶操做的菜單進行動態控制顯示操做,這裏顯示的控制使用 freemarker 指令+內建函數實現,指令與內建函數操做參考這裏

登陸用戶角色擁有權限查詢實現
  • IndexController.java
/**
 * 後端管理主頁面
 * @return
 */
@RequestMapping("main")
public String main(HttpServletRequest request){
    Integer userId = LoginUserUtil.releaseUserIdFromCookie(request);
    request.setAttribute("user",userService.selectByPrimaryKey(userId));
    List<String> permissions=permissionService.queryUserHasRolesHasPermissions(userId);
    request.getSession().setAttribute("permissions",permissions);
    return "main";
}
  • PermissionService.java
@Service
public class PermissionService extends BaseService<Permission,Integer> {

    @Autowired
    private PermissionMapper permissionMapper;

    public List<String> queryUserHasRolesHasPermissions(Integer userId) {
        return permissionMapper.queryUserHasRolesHasPermissions(userId);
    }
}
  • PermissionMapper.java & PermissionMapper.xml
public interface PermissionMapper extends BaseMapper<Permission,Integer> {
    List<String>  queryUserHasRolesHasPermissions(Integer userId);
}
<select id="queryUserHasRolesHasPermissions" parameterType="int" resultType="java.lang.String">
    select distinct p.acl_value
    from t_user_role ur left join  t_permission p on ur.role_id = p.role_id
    where ur.user_id=#{userId}
</select>
系統主頁面菜單顯示指令控制

這裏僅顯示部分菜單控制。

<#if permissions?seq_contains("60")>
    <li class="layui-nav-item">
        <a href="javascript:;" class="layui-menu-tips"><i class="fa fa-gears"></i><span class="layui-left-nav"> 系統設置</span> <span class="layui-nav-more"></span></a>
        <dl class="layui-nav-child">
            <#if permissions?seq_contains("6010")>
                <dd>
                    <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-11" data-tab="user/index" target="_self"><i class="fa fa-user"></i><span class="layui-left-nav"> 用戶管理</span></a>
                </dd>
                </#if>
            <#if permissions?seq_contains("6020")>
                <dd class="">
                    <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-12" data-tab="role/index" target="_self"><i class="fa fa-tachometer"></i><span class="layui-left-nav"> 角色管理</span></a>
                </dd>
                </#if>
            <#if permissions?seq_contains("6030")>
                <dd class="">
                    <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-13" data-tab="module/index" target="_self"><i class="fa fa-tachometer"></i><span class="layui-left-nav"> 菜單管理</span></a>
                </dd>
            </#if>
        </dl>
    </li>
</#if>

後端方法級別訪問控制

實現了菜單級別顯示控制,但最終客戶端有可能會經過瀏覽器來輸入資源地址從而越過 ui 界面來訪問後端資源,因此接下來加入控制方法級別資源的訪問控制操做,這裏使用 aop+自定義註解實現

自定義註解@RequirePermission
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
    String code() default "";
}
方法級別使用註解
@RequestMapping("list")
@ResponseBody
@RequirePermission(code = "101001")
public Map<String,Object> querySaleChancesByParams(Integer flag,HttpServletRequest request,SaleChanceQuery saleChanceQuery){
    if(null !=flag &&flag==1){
        // 查詢分配給當前登陸用戶 營銷記錄
        saleChanceQuery.setAggsinMan(LoginUserUtil.releaseUserIdFromCookie(request));
    }
    return  saleChanceService.queryByParamsForTable(saleChanceQuery);
}
定義aop切面類攔截指定註解標註的方法
@Component
@Aspect
public class PermissionProxy {

    @Autowired
    private HttpSession session;

    @Around(value = "@annotation(com.xxx.sys.annotaions.RequirePermission)")
    public  Object around(ProceedingJoinPoint pjp) throws Throwable {
        List<String> permissions = (List<String>) session.getAttribute("permissions");
        if(null == permissions || permissions.size()==0){
            throw  new NoPermissionException();
        }
        Object result =null;
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        RequirePermission requirePermission = methodSignature.getMethod().getDeclaredAnnotation(RequirePermission.class);
        if(!(permissions.contains(requirePermission.code()))){
            throw  new NoPermissionException();
        }
        result= pjp.proceed();
        return result;
    }
}

擴展~自定義註解實例

從 JDK5 開始,Java 增長對元數據的支持,也就是註解,註解與註釋是有必定區別的,能夠把註解理解爲代碼裏的特殊標記,這些標記能夠在編譯,類加載,運行時被讀取,並執行相應的處理。經過註解開發人員能夠在不改變原有代碼和邏輯的狀況下在源代碼中嵌入補充信息。下面咱們來看看如何自定義註解。

建立自定義註解類

package com.lebyte.annotations;

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

/*    @Target,@Retention,@Inherited,@Documented
 *     這四個是對註解進行註解的元註解,負責自定義的註解的屬性
 */
@Target({ElementType.TYPE,ElementType.METHOD})    //表示註解的做用對象,ElementType.TYPE表示類,ElementType.METHOD表示方法
@Retention(RetentionPolicy.RUNTIME)        //表示註解的保留機制,RetentionPolicy.RUNTIME表示運行時註解
@Inherited            //表示該註解可繼承
@Documented            //表示該註解可生成文檔
public @interface Design {
    String author();        //註解成員,若是註解只有一個成員,則成員名必須爲value(),成員類型只能爲原始類型
    int data() default 0;    //註解成員,默認值爲0
}

使用註解

package com.lebyte;

import com.lebyte.annotations.Design;

@Design(author="lebyte",data=100)    //使用自定義註解,有默認值的成員能夠不用賦值,其他成員都要賦值
public class Person {
    @Design(author="lebyte",data=90)
    public void live(){

    }
}

解析註解

package com.lebyte;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import com.lebyte.annotations.Design;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {

        Class c=Class.forName("com.lebyte.Person");        //使用類加載器加載類

        //一、找到類上的註解
        if(c.isAnnotationPresent(Design.class)){    //判斷類是否被指定註解註解
            Design d=(Design) c.getAnnotation(Design.class);    //拿到類上的指定註解實例
            System.out.println(d.data());
        }

        //二、找到方法上的註解
        Method[] ms=c.getMethods();
        for(Method m:ms){
            if(m.isAnnotationPresent(Design.class)){    //判斷方法是否被指定註解註解
                Design d=m.getAnnotation(Design.class);        //拿到類上的指定註解實例
                System.out.println(d.data());
            }
        }

        //三、另一種方法
        for(Method m:ms){
            Annotation[] as=m.getAnnotations();        //拿到類上的註解集合
            for(Annotation a:as){
                if(a instanceof Design){        //判斷指定註解
                    Design d=(Design) a;
                    System.out.println(d.data());
                }
            }
        }
    }

}

Method m:ms){

if(m.isAnnotationPresent(Design.class)){    //判斷方法是否被指定註解註解
            Design d=m.getAnnotation(Design.class);        //拿到類上的指定註解實例
            System.out.println(d.data());
        }
    }

    //三、另一種方法
    for(Method m:ms){
        Annotation[] as=m.getAnnotations();        //拿到類上的註解集合
        for(Annotation a:as){
            if(a instanceof Design){        //判斷指定註解
                Design d=(Design) a;
                System.out.println(d.data());
            }
        }
    }
}

}

相關文章
相關標籤/搜索