知識分享:你知道權(quán)限管理的角色授權(quán)與認證嗎?

權(quán)限管理中确徙,角色授權(quán)與認證屬于權(quán)限模塊中的關(guān)鍵模塊醒串,角色授權(quán)即是將角色能夠操作的菜單資源分配給指定角色的行為,角色認證即是當用戶扮演指定角色登錄系統(tǒng)后系統(tǒng)對于用戶操作的資源進行權(quán)限校驗的操作鄙皇,意思這里說明白了芜赌,那么在代碼中應該具體怎么實現(xiàn)呢?

角色授權(quán)與認證的方式

前端頁面展示控制

后端權(quán)限訪問控制

案例實操

角色授權(quán)

樹形數(shù)據(jù)展示

完成角色記錄基本 crud 功能之后伴逸,接下來實現(xiàn)角色授權(quán)功能缠沈,這里實現(xiàn)角色授權(quán)首先完成待授權(quán)資源顯示功能。對于資源的顯示错蝴,這里使用開源的 tree 插件 ztree洲愤。

資源數(shù)據(jù)查詢后端實現(xiàn)

前端 ztree 顯示的資源數(shù)據(jù)格式參考這里

ModuleMapper.xml

<selectid="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

publicList<TreeDto>queryAllModules(){

returnmoduleMapper.queryAllModules();

}

ModuleController.java

@RequestMapping("queryAllModules")

@ResponseBody

publicList<TreeDto>queryAllModules(){

returnmoduleService.queryAllModules();

}

資源數(shù)據(jù) ztree 顯示

role.js 添加授權(quán)點擊事件

//頭工具欄事件

table.on('toolbar(roles)',function(obj){

varcheckStatus=table.checkStatus(obj.config.id);

switch(obj.event){

case"add":

openAddOrUpdateRoleDialog();

break;

case"grant":

openAddGrantDailog(checkStatus.data);

break;

?? };

});

?

functionopenAddGrantDailog(datas){

if(datas.length==0){

layer.msg("請選擇待授權(quán)角色記錄!", {icon:5});

return;

?? }

if(datas.length>1){

layer.msg("暫不支持批量角色授權(quán)!", {icon:5});

return;

?? }

varurl=ctx+"/role/toAddGrantPage?roleId="+datas[0].id;

vartitle="角色管理-角色授權(quán)";

layui.layer.open({

title:title,

type:2,

area:["600px","280px"],

maxmin:true,

content:url

?? });

}

RoleController.java 添加視圖轉(zhuǎn)發(fā)方法

@RequestMapping("toAddGrantPage")

publicStringtoAddGrantPage(IntegerroleId,Modelmodel){

model.addAttribute("roleId",roleId);

return"role/grant";

}

準備顯示資源數(shù)據(jù)模板

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

<html>

<head>

<linkrel="stylesheet"href="${ctx}/static/js/zTree_v3-3.5.32/css/zTreeStyle/zTreeStyle.css"type="text/css">

<scripttype="text/javascript"src="${ctx}/static/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>

<scripttype="text/javascript"src="${ctx}/static/js/zTree_v3-3.5.32/js/jquery.ztree.core.js"></script>

<scripttype="text/javascript"src="${ctx}/static/js/zTree_v3-3.5.32/js/jquery.ztree.excheck.js"></script>

</head>

<body>

<divid="test1"class="ztree"></div>

<inputid="roleId"value="${roleId!}"type="hidden">

<scripttype="text/javascript">

varctx="${ctx}";

</script>

<scripttype="text/javascript"src="${ctx}/static/js/role/grant.js"></script>

</body>

</html>

添加 grant.js

varzTreeObj;

$(function() {

loadModuleInfo();

});

functionloadModuleInfo() {

$.ajax({

type:"post",

url:ctx+"/module/queryAllModules"

dataType:"json",

success:function(data) {

// zTree 的參數(shù)配置顷锰,深入使用請參考 API 文檔(setting 配置詳解)

varsetting={

data: {

simpleData: {

enable:true

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? },

view:{

showLine:false

// showIcon: false

? ? ? ? ? ? ?? },

check: {

enable:true,

chkboxType: {"Y":"ps","N":"ps"}

? ? ? ? ? ? ?? }

? ? ? ? ?? };

varzNodes=data;

zTreeObj=$.fn.zTree.init($("#test1"),setting,zNodes);

? ? ?? }

?? })

}

角色授權(quán)

權(quán)限記錄添加后端實現(xiàn)

RoleService.java

publicvoidaddGrant(Integer[]mids,IntegerroleId) {

/**

* 核心表-t_permission? t_role(校驗角色存在)

* ? 如果角色存在原始權(quán)限? 刪除角色原始權(quán)限

* ? ? 然后添加角色新的權(quán)限 批量添加權(quán)限記錄到t_permission

*/

Roletemp=selectByPrimaryKey(roleId);

AssertUtil.isTrue(null==roleId||null==temp,"待授權(quán)的角色不存在!");

intcount=permissionMapper.countPermissionByRoleId(roleId);

if(count>0){

AssertUtil.isTrue(permissionMapper.deletePermissionsByRoleId(roleId)<count,"權(quán)限分配失敗!");

? ? ?? }

if(null!=mids&&mids.length>0){

List<Permission>permissions=newArrayList<Permission>();

for(Integermid:mids) {

Permissionpermission=newPermission();

permission.setCreateDate(newDate());

permission.setUpdateDate(newDate());

permission.setModuleId(mid);

permission.setRoleId(roleId);

permission.setAclValue(moduleMapper.selectByPrimaryKey(mid).getOptValue());

permissions.add(permission);

? ? ? ? ?? }

permissionMapper.insertBatch(permissions);

? ? ?? }

}

RoleController.java

@RequestMapping("addGrant")

@ResponseBody

publicResultInfoaddGrant(Integer[]mids,IntegerroleId){

roleService.addGrant(mids,roleId);

returnsuccess("權(quán)限添加成功");

}

權(quán)限記錄添加前端核心 js

修改 grant.js 文件 添加 ztree 復選框點擊回調(diào) onCheck 事件柬赐。

varzTreeObj;

$(function() {

loadModuleInfo();

});

functionloadModuleInfo() {

$.ajax({

type:"post",

url:ctx+"/module/queryAllModules",

dataType:"json",

success:function(data) {

// zTree 的參數(shù)配置,深入使用請參考 API 文檔(setting 配置詳解)

varsetting={

data: {

simpleData: {

enable:true

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? },

view:{

showLine:false

// showIcon: false

? ? ? ? ? ? ?? },

check: {

enable:true,

chkboxType: {"Y":"ps","N":"ps"}

? ? ? ? ? ? ?? },

callback: {

onCheck:zTreeOnCheck

? ? ? ? ? ? ?? }

? ? ? ? ?? };

varzNodes=data;

zTreeObj=$.fn.zTree.init($("#test1"),setting,zNodes);

? ? ?? }

?? })

}

?

functionzTreeOnCheck(event,treeId,treeNode) {

varnodes=zTreeObj.getCheckedNodes(true);

varroleId=$("#roleId").val();

varmids="mids=";

for(vari=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);

? ? ? ? ?? }

?? })

}

角色已添加權(quán)限記錄回顯

這里要實現(xiàn)已添加的角色記錄權(quán)限再次查看或授權(quán)時顯示原始權(quán)限的功能官紫,ztree 復選框是否選擇屬性配置參考這里肛宋。

資源查詢后端方法實現(xiàn)

ModuleService.java

publicList<TreeDto>queryAllModules02(IntegerroleId) {

List<TreeDto>treeDtos=moduleMapper.queryAllModules();

// 根據(jù)角色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);

? ? ? ? ?? }

? ? ?? });

?? }

returntreeDtos;

}

角色擁有權(quán)限 sql 查詢

<selectid="queryRoleHasAllModuleIdsByRoleId"parameterType="int"resultType="java.lang.Integer">

?? select module_id from t_permission where role_id=#{roleId}

</select>

ModuleController.java

@RequestMapping("queryAllModules")

@ResponseBody

publicList<TreeDto>queryAllModules(IntegerroleId){

returnmoduleService.queryAllModules02(roleId);

}

權(quán)限回顯前端 js

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

functionloadModuleInfo() {

$.ajax({

type:"post",

url:ctx+"/module/queryAllModules",

data:{

roleId:$("#roleId").val()

? ? ?? },

dataType:"json",

success:function(data) {

// zTree 的參數(shù)配置州藕,深入使用請參考 API 文檔(setting 配置詳解)

varsetting={

data: {

simpleData: {

enable:true

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? },

view:{

showLine:false

// showIcon: false

? ? ? ? ? ? ?? },

check: {

enable:true,

chkboxType: {"Y":"ps","N":"ps"}

? ? ? ? ? ? ?? },

callback: {

onCheck:zTreeOnCheck

? ? ? ? ? ? ?? }

? ? ? ? ?? };

varzNodes=data;

zTreeObj=$.fn.zTree.init($("#test1"),setting,zNodes);

? ? ?? }

?? })

}

角色認證

當完成角色權(quán)限添加功能后,下一步就是對角色操作的資源進行認證操作酝陈,這里對于認證包含兩塊:

菜單級別顯示控制

后端方法訪問控制

菜單級別訪問控制實現(xiàn)

系統(tǒng)根據(jù)登錄用戶扮演的不同角色來對登錄用戶操作的菜單進行動態(tài)控制顯示操作床玻,這里顯示的控制使用 freemarker 指令+內(nèi)建函數(shù)實現(xiàn),指令與內(nèi)建函數(shù)操作參考這里后添。

登錄用戶角色擁有權(quán)限查詢實現(xiàn)

IndexController.java

/**

* 后端管理主頁面

* @return

*/

@RequestMapping("main")

publicStringmain(HttpServletRequestrequest){

IntegeruserId=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

publicclassPermissionServiceextendsBaseService<Permission,Integer>{

?

@Autowired

privatePermissionMapperpermissionMapper;

?

publicList<String>queryUserHasRolesHasPermissions(IntegeruserId) {

returnpermissionMapper.queryUserHasRolesHasPermissions(userId);

?? }

}

PermissionMapper.java & PermissionMapper.xml

publicinterfacePermissionMapperextendsBaseMapper<Permission,Integer>{

List<String>queryUserHasRolesHasPermissions(IntegeruserId);

}

<selectid="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>

系統(tǒng)主頁面菜單顯示指令控制

這里僅顯示部分菜單控制笨枯。

<#ifpermissions?seq_contains("60")>

<liclass="layui-nav-item">

<ahref="javascript:;"class="layui-menu-tips"><iclass="fa fa-gears"></i><spanclass="layui-left-nav">系統(tǒng)設(shè)置</span><spanclass="layui-nav-more"></span></a>

<dlclass="layui-nav-child">

<#ifpermissions?seq_contains("6010")>

<dd>

<ahref="javascript:;"class="layui-menu-tips"data-type="tabAdd"data-tab-mpi="m-p-i-11"data-tab="user/index"target="_self"><iclass="fa fa-user"></i><spanclass="layui-left-nav">用戶管理</span></a>

</dd>

</#if>

<#ifpermissions?seq_contains("6020")>

<ddclass="">

<ahref="javascript:;"class="layui-menu-tips"data-type="tabAdd"data-tab-mpi="m-p-i-12"data-tab="role/index"target="_self"><iclass="fa fa-tachometer"></i><spanclass="layui-left-nav">角色管理</span></a>

</dd>

</#if>

<#ifpermissions?seq_contains("6030")>

<ddclass="">

<ahref="javascript:;"class="layui-menu-tips"data-type="tabAdd"data-tab-mpi="m-p-i-13"data-tab="module/index"target="_self"><iclass="fa fa-tachometer"></i><spanclass="layui-left-nav">菜單管理</span></a>

</dd>

</#if>

</dl>

</li>

</#if>

后端方法級別訪問控制

實現(xiàn)了菜單級別顯示控制,但最終客戶端有可能會通過瀏覽器來輸入資源地址從而越過 ui 界面來訪問后端資源遇西,所以接下來加入控制方法級別資源的訪問控制操作馅精,這里使用 aop+自定義注解實現(xiàn)

自定義注解@RequirePermission

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public@interfaceRequirePermission{

Stringcode()default"";

}

方法級別使用注解

@RequestMapping("list")

@ResponseBody

@RequirePermission(code="101001")

publicMap<String,Object>querySaleChancesByParams(Integerflag,HttpServletRequestrequest,SaleChanceQuerysaleChanceQuery){

if(null!=flag&&flag==1){

// 查詢分配給當前登錄用戶 營銷記錄

saleChanceQuery.setAggsinMan(LoginUserUtil.releaseUserIdFromCookie(request));

?? }

returnsaleChanceService.queryByParamsForTable(saleChanceQuery);

}

定義aop切面類攔截指定注解標注的方法

@Component

@Aspect

publicclassPermissionProxy{

?

@Autowired

privateHttpSessionsession;

?

@Around(value="@annotation(com.xxx.sys.annotaions.RequirePermission)")

publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{

List<String>permissions=(List<String>)session.getAttribute("permissions");

if(null==permissions||permissions.size()==0){

thrownewNoPermissionException();

? ? ?? }

Objectresult=null;

MethodSignaturemethodSignature=(MethodSignature)pjp.getSignature();

RequirePermissionrequirePermission=methodSignature.getMethod().getDeclaredAnnotation(RequirePermission.class);

if(!(permissions.contains(requirePermission.code()))){

thrownewNoPermissionException();

? ? ?? }

result=pjp.proceed();

returnresult;

?? }

}

擴展~自定義注解實例

從 JDK5 開始,Java 增加對元數(shù)據(jù)的支持粱檀,也就是注解洲敢,注解與注釋是有一定區(qū)別的,可以把注解理解為代碼里的特殊標記茄蚯,這些標記可以在編譯压彭,類加載,運行時被讀取渗常,并執(zhí)行相應的處理壮不。通過注解開發(fā)人員可以在不改變原有代碼和邏輯的情況下在源代碼中嵌入補充信息。下面我們來看看如何自定義注解皱碘。

創(chuàng)建自定義注解類

packagecom.lebyte.annotations;

?

importjava.lang.annotation.Documented;

importjava.lang.annotation.ElementType;

importjava.lang.annotation.Inherited;

importjava.lang.annotation.Retention;

importjava.lang.annotation.RetentionPolicy;

importjava.lang.annotation.Target;

?

/* ?? @Target,@Retention,@Inherited,@Documented

* ? ? 這四個是對注解進行注解的元注解询一,負責自定義的注解的屬性

*/

@Target({ElementType.TYPE,ElementType.METHOD})//表示注解的作用對象,ElementType.TYPE表示類癌椿,ElementType.METHOD表示方法

@Retention(RetentionPolicy.RUNTIME)//表示注解的保留機制健蕊,RetentionPolicy.RUNTIME表示運行時注解

@Inherited//表示該注解可繼承

@Documented//表示該注解可生成文檔

public@interfaceDesign{

Stringauthor();//注解成員,如果注解只有一個成員踢俄,則成員名必須為value()缩功,成員類型只能為原始類型

intdata()default0;//注解成員,默認值為0

}

使用注解

packagecom.lebyte;

?

importcom.lebyte.annotations.Design;

?

@Design(author="lebyte",data=100)//使用自定義注解都办,有默認值的成員可以不用賦值嫡锌,其余成員都要賦值

publicclassPerson{

@Design(author="lebyte",data=90)

publicvoidlive(){

?

?? }

}

解析注解

packagecom.lebyte;

?

importjava.lang.annotation.Annotation;

importjava.lang.reflect.Method;

?

importcom.lebyte.annotations.Design;

?

publicclassMain{

?

publicstaticvoidmain(String[]args)throwsClassNotFoundException{

?

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

?

//1、找到類上的注解

if(c.isAnnotationPresent(Design.class)){//判斷類是否被指定注解注解

Designd=(Design)c.getAnnotation(Design.class);//拿到類上的指定注解實例

System.out.println(d.data());

? ? ?? }

?

//2脆丁、找到方法上的注解

Method[]ms=c.getMethods();

for(Methodm:ms){

if(m.isAnnotationPresent(Design.class)){//判斷方法是否被指定注解注解

Designd=m.getAnnotation(Design.class);//拿到類上的指定注解實例

System.out.println(d.data());

? ? ? ? ?? }

? ? ?? }

?

//3世舰、另外一種方法

for(Methodm:ms){

Annotation[]as=m.getAnnotations();//拿到類上的注解集合

for(Annotationa:as){

if(ainstanceofDesign){//判斷指定注解

Designd=(Design)a;

System.out.println(d.data());

? ? ? ? ? ? ?? }

? ? ? ? ?? }

? ? ?? }

?? }

?

}

ooooo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市槽卫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胰蝠,老刑警劉巖歼培,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件震蒋,死亡現(xiàn)場離奇詭異,居然都是意外死亡躲庄,警方通過查閱死者的電腦和手機查剖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來噪窘,“玉大人笋庄,你說我怎么就攤上這事【蠹啵” “怎么了直砂?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長浩习。 經(jīng)常有香客問我静暂,道長,這世上最難降的妖魔是什么谱秽? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任洽蛀,我火速辦了婚禮,結(jié)果婚禮上疟赊,老公的妹妹穿的比我還像新娘郊供。我一直安慰自己,他們只是感情好近哟,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布驮审。 她就那樣靜靜地躺著,像睡著了一般椅挣。 火紅的嫁衣襯著肌膚如雪头岔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天鼠证,我揣著相機與錄音峡竣,去河邊找鬼。 笑死量九,一個胖子當著我的面吹牛适掰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荠列,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼类浪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肌似?” 一聲冷哼從身側(cè)響起费就,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎川队,沒想到半個月后力细,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睬澡,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年眠蚂,在試婚紗的時候發(fā)現(xiàn)自己被綠了煞聪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡逝慧,死狀恐怖昔脯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笛臣,我是刑警寧澤云稚,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站捐祠,受9級特大地震影響碱鳞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜踱蛀,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一窿给、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧率拒,春花似錦崩泡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勃痴,卻和暖如春谒所,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沛申。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工劣领, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铁材。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓尖淘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親著觉。 傳聞我的和親對象是個殘疾皇子村生,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容