栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【爆肝推荐】手摸手带你做后台管理项目(第四章)整合redis添加shiro权限

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【爆肝推荐】手摸手带你做后台管理项目(第四章)整合redis添加shiro权限

前情提要

这一篇是关于动态权限和动态目录的,
shiro的授权器在碰到权限的校验时候才会去触发,这个时候就可以从数据库中获取到用户关联的角色,
角色绑定的权限,大概就如下图了

有兴趣可以了解一下RBAC,大概就是如下的一个关系

动态目录就更简单了,用户关联的角色,角色所拥有的目录,这个就是展示的目录了,修改数据库数据就可达到动态的目的。

正文开始

设计五个表,管理员表(也就是用户表,已存在) 、角色表、目录表、用户角色表、角色目录表,如果没懂这些关联,可以看一下图片,图片最下方标记了关系,希望能看懂

创建数据库sys_menu

创建sys_role

创建sys_role_menu

创建sys_user_role

记得创建对应的controller和service和dao以及xml、entity

太多了,就不一一创建展示了,记得service和dao集成mybatis plus提供的类

修改上篇没动的授权器UserRealm
  @Autowired
    private SysMenuService menuService;


  
    @Override
   	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("这里是授权");
        UserEntity userEntity = (UserEntity) principalCollection.getPrimaryPrincipal();
        Integer id = userEntity.getId();
        //设置id为空则拥有所有权限,sql中设置了id为空则查询所有权限
        List menuList = menuService.findByUserId(id);
        //转存set是为了去重,保证权限唯一
        Set collect = menuList.stream().map(SysMenuEntity::getPerms).collect(Collectors.toSet());
        //所有权限
        Set perms = new HashSet<>();
        collect.stream().forEach(y -> {
            //防止空的造成异常
            if(!StringUtils.isEmpty(y)){
                //存放无论是否有多个或者单个,直接变成数组,更加清晰
                
                perms.addAll(Arrays.asList(y.split(",")));
            }

        });
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //此处是放入权限中,不是role角色中
        simpleAuthorizationInfo.setStringPermissions(perms);
        return simpleAuthorizationInfo;
    }
SysMenuService
	//用户查询关联的目录
 List findByUserId(Integer userId);
SysMenuServiceImpl
 @Autowired
    private SysMenuDao menuDao;

    @Override
    public List findByUserId(Integer userId) {
        return menuDao.selectByUserId(userId);
    }
SysMenuDao
 List selectByUserId(@Param("userId") Integer userId);
SysMenuDao.xml
    
                        
图标地址 点击前往
sysMenu.js

我将加载树表格的请求封装起来了,保存或者修改或者删除后调用init(),让树重新加载即可

var vm = new Vue({
    el:"#app",
    mounted(){
        this.init();
    },
    data:{
        menu:{
            name:null,
            parentId:null,
            path:null,
            type:2,
            perms:null,
            icon:null,
            sort:null
        },
        show:true,
        title:"新增角色",
        treeData:[],
        defaultProps: {
            children: 'childList',
            label: 'name'
        },
        types:[],
        options:[]
    },

    methods:{

        init(){
            layui.config({
                //   base: '/js/'存放treeTable.js的文件夹
                base: '/treetable/'
            }).use([ 'treetable','table'], function () {
                let treeTable = layui.treetable;
                var table = layui.table;
                treeTable.render({
                    elem: '#table'
                    , cellMinWidth: 80
                    ,treeSpid: '0'
                    ,icon_key:'id',
                    icon: {
                        open: 'layui-icon layui-icon-triangle-d',
                        close: 'layui-icon layui-icon-triangle-r',
                        left: 16,
                    }
                    ,hide_class: 'layui-hide'
                    ,primary_key:"id"
                    ,parent_key:'parentId'
                    ,treeColIndex: 0
                    , url: 'menu/list'
                    ,isPidData: true
                    ,treePidName:'parentId'
                    , page: false
                    ,treeDefaultClose: true	//是否默认折叠
                    ,treelinkage: false		//父级展开时是否自动展开所有子级
                    ,is_click_icon: false,
                    is_checkbox: false
                    , cols: [[
                        {field: 'name', title: '名称'}
                        , {field: 'path', title: '路径'}
                        , {field: 'perms', title: '权限'}
                        , {
                            field: 'type', title: '名称',
                            templet: ' {{d.type == 0?"按钮":d.sex==1?"目录":"菜单"}} '
                        }
                        , {field: 'icon', title: '图标',
                            templet: ''

                        }
                        ,{fixed: 'right', align: 'center',title:'操作', toolbar: '#barDemo', width:150}
                    ]]
                    , page: true
                });
                //监听行工具事件
                //tool(table):table是 id值,elem的值,还得加一个 lay-filter="table"才会生效
                table.on('tool(table)', function(obj){
                    console.log(obj)
                    if(obj.event === 'del'){
                        console.log("id",obj.data.id);
                        vm.del(obj.data.id);
                    } else if(obj.event === 'edit'){
                        vm.update(obj.data.id);
                    }
                });
            })
        },
        redioState(){
            vm.menu.parentId = null;
            vm.menu.name = null;
            vm.menu.path = null;
            vm.menu.perms = null;
            vm.menu.icon = null;
            vm.menu.sort = null;
            if(vm.menu.type != 2){
                vm.treeList(vm.menu.type+1);
            }
        },
         //加载上级菜单
        treeList(type){
            axios({
                url:"menu/type/"+type,
                method: "get"
            }).then(res =>{
                if(res.data.code == 200){
                    console.log("####",res.data.data);

                    vm.options = res.data.data;
                }
            });
        },
        //填layui和vue的坑
        reloadTypeList(){
            vm.types=[];
            vm.types.push({type:2,  name:'目录'});
            vm.types.push({type:1,  name:'菜单'});
            vm.types.push({type:0,  name:'按钮'});

        },
        //查询+重新加载数据
        reload(){
            vm.init();
            vm.show = true;
        },
        getType(type){
            console.log("type",type)
            vm.menu.type = type;
        },
        add(){
            vm.show = false;
            //初始化
            vm.menu = {
                name:null,
                parentId:null,
                path:null,
                type:2,
                perms:null,
                icon:null,
                sort:null
            };
            vm.reloadTypeList();
            vm.title= "新增角色";
        },
        update(id){
            vm.show = false;
            vm.menu = {};
            vm.reloadTypeList();
            vm.info(id);
            vm.title= "修改角色";
        },
        del(id){
            let that = this;
            layer.open({
                title: '删除'
                ,content: '是否删除数据',
                btn:['确定','取消'],
                yes: function(index, layero){
                    axios({
                        url:"menu/del/"+id,
                        method: "post",
                        headers:{
                            "Content-Type": "application/json"
                        }
                    }).then(res =>{
                        if(res.data.code == 200){
                            that.$message({message:"删除成功", type: 'success'});
                            vm.reload();
                        }else {
                            that.$message.error("删除失败");
                        }
                    });
                    layer.close(index)
                }
            });
        },
        //保存或者更新
        saveOrUpdate(){
            let state = vm.menu.id == null|| vm.menu.id == "";
            let url = state ?"menu/save":"menu/update";
            axios({
                url:url,
                method: "post",
                headers:{
                    "Content-Type": "application/json"
                },
                data:JSON.stringify(vm.menu)
            }).then(res =>{
                if(res.data.code == 200){
                    this.$message({message: state?"添加成功":"修改成功", type: 'success'});
                    vm.reload();
                }else{
                    this.$message.error(state?'新增失败':"修改失败");
                }
            });

        },
        cannel(){
            vm.show = true;
        },
        //查询单条
        info(id){
            axios({
                method:"get",
                url: "menu/info/" + id
            }).then(res =>{
                if(res.data.code == 200){
                    vm.menu = res.data.data;
                    if(res.data.data.type != 2){
                        vm.treeList(res.data.data.type+1);
                    }
                }

            })
        },



    }

})
index.html

左侧目录栏添加一个菜单管理

菜单管理

重启看一下效果,没毛病

目前上级没接口,不过js已经写了

SysMenuController 新增typeList接口
   @GetMapping("type/{type}")
    public Result typeList(@PathVariable Integer type){
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.eq("type",type);
        wrapper.eq("is_del","0");
        List list = menuService.list(wrapper);
        return Result.success(list);
    }

没什么问题

角色管理sysRole.html

这个模块东西很多




    
    
    
    
    
    
    
    
    
    


{{title}}
sysRole.js
var vm = new Vue({
    el:"#app",
    mounted(){
        layui.use('table', function(){
            var table = layui.table;
            table.render({
                elem: '#table'
                ,cellMinWidth: 80
                ,url:'role/list'
                ,cols: [[
                    {type:'checkbox'}
                    ,{field:'id',  title: 'id' }
                    ,{field:'roleName',  title: '角色名称' }
                ]]
                ,page: true
            });
        });
        this.menuList();
    },
    data:{
        role:{
            roleName:null,
            menuList:[]
        },
        show:true,
        title:"新增角色",
        treeData:[],
        treeList:[],
        //子集节点名称
        defaultProps: {
            children: 'childList',
            label: 'name'
        }
    },

    methods:{
        menuList(){
            axios({
                url: "menu/menuList",
                methods: "get"
            }).then(res => {
                //新增或者修改时被格式化的树节点
                vm.treeData = res.data.data.menuList;
                //未被格式化的数据,用于比较和获取父节点
                vm.treeList = res.data.data.list;
            });
        },

        //查询+重新加载数据
        reload(){
            layui.use('table', function () {
                var table = layui.table;
                table.reload('table', {
                    url: 'role/list'
                });
            });
            vm.show = true;
        },
        add(){
            vm.show = false;
            //初始化
            vm.role = {
                roleName:null,
                menuList:[]
            };
            vm.title= "新增角色";
            //设置选中节点为空
            this.$refs.tree.setCheckedKeys([]);
        },
        update(){
            let data = ids();
            if(data == null || data.length == 0 || data.length  > 1){
                alert("请选择一条数据!");
                return;
            }
            //设置选中节点为空
            this.$refs.tree.setCheckedKeys([]);
            vm.show = false;
            vm.role = {};
            vm.info(data[0]);
            vm.title= "修改角色";
        },
        del(){
            let that = this;
            let data = ids();
            if(data == null || data.length == 0){
                alert("请选择!")
                return;
            }
            layer.open({
                title: '删除'
                ,content: '是否删除数据',
                btn:['确定','取消'],
                yes: function(index, layero){
                    axios({
                        url:"role/del",
                        method: "post",
                        headers:{
                            "Content-Type": "application/json"
                        },
                        data:JSON.stringify(data)
                    }).then(res =>{
                        if(res.data.code == 200){
                            that.$message({message:"删除成功", type: 'success'});
                            vm.reload();
                        }else {
                            that.$message.error("删除失败");
                        }
                    });
                    layer.close(index)
                }
            });



        },
        //保存或者更新
        saveOrUpdate(){
            //抽取选中节点数据,只有子节点
            let menuList = vm.getTreeData();
            vm.role.menuList=menuList;
            console.log("menuList",menuList);
            let state = vm.role.id == null|| vm.role.id == "";
            let url = state ?"role/save":"role/update";
            axios({
                url:url,
                method: "post",
                headers:{
                    "Content-Type": "application/json"
                },
                data:JSON.stringify(vm.role)
            }).then(res =>{
                if(res.data.code == 200){
                    this.$message({message: state?"添加成功":"修改成功", type: 'success'});
                    vm.reload();
                }else{
                    this.$message.error(state?'新增失败':"修改失败");
                }
            });

        },
        cannel(){
            vm.show = true;
        },
        //查询单条
        info(id){
            axios({
                method:"get",
                url: "role/info/" + id
            }).then(res =>{
                if(res.data.code == 200){
                    vm.role = res.data.data;
                    let list = res.data.data.menuList;
                    let arr =[];
                    for (let i = 0; i < list.length; i++) {
                        arr.push(list[i].menuId);
                    }
                    //从数据库中获取被选中的节点
                    this.$refs.tree.setCheckedKeys(arr);
                }

            })
        },
        //获取选中的增删改查标签
        getTreeData(){
            let arr =[];
            let nodes = this.$refs.tree.getCheckedNodes();
            if(nodes != null && nodes.length > 0){
                for (let i = 0; i < nodes.length; i++) {
                    let child = nodes[i].childList;
                    //为空的则是增删改查标签
                    if(child == null){
                        console.log("tree",nodes[i]);
                        for (let j = 0; j < vm.treeList.length; j++) {
                            //比较节点,并且获取父级节点
                            if(vm.treeList[j].id == nodes[i].id){
                                arr.push({"menuId":vm.treeList[j].id,"parentId":vm.treeList[j].parentId});
                            }
                        }
                    }
                }
            }
            return arr;
        }
    }
});
SysRoleEntity添加非表字段
//用于接收和反显关联的菜单数据
    @TableField(exist = false)
    private List menuList;
SysRoleMenuEntity添加非表字段
//用户接收,角色关联的菜单的父级
@TableField(exist = false)
private Integer parentId;
SysRoleController
import com.macro.Vo.PageEntity;
import com.macro.entity.SysRoleEntity;
import com.macro.service.SysRoleService;
import com.macro.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("role")
public class SysRoleController {

    @Autowired
    private SysRoleService roleService;


    @GetMapping("list")
    public Result list(PageEntity param){
        Result result = roleService.findList(param);
        return result;
    }
    @PostMapping("save")
    public Result save(@RequestBody SysRoleEntity role){
        int num = roleService.insert(role);
        return num > 0?Result.success():Result.error("添加失败");
    }

    @PostMapping("update")
    public Result update(@RequestBody SysRoleEntity role){
        int num = roleService.updateEntity(role);
        return num > 0?Result.success():Result.error("更新失败");
    }

    @GetMapping("info/{id}")
    public Result info(@PathVariable Integer id){
        SysRoleEntity role = roleService.findById(id);
        return Result.success(role);
    }

    @PostMapping("del")
    public Result update(@RequestBody String[] ids){
        roleService.delIds(ids);
        return Result.success();
    }

}
SysRoleService
import com.baomidou.mybatisplus.extension.service.IService;
import com.macro.Vo.PageEntity;
import com.macro.entity.SysRoleEntity;
import com.macro.utils.Result;

public interface SysRoleService  extends IService {


    int updateEntity(SysRoleEntity role);

    Result findList(PageEntity param);

    int insert(SysRoleEntity role);

    SysRoleEntity findById(Integer id);

    void delIds(String[] ids);

}


SysRoleServiceImpl
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.macro.Vo.PageEntity;
import com.macro.dao.SysRoleDao;
import com.macro.entity.SysMenuEntity;
import com.macro.entity.SysRoleEntity;
import com.macro.entity.SysRoleMenuEntity;
import com.macro.service.SysMenuService;
import com.macro.service.SysRoleMenuService;
import com.macro.service.SysRoleService;
import com.macro.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysRoleServiceImpl extends ServiceImpl implements SysRoleService {

    @Autowired
    private SysRoleDao roleDao;
    @Autowired
    private SysRoleMenuService roleMenuService;
    @Autowired
    private SysMenuService menuService;

    @Override
    public int updateEntity(SysRoleEntity role) {
        int update = roleDao.updateById(role);
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.eq("role_id",role.getId());
        //旧的所有关联菜单
        List list = roleMenuService.list(wrapper);
        //新的关联菜单
        List menuList = role.getMenuList();
        if(update > 0 && menuList != null && menuList.size() > 0){
            //抽取旧的目录id
            Set arr = list.stream().map(SysRoleMenuEntity::getMenuId).collect(Collectors.toSet());
            //新数据,包含父级
            menuList =getMenuList(menuList);
            //抽出所有的目录id,并且保证不重复
            Set menuIds = menuList.stream().map(SysRoleMenuEntity::getMenuId).collect(Collectors.toSet());
            //不存在则删除
            list.stream().forEach(y -> {
                if(!menuIds.contains(y.getMenuId())){
                    roleMenuService.removeById(y.getId());
                }
            });
            //不存在则新增
            menuList.stream().forEach(y -> {
                if(!arr.contains(y.getMenuId())){
                    y.setRoleId(role.getId());
                    roleMenuService.save(y);
                }
            });
        }
        return update;
    }

    @Override
    public Result findList(PageEntity param) {
        PageHelper.startPage(param.getPage(), param.getLimit());
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.eq("is_del","0");
        List list = roleDao.selectList(wrapper);
        PageInfo pageInfo = new PageInfo<>(list);
        return Result.success(0,pageInfo.getTotal(),list);
    }

    @Override
    public int insert(SysRoleEntity role) {
        int num = roleDao.insert(role);
        //获取父级
        if(num > 0){
            List list = role.getMenuList();
            if(list != null && list.size() > 0){
                list =getMenuList(list);
                list.stream().forEach(y -> {
                    y.setRoleId(role.getId());
                    roleMenuService.save(y);
                });
            }
        }
        return num;
    }

    @Override
    public SysRoleEntity findById(Integer id) {
        SysRoleEntity entity = roleDao.selectById(id);
        if(entity != null){
            List menuList = roleMenuService.findByRoleId(id);
            entity.setMenuList(menuList);
        }
        return entity;
    }

    @Override
    public void delIds(String[] ids) {
        //删除角色
        roleDao.deleteBatchIds(Arrays.asList(ids));
        //删除关联的信息
        roleMenuService.remove(new QueryWrapper().in("role_id",ids));
    }


    //新增和修改共用,获取目录和菜单id
    public List getMenuList(List list){
        Set parentIdList = list.stream().map(SysRoleMenuEntity::getParentId).collect(Collectors.toSet());
        if(parentIdList !=null && parentIdList.size() >0){
            QueryWrapper wrapper = new QueryWrapper();
            //排除为父级为0的数据
            wrapper.ne("parent_id","0");
            wrapper.in("id",parentIdList);

            //查出菜单和菜单,直接获取父节点的id
            List parentList = menuService.list(wrapper);
            Set collect = parentList.stream().map(SysMenuEntity::getParentId).collect(Collectors.toSet());
            parentIdList.addAll(collect);
            parentIdList.stream().forEach(y -> {
                SysRoleMenuEntity entity = new SysRoleMenuEntity();
                entity.setMenuId(y);
                list.add(entity);
            });

        }
        return list;
    }
}
SysRoleMenuService增加findByRoleId
 List findByRoleId(Integer roleId);
SysRoleMenuServiceImpl实现findByRoleId方法
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.macro.dao.SysRoleMenuDao;
import com.macro.entity.SysRoleMenuEntity;
import com.macro.service.SysRoleMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class SysRoleMenuServiceImpl extends ServiceImpl implements SysRoleMenuService {

    @Autowired
    private SysRoleMenuDao roleMenuDao;

    @Override
    public List findByRoleId(Integer roleId) {
        return roleMenuDao.findByRoleId(roleId);
    }
}

SysRoleMenuDao
import com.baomidou.mybatisplus.core.mapper.baseMapper;
import com.macro.entity.SysRoleMenuEntity;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface SysRoleMenuDao extends baseMapper {
    List findByRoleId(@Param("roleId") Integer roleId);
}

SysRoleMenuDao.xml




    
    

index.html增加角色管理(sysRole.html)

和上次增加菜单管理一样

角色管理

运行效果,此时还看不到菜单树

增加menu/menuList接口

SysMenuEntity增加子集
	//非表字段
    @TableField(exist = false)
    private List childList;
SysMenuController 新增 menuList
   
    @GetMapping("menuList")
    public Result menuList(){
        //用户展示树节点的
        Map> map = new HashMap<>();
        List menuList = menuService.menuList();

        //用于比较是否选中的
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.eq("is_del","0");
        List list =  menuService.list(wrapper);
        map.put("menuList",menuList);
        map.put("list",list);
        return Result.success(map);
    }
SysMenuService 新增 menuList
   List menuList();
SysMenuServiceImpl

        
    public List getHierarchyList(List menuList){
        //获取所有的目录
        //type = 2 为目录
        List pathList = menuList.stream().filter(y -> y.getType().equals(2)).collect(Collectors.toList());
        //获取所有菜单
        //type = 1 为菜单
        List childList = menuList.stream().filter(y -> y.getType().equals(1)).collect(Collectors.toList());

        childList.stream().forEach( y -> {
            //直接从sys_menu表查询权限
            List child =  menuDao.selectList(new QueryWrapper().eq("parent_id",y.getId()).eq("is_del","0"));
            y.setChildList(child);
        });
        pathList.stream().forEach( y-> {
            //目录id与 菜单的父级id相同 则菜单是目录的子级
            List child = childList.stream().filter(x -> x.getParentId().equals(y.getId())).collect(Collectors.toList());
            y.setChildList(child);
        });
        return pathList;
    }

运行效果如下:

总结

这篇太长了,想着一篇把动态目录也做了,算了,拆到下一篇,权限那块稍微麻烦一点,新权限可能移除了一些权限,也添加了一些权限,绿线是相同的,不修改动的,红线是需要移除的,蓝线是需要新增的

所以我把旧数据的menuId和新数据的menuId提取出来了,旧数据在新数据中查询这个menuId是否存在,不存在则是需要移除的,新数据在旧数据中查询某一个menuId是否存在,新的menuId不存在就是需要新增的

源码

在公众号内发送后台即可获取源码和数据库

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/305697.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号