if(i
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 复选框是否选择属性配置参考这里。
资源查询后端方法实现
ModuleService.java
publicListqueryAllModules02(IntegerroleId) {
ListtreeDtos=moduleMapper.queryAllModules();
// 根据角色id 查询角色拥有的菜单id List
ListroleHasMids=permissionMapper.queryRoleHasAllModuleIdsByRoleId(roleId);
if(null!=roleHasMids&&roleHasMids.size()>0){
treeDtos.forEach(treeDto->{
if(roleHasMids.contains(treeDto.getId())){
// 说明当前角色 分配了该菜单
treeDto.setChecked(true);
}
});
}
returntreeDtos;
}
角色拥有权限 sql 查询
select module_id from t_permission where role_id=#{roleId}
ModuleController.java
@RequestMapping("queryAllModules")
@ResponseBody
publicListqueryAllModules(IntegerroleId){
returnmoduleService.queryAllModules02(roleId);
}
权限回显前端 js
这里修改 grant.js 查询资源时传入当前选择角色 id
functionloadModuleInfo() {
$.ajax({
type:"post",
url:ctx+"/module/queryAllModules",
data:{
roleId:$("#roleId").val()
},
dataType:"json",
success:function(data) {
// zTree 的参数配置,深入使用请参考 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);
}
})
}
角色认证
当完成角色权限添加功能后,下一步就是对角色操作的资源进行认证操作,这里对于认证包含两块:
菜单级别显示控制
后端方法访问控制
菜单级别访问控制实现
系统根据登录用户扮演的不同角色来对登录用户操作的菜单进行动态控制显示操作,这里显示的控制使用 freemarker 指令+内建函数实现,指令与内建函数操作参考这里。
登录用户角色拥有权限查询实现
IndexController.java
/**
* 后端管理主页面
* @return
*/
@RequestMapping("main")
publicStringmain(HttpServletRequestrequest){
IntegeruserId=LoginUserUtil.releaseUserIdFromCookie(request);
request.setAttribute("user",userService.selectByPrimaryKey(userId));
Listpermissions=permissionService.queryUserHasRolesHasPermissions(userId);
request.getSession().setAttribute("permissions",permissions);
return"main";
}
PermissionService.java
@Service
publicclassPermissionServiceextendsBaseService{
@Autowired
privatePermissionMapperpermissionMapper;
publicListqueryUserHasRolesHasPermissions(IntegeruserId) {
returnpermissionMapper.queryUserHasRolesHasPermissions(userId);
}
}
PermissionMapper.java & PermissionMapper.xml
publicinterfacePermissionMapperextendsBaseMapper{
ListqueryUserHasRolesHasPermissions(IntegeruserId);
}
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}
系统主页面菜单显示指令控制
这里仅显示部分菜单控制。
<#ifpermissions?seq_contains("60")>
系统设置
<#ifpermissions?seq_contains("6010")>
用户管理
#if>
<#ifpermissions?seq_contains("6020")>
角色管理
#if>
<#ifpermissions?seq_contains("6030")>
菜单管理
#if>
#if>
后端方法级别访问控制
实现了菜单级别显示控制,但最终客户端有可能会通过浏览器来输入资源地址从而越过 ui 界面来访问后端资源,所以接下来加入控制方法级别资源的访问控制操作,这里使用 aop+自定义注解实现
自定义注解@RequirePermission
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceRequirePermission{
Stringcode()default"";
}
方法级别使用注解
@RequestMapping("list")
@ResponseBody
@RequirePermission(code="101001")
publicMapquerySaleChancesByParams(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{
Listpermissions=(List)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 增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。下面我们来看看如何自定义注解。
创建自定义注解类
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(){
}
}
解析注解
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"); //使用类加载器加载类
//1、找到类上的注解
if(c.isAnnotationPresent(Design.class)){ //判断类是否被指定注解注解
Design d=(Design) c.getAnnotation(Design.class); //拿到类上的指定注解实例
System.out.println(d.data());
}
//2、找到方法上的注解
Method[] ms=c.getMethods();
for(Method m:ms){
if(m.isAnnotationPresent(Design.class)){ //判断方法是否被指定注解注解
Design d=m.getAnnotation(Design.class); //拿到类上的指定注解实例
System.out.println(d.data());
}
}
//3、另外一种方法
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());
}
}
}
}
}