Java研学-RBAC权限控制(五)

六 角色管理

1 思路

  角色和权限是不同的,比如人事部内部可以有更详细的角色划分,人事专员,人事主管,人事总监等。中间表不需使用逆向工程生成实体,需要的时候定义即可

2 数据库表

① 角色表

CREATE TABLE `role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `sn` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

② 角色权限中间表 – 该表不能够设置主键(多对多关系)

CREATE TABLE `role_permission` (
  `role_id` bigint(20) DEFAULT NULL,
  `permission_id` bigint(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3 使用逆向工程

① 实体类

// getset便于后期字段维护
@Getter
@Setter
public class Role {
    private Long id;
    private String name;
    private String sn;
}

② mapper接口

public interface RoleMapper {
    int deleteByPrimaryKey(Long id);

    int insert(Role record);

    Role selectByPrimaryKey(Long id);

    List<Role> selectAll();

    int updateByPrimaryKey(Role record);

    List<Role> selectForList(QueryObject qo);
	// 此处可以通过mybatisx快速填写参数
    void insertRelation(@Param("roleId") Long id, @Param("permissionId") Long permissionId);

    void insertRelationBatch(@Param("roleId") Long id, @Param("permissionIds") Long[] permissionIds);

    void deleteRelation(Long roleId);
}

③ mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.tj.mapper.RoleMapper" >
  <resultMap id="BaseResultMap" type="cn.tj.domain.Role" >
    <id column="id" property="id" />
    <result column="name" property="name" />
    <result column="sn" property="sn" />
  </resultMap>
  <delete id="deleteByPrimaryKey" >
    delete from role
    where id = #{id}
  </delete>
  <delete id="deleteRelation">
    delete from role_permission where role_id=#{roleId}
  </delete>
  <insert id="insert" useGeneratedKeys="true" keyProperty="id" >
    insert into role (name, sn)
    values (#{name}, #{sn})
  </insert>
  <insert id="insertRelation">
    insert into role_permission(role_id, permission_id) values (#{roleId},#{permissionId})
  </insert>
  <insert id="insertRelationBatch">
    insert into role_permission(role_id,permission_id) values
    <foreach collection="permissionIds" item="permissionId" separator=",">
      (#{roleId},#{permissionId})
    </foreach>
  </insert>
  <update id="updateByPrimaryKey" >
    update role
    set name = #{name},
      sn = #{sn}
    where id = #{id}
  </update>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" >
    select id, name, sn
    from role
    where id = #{id}
  </select>
  <select id="selectAll" resultMap="BaseResultMap" >
    select id, name, sn
    from role
  </select>
    <select id="selectForList" resultMap="BaseResultMap">
      select id, name, sn
      from role
    </select>
</mapper>

4 service – 注意修改分页 – 角色新增

① 接口

public interface IRoleService {
    void save(Role role, Long[] permissionIds);

    void delete(Long id);

    void update(Role role, Long[] permissionIds);

    Role get(Long id);

    List<Role> listAll();
    // 查询分页方法
    PageInfo<Role> query(QueryObject qo);
}

② 实现类

@Service
// @Slf4j
public class RoleServiceImpl implements IRoleService {
    @Autowired
    private RoleMapper roleMapper;

    public void setRoleMapper(RoleMapper roleMapper) {
        this.roleMapper = roleMapper;
    }

	// 需先插入角色表,再插入中间表,中间表需要角色id和权限id
    @Override
    @Transactional
    public void save(Role role, Long[] permissionIds) {
        // 向角色表中插入数据
        roleMapper.insert(role);
        // 维护中间表关系  必须插入角色后才会回填 id 应避免这种循环插入数据库的操作(优化)
        /*for (Long permissionId : permissionIds) {
            roleMapper.insertRelation(role.getId(),permissionId);
        }*/

        // 使用批量的时候要小心数据量过大,解决的方式就是批处理。(使用动态sql)
        if(permissionIds != null && permissionIds.length > 0){
            roleMapper.insertRelationBatch(role.getId(),permissionIds);
        }
    }

    @Override
    @Transactional
    public void delete(Long id) {
        // 删除中间表信息
        roleMapper.deleteRelation(id);
        // 删除自身信息
        roleMapper.deleteByPrimaryKey(id);
    }

    @Override
    @Transactional
    public void update(Role role, Long[] permissionIds) {
        // 修改角色信息
        roleMapper.updateByPrimaryKey(role);
        // 维护中间表关系(我们采取的就是先删后加策略)
        // 删除中间表关系
        roleMapper.deleteRelation(role.getId());
        // 添加中间表关系
        if(permissionIds != null && permissionIds.length > 0){
            roleMapper.insertRelationBatch(role.getId(),permissionIds);
        }
    }

    @Override
    public Role get(Long id) {
        return roleMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<Role> listAll() {
        return roleMapper.selectAll();
    }

    @Override
    public PageInfo<Role> query(QueryObject qo) {
        PageHelper.startPage(qo.getCurrentPage(),qo.getPageSize());
        return new PageInfo<Role>(roleMapper.selectForList(qo));
    }
}

5 controller

@Controller
@RequestMapping("/role")
public class RoleController {
    @Autowired
    private IRoleService roleService;
    // 需查询所有权限
    @Autowired
    private IPermissionService permissionService;
    // 处理角色查询所有方法
    @RequestMapping("/list")
    @RequirePermission(name="角色列表",expression = "role:list")
    public String list(Model model, QueryObject qo){
        PageInfo<Role> pageInfo = roleService.query(qo);
        model.addAttribute("pageInfo",pageInfo);
        return "role/list";  // WEB-INF/views/    role/list     .jsp
    }

    // 处理角色删除方法
    @RequestMapping("/delete")
    @RequirePermission(name="角色删除",expression = "role:delete")
    public String delete(Long id){
        if (id != null) {
            roleService.delete(id);
        }
        return "redirect:/role/list"; 
    }

    // 进入角色新增/编辑页面方法 需回显角色,所有权限,拥有权限的信息
    @RequirePermission(name="进入角色新增/编辑页面",expression = "role:input")
    @RequestMapping("/input")
    public String input(Long id,Model model){
        // 查询所有的权限信息列表
        List<Permission> allPermissions = permissionService.listAll();
        model.addAttribute("allPermissions",allPermissions);
        if (id != null) {
            // 修改
            // 当前角色的信息
            Role role = roleService.get(id);
            model.addAttribute("role",role);
            // 根据角色id查询当前角色的权限集合
            List<Permission> selfPermissions = permissionService.queryByRoleId(id);
            model.addAttribute("selfPermissions",selfPermissions);
        }
        return "role/input"; 
    }

	// 此处前端返回参数需要使用数组进行接收(名字必须相同,不同接收不到)
    // 角色新增方法
    @RequestMapping("/saveOrUpdate")
    @RequirePermission(name="角色新增/编辑",expression = "role:saveOrUpdate")
    public String saveOrUpdate(Role role,Long[] permissionIds){
        if(role.getId() == null){
            roleService.save(role,permissionIds);
        } else {
            roleService.update(role,permissionIds);
        }
        return "redirect:/role/list"; 
    }
}

6 前端

① list.html – 角色列表

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>角色管理</title>
</head>
<body class="hold-transition skin-black sidebar-mini">
    <div th:replace="common/fragment :: link"></div>
    <div class="wrapper">
        <div th:replace="common/fragment :: navbar"></div>
        <div th:replace="common/fragment :: menu"></div>
        <div class="content-wrapper">
            <section class="content-header">
                <h1>角色管理</h1>
            </section>
            <section class="content">
                <div class="box">
                    <!--高级查询--->
                    <div style="margin: 10px;">
                        <!--高级查询--->
                        <form class="form-inline" id="searchForm" action="/role/list" method="post">
                            <input type="hidden" name="currentPage" id="currentPage" value="1">
                            <a href="/role/input" class="btn btn-success btn-input"><span class="glyphicon glyphicon-plus"></span> 添加</a>
                        </form>
                        <div class="box-body table-responsive ">
                            <table class="table table-hover table-bordered table-striped" >
                                <thead>
                                <tr>
                                    <th>编号</th>
                                    <th>角色名称</th>
                                    <th>角色编号</th>
                                    <th>操作</th>
                                </tr>
                                </thead>
                                <tbody>
                                    <tr th:each="role,start:${pageInfo.list}">
                                        <td th:text="${start.count}">1</td>
                                        <td th:text="${role.name}">部门经理</td>
                                        <td th:text="${role.sn}">MGR</td>
                                        <td>
                                            <a class="btn btn-info btn-xs btn-input" th:href="|/role/input?id=${role.id}|">
                                                <span class="glyphicon glyphicon-pencil"></span> 编辑
                                            </a>
                                            <a class="btn btn-danger btn-xs btn-delete" th:data-url="|/role/delete?id=${role.id}|">
                                                <span class="glyphicon glyphicon-trash"></span> 删除
                                            </a>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                            <div th:replace="common/fragment :: page"></div>
                        </div>
                    </div>
                </div>
            </section>
        </div>
        <div th:replace="common/fragment :: footer"></div>
    </div>
</body>
</html>

② input.html
  角色新增较复杂故不使用模态框,新增时需添加角色和该角色有哪些权限。展示时角色列表不展示各角色拥有哪些权限(一个角色可能有很多权限,可设置查看权限按钮)。添加角色时,角色名称与编号是角色数据,分配权限左侧框体内为所有的可分配权限,右侧为可以被分配的权限

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>角色管理</title>
</head>
<body class="hold-transition skin-black sidebar-mini">
    <div th:replace="common/fragment :: link"></div>
    <div class="wrapper">
        <div th:replace="common/fragment :: navbar"></div>
        <div th:replace="common/fragment :: menu"></div>
        <div class="content-wrapper">
            <section class="content-header">
                <h1>角色编辑</h1>
            </section>
            <section class="content">
                <div class="box">
                    <form class="form-horizontal" action="/role/saveOrUpdate" method="post" id="editForm">
                    <!--?.Thymeleaf的安全导航操作符。role不是null获取role的id属性。role是null表达式的结果将为null,并且不会尝试访问id,从而避免可能的空指针异常。-->
                        <input type="hidden" name="id" th:value="${role?.id}">
                        <div class="form-group"  style="margin-top: 10px;">
                            <label  class="col-sm-2 control-label">角色名称:</label>
                            <div class="col-sm-6">
                                <input type="text" class="form-control" th:value="${role?.name}" name="name" placeholder="请输入角色名称">
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">角色编号:</label>
                            <div class="col-sm-6">
                                <input type="text" class="form-control" name="sn" th:value="${role?.sn}" placeholder="请输入角色编号">
                            </div>
                        </div>
                        <div class="form-group " id="role">
                            <label for="role" class="col-sm-2 control-label">分配权限:</label><br/>
                            <div class="row" style="margin-top: 10px">
                                <div class="col-sm-2 col-sm-offset-2">
                                    <select multiple class="form-control allPermissions" style="height: 350px;" size="15">
                                        <option th:each="permission:${allPermissions}"
                                            th:text="${permission.name}"
                                            th:value="${permission.id}"></option>
                                    </select>
                                </div>
                                <div class="col-sm-1" style="margin-top: 60px;" align="center">
                                    <div>
                                        <a type="button" class="btn btn-primary" style="margin-top: 10px" title="右移动"
                                           onclick="moveSelected('allPermissions', 'selfPermissions')">
                                            <span class="glyphicon glyphicon-menu-right"></span>
                                        </a>
                                    </div>
                                    <div>
                                        <a type="button" class="btn btn-primary " style="margin-top: 10px" title="左移动"
                                           onclick="moveSelected('selfPermissions', 'allPermissions')">
                                            <span class="glyphicon glyphicon-menu-left"></span>
                                        </a>
                                    </div>
                                    <div>
                                        <a type="button" class="btn btn-primary " style="margin-top: 10px" title="全右移动"
                                           onclick="moveAll('allPermissions', 'selfPermissions')">
                                            <span class="glyphicon glyphicon-forward"></span>
                                        </a>
                                    </div>
                                    <div>
                                        <a type="button" class="btn btn-primary " style="margin-top: 10px" title="全左移动"
                                           onclick="moveAll('selfPermissions', 'allPermissions')">
                                            <span class="glyphicon glyphicon-backward"></span>
                                        </a>
                                    </div>
                                </div>
                                <div class="col-sm-2">
                                    <select multiple class="form-control selfPermissions" style="height: 350px;" size="15" name="permissionIds">
                                        <option th:each="permission:${selfPermissions}"
                                                th:text="${permission.name}"
                                                th:value="${permission.id}"></option>
                                    </select>
                                </div>
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-sm-offset-1 col-sm-6">
                                <button type="button" class="btn btn-primary btn-submit">保存</button>
                                <a href="javascript:window.history.back()" class="btn btn-danger">取消</a>
                            </div>
                        </div>
                    </form>
                </div>
            </section>
        </div>
        <div th:replace="common/fragment :: footer"></div>
    </div>
    <script>
        // 全部左右移动
        function moveAll(src, target) {
            $('.' + target).append($('.' + src + ' > option'));
        }
        // 选中左右移动
        function moveSelected(src, target) {
            $('.' + target).append($('.' + src + ' > option:selected'));
        }
        // 点击保存按钮
        $('.btn-submit').click(function () {
        // 提交表单前获取右侧选框中的所有权限,防止用户丢失选中,将其全部设为选中
            $('.selfPermissions > option').prop('selected',true);
            $('#editForm').submit();
        })
        // 一进入页面就需要进行去重
        $(function () {
            // 下拉列表去重 角色回显的已拥有权限,应从全部权限中去除
            // 获取角色的全部权限
            // map会进行遍历以kv的形式将数据存到数组中,最后需要return数据,该方法会返回一个数组(不用pushl)
            var arr = $('.selfPermissions > option').map(function (index, domEle) {
            	// 此处获得的是JQ对象
                return $(domEle).val();
            })
            // 与所有权限遍历做比对
            $('.allPermissions > option').each(function (i, e) {
                // 判断每次遍历的元素是否在上面的数组(角色权限)中出现过?(出现代表重复,需要删除)
                // inArray参数为(每次遍历的元素,查看是否存在在其中的数组 判断条件)
                if($.inArray($(e).val(),arr) >= 0){
                    // 若存在则返回索引下标,说明重复了。我们要将该元素删除,不存在则返回-1
                    $(e).remove();
                }
            })
        })
    </script>
</body>
</html>

7 IPermissionService 接口 – 角色编辑所需查询权限方法

public interface IPermissionService {
	......
    /**
     * 根据角色 id 查询权限集合
     * @param roleId 角色 id
     * @return List<Permission> 权限集合
     */
    List<Permission> queryByRoleId(Long roleId);
}

8 PermissionServiceImpl 实现类

@Service
public class PermissionServiceImpl implements IPermissionService, ApplicationContextAware {
	......
    @Override
    public List<Permission> queryByRoleId(Long roleId) {
        return permissionMapper.queryByRoleId(roleId);
    }
}

9 PermissionMapper

@Repository
public interface PermissionMapper {
	......
    List<Permission> queryByRoleId(Long roleId);
}

10 PermissionMapper.xml – 子查询

......
<select id="queryByRoleId" resultMap="BaseResultMap">
        select id,name,expression from permission where id in (
        select permission_id from role_permission where role_id = #{roleId})
</select>

11 RoleController – 修改时可更改角色信息以及权限信息(都要传)

	// 进入角色新增/编辑页面方法
    @RequirePermission(name="进入角色新增/编辑页面",expression = "role:input")
    @RequestMapping("/input")
    public String input(Long id,Model model){
        // 所有的权限信息列表
        List<Permission> allPermissions = permissionService.listAll();
        model.addAttribute("allPermissions",allPermissions);
        if (id != null) {
            // 修改
            // 当前角色的信息
            Role role = roleService.get(id);
            model.addAttribute("role",role);
            // 当前角色的权限集合
            List<Permission> selfPermissions = permissionService.queryByRoleId(id);
            model.addAttribute("selfPermissions",selfPermissions);
        }
        return "role/input";   // WEB-INF/views/    role/input     .jsp
    }

	// 角色新增方法 单击保存时需传递多个参数
    @RequestMapping("/saveOrUpdate")
    @RequirePermission(name="角色新增/编辑",expression = "role:saveOrUpdate")
    public String saveOrUpdate(Role role,Long[] permissionIds){
        if(role.getId() == null){
            roleService.save(role,permissionIds);
        } else {
            roleService.update(role,permissionIds);
        }
        return "redirect:/role/list"; // 再次发起请求 到我们上面的查询所有的控制器方法。
    }

12 IRoleService

	void update(Role role, Long[] permissionIds);

13 RoleServiceImpl

	@Override
    @Transactional
    public void update(Role role, Long[] permissionIds) {
        // 修改角色信息
        roleMapper.updateByPrimaryKey(role);
        // 维护中间表关系(我们采取的就是先删后加策略)
        // 删除中间表关系(根据roleId)
        roleMapper.deleteRelation(role.getId());
        // 添加中间表关系 permissionIds是数组,若数组为空,插入方法调用mapper的动态sql就会出错,所以需要判断
        // 首先不能为null而且长度大于0
        if(permissionIds != null && permissionIds.length > 0){
            roleMapper.insertRelationBatch(role.getId(),permissionIds);
        }
    }

14 RoleMapper – deleteRelation 方法 删除中间表数据

void deleteRelation(Long roleId);

15 RoleMapper.xml – deleteRelation

<delete id="deleteRelation">
    delete from role_permission where role_id=#{roleId}
</delete>

16 Role的list.html – 角色删除

<!--此处使用data-url自定义属性进行数据传递-->
<a class="btn btn-danger btn-xs btn-delete" th:data-url="|/role/delete?id=${role.id}|">
      <span class="glyphicon glyphicon-trash"></span> 删除
</a>

<!--调sweetalter2删除框-->
$(function () {
    $('.btn-delete').click(function () {
        var url = $(this).data('url');
        Swal.fire({
            title: '您确定要删除吗?',
            text: "此操作不可撤销!",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '确定',
            cancelButtonText: '取消'
        }).then((result) => {
            if(result.value) {
                location.href = url;
            }
        });
    });

17 RoleController

	// 处理角色删除方法
    @RequestMapping("/delete")
    @RequirePermission(name="角色删除",expression = "role:delete")
    public String delete(Long id){
        if (id != null) {
            roleService.delete(id);
        }
        return "redirect:/role/list";
    }

18 IRoleService

    void delete(Long id);

19 RoleServiceImpl

	@Override
    @Transactional
    public void delete(Long id) {
    	// 此处应先删中间表的数据(中间表使用他表信息)
        // 删除中间表信息
        roleMapper.deleteRelation(id);
        // 删除自身信息
        roleMapper.deleteByPrimaryKey(id);
    }

20 角色管理小结

  • 角色列表
    • 创建 role 表及中间表 role_permission,通过逆向工程生成所需要的代码,完成 Service.Controller 代码。
    • 完成 role/list 页面的遍历。
  • 角色新增
    • 在进入新增页面之前,我们需要查询所有的权限集合。并放入作用域中。
    • 在前台页面我们就可以将所有的权限集合展示出来。
    • 完成前台页面角色的 js 左右移动。
    • 因为按钮是 button 。我们需要写一个 jQuery 调用 submit() 方法提交表单。提交之前我们需要将所有右侧下拉列表中的 option 进行选中。防止用于不选中就直接提交。
    • 在后台保存的时候接收中间表的 权限数组。
    • 调用插入方法时必须先插入角色。(因为只有插入了角色后才有角色 id)
    • 维护中间表关系。(优化后为动态 SQL 插入)
  • 角色编辑
    • 在进入角色编辑页面之前。我们需要回显3组数据。(所有的权限集合,当前角色的信息,当前角色的权限集合(子查询或多表连接))。
    • 前台在页面加载完成之后 立即去做去重操作。
    • 在后台我们编辑时使用的是 先将所有原始权限删除,在添加新的权限。
  • 角色删除
    • 别忘记维护中间表关系
    • 要先删除中间表,在删除角色。
  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泰勒疯狂展开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值