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

五 权限管理

1 介绍

  保护后台中的一些资源,在系统中拥有对应资源的访问权限才能访问对应资源

2 思路

  用户请求资源时,都是通过浏览器发送的,而控制器接受对应的请求(控制器就是请求的入口),再将请求传递给controller层,因此需要在进入请求入口前进行权限判定(拦截器),程序中一般有很多的控制器,每个控制器中又有很多控制器方法,但不是所有控制器方法都需要权限判定(公司主页,论坛,登录页面)我们只需在保护的资源上添加标记。(注解)

  我们通过自定义注解的方式,给需要收到权限控制的方法贴上标记,由于权限不同,所以要给予不同权限不同的名字用来区分,名字是给程序员用来区分的,此处应再设置表达式,使程序进行区分,表达式是访问对应方法所需的权限,因此表达式不能重复。一般表达式的组成由各级目录组成,因为路径是唯一的,如@RequirePermission(name=“部门删除”,expression=“department:delete”)

  此时处理器上有名字,有权限(打上标记),当浏览器请求过来时,可以将其交给拦截器处理,拦截器拿到处理器方法上的注解中的权限后,与当前用户的权限做对比,有对应的权限则放行(联表查询)

  权限表达式应该被存储到权限表中,该表包含id,name,expremission。这样我们可以获取所有controller中方法上贴的@RequirePermission注解中的数据,封装成Permission对象存储到数据库中

3 数据库表

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

4 逆向工程依赖

<!-- MyBatis 逆向工程插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
    <verbose>true</verbose>
    <overwrite>false</overwrite>
</configuration>
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.45</version>
    </dependency>
</dependencies>
</plugin>

5 逆向工程xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
		PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
		"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!-- 配置生成器 -->
<generatorConfiguration>
	<context id="mysql" defaultModelType="hierarchical"
			 targetRuntime="MyBatis3Simple">
		<!-- 自动识别数据库关键字,默认false,如果设置为true,根据SqlReservedWords中定义的关键字列表; 一般保留默认值,遇到数据库关键字(Java关键字),使用columnOverride覆盖 -->
		<property name="autoDelimitKeywords" value="false" />
		<!-- 生成的Java文件的编码 -->
		<property name="javaFileEncoding" value="UTF-8" />
		<!-- 格式化java代码 -->
		<property name="javaFormatter"
				  value="org.mybatis.generator.api.dom.DefaultJavaFormatter" />
		<!-- 格式化XML代码 -->
		<property name="xmlFormatter"
				  value="org.mybatis.generator.api.dom.DefaultXmlFormatter" />

		<!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
		<property name="beginningDelimiter" value="`" />
		<property name="endingDelimiter" value="`" />

		<commentGenerator>
			<property name="suppressDate" value="true" />
			<property name="suppressAllComments" value="true" />
		</commentGenerator>

		<!-- 必须要有的,使用这个配置链接数据库 @TODO:是否可以扩展 -->
		<jdbcConnection driverClass="com.mysql.jdbc.Driver"
						connectionURL="jdbc:mysql:///rbac" userId="root" password="root">
			<!-- 这里面可以设置property属性,每一个property属性都设置到配置的Driver-->
		</jdbcConnection>

		<!-- java类型处理器 用于处理DB中的类型到Java中的类型,默认使用JavaTypeResolverDefaultImpl; 注意一点,默认会先尝试使用IntegerLongShort等来对应DECIMALNUMERIC数据类型; -->
		<javaTypeResolver
				type="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl">
			<!-- true:使用BigDecimal对应DECIMALNUMERIC数据类型 false:默认, scale>0;length>18:使用BigDecimal; 
				scale=0;length[10,18]:使用Long; scale=0;length[5,9]:使用Integer; scale=0;length<5:使用Short-->
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>


		<!-- java模型创建器,是必须要的元素 负责:1,key类(见context的defaultModelType);2,java类;3,查询类 
			targetPackage:生成的类要放的包,真实的包受enableSubPackages属性控制; targetProject:目标项目,指定一个存在的目录下,生成的内容会放到指定目录中,如果目录不存在,MBG不会自动建目录 -->
		<javaModelGenerator targetPackage="cn.tj.domain"
							targetProject="src/main/java">
			<!-- for MyBatis3/MyBatis3Simple 自动为每一个生成的类创建一个构造方法,构造方法包含了所有的field;而不是使用setter; -->
			<property name="constructorBased" value="false" />

			<!-- for MyBatis3 / MyBatis3Simple 是否创建一个不可变的类,如果为true, 那么MBG会创建一个没有setter方法的类,取而代之的是类似constructorBased的类 -->
			<property name="immutable" value="false" />

			<!-- 设置是否在getter方法中,对String类型字段调用trim()方法
			<property name="trimStrings" value="true" /> -->
		</javaModelGenerator>

		<!-- 生成SQL map的XML文件生成器, 注意,在Mybatis3之后,我们可以使用mapper.xml文件+Mapper接口(或者不用mapper接口),
			或者只使用Mapper接口+Annotation,所以,如果 javaClientGenerator配置中配置了需要生成XML的话,这个元素就必须配置
			targetPackage/targetProject:同javaModelGenerator -->
		<sqlMapGenerator targetPackage="cn.tj.mapper"
						 targetProject="src/main/resources">
			<!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
			<property name="enableSubPackages" value="true" />
		</sqlMapGenerator>


		<!-- 对于mybatis来说,即生成Mapper接口,注意,如果没有配置该元素,那么默认不会生成Mapper接口 targetPackage/targetProject:同javaModelGenerator
			type:选择怎么生成mapper接口(在MyBatis3/MyBatis3Simple下): 1ANNOTATEDMAPPER:会生成使用Mapper接口+Annotation的方式创建(SQL生成在annotation中),不会生成对应的XML2MIXEDMAPPER:使用混合配置,会生成Mapper接口,并适当添加合适的Annotation,但是XML会生成在XML中; 3XMLMAPPER:会生成Mapper接口,接口完全依赖XML;
			注意,如果context是MyBatis3Simple:只支持ANNOTATEDMAPPERXMLMAPPER -->
		<javaClientGenerator targetPackage="cn.tj.mapper"
							 type="XMLMAPPER" targetProject="src/main/java">
			<!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
			<property name="enableSubPackages" value="true" />

			<!-- 可以为所有生成的接口添加一个父接口,但是MBG只负责生成,不负责检查 <property name="rootInterface"
				value=""/> -->
		</javaClientGenerator>

		<table tableName="permission">
			<property name="useActualColumnNames" value="true"/>
			<property name="constructorBased" value="false" />
			<generatedKey column="id" sqlStatement="JDBC" />
		</table>
	</context>
</generatorConfiguration>

6 自动生成

① 实体类

@Getter
@Setter
public class Permission {
    private Long id;
    private String name;
    private String expression;
}

② 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.PermissionMapper" >
  <resultMap id="BaseResultMap" type="cn.tj.domain.Permission" >
    <id column="id" property="id" />
    <result column="name" property="name" />
    <result column="expression" property="expression" />
  </resultMap>
  <delete id="deleteByPrimaryKey" >
    delete from permission
    where id = #{id}
  </delete>
  <insert id="insert" useGeneratedKeys="true" keyProperty="id" >
    insert into permission (name, expression)
    values (#{name}, #{expression})
  </insert>
  <update id="updateByPrimaryKey" >
    update permission
    set name = #{name},
      expression = #{expression}
    where id = #{id}
  </update>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" >
    select id, name, expression
    from permission
    where id = #{id}
  </select>
  <select id="selectAll" resultMap="BaseResultMap" >
    select id, name, expression
    from permission
  </select>
    <select id="selectForList" resultMap="BaseResultMap">
      select id, name, expression
      from permission
    </select>
    <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>
</mapper>

③ mapper

@Repository
public interface PermissionMapper {
    int deleteByPrimaryKey(Long id);

    int insert(Permission record);

    Permission selectByPrimaryKey(Long id);

    List<Permission> selectAll();

    int updateByPrimaryKey(Permission record);

    List<Permission> selectForList(QueryObject qo);

    List<Permission> queryByRoleId(Long roleId);
}

7 service

① 接口

public interface IPermissionService {
    void save(Permission permission);
    void delete(Long id);
    void update(Permission permission);
    Permission get(Long id);
    List<Permission> listAll();
    // 查询分页方法
    PageInfo<Permission> query(QueryObject qo);
    /**
     * 加载权限
     */
    void reload();
    /**
     * 根据角色 id 查询权限集合
     * @param roleId 角色 id
     * @return List<Permission> 权限集合
     */
    List<Permission> queryByRoleId(Long roleId);
}

② 实现类

@Service
public class PermissionServiceImpl implements IPermissionService, ApplicationContextAware {
    @Autowired
    private PermissionMapper permissionMapper;

    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ctx = applicationContext;
    }

    public void setPermissionMapper(PermissionMapper permissionMapper) {
        this.permissionMapper = permissionMapper;
    }

    @Override
    @Transactional
    public void save(Permission permission) {
        permissionMapper.insert(permission);
    }

    @Override
    public void delete(Long id) {
        permissionMapper.deleteByPrimaryKey(id);
    }

    @Override
    public void update(Permission permission) {
        permissionMapper.updateByPrimaryKey(permission);
    }

    @Override
    public Permission get(Long id) {
        return permissionMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<Permission> listAll() {
        return permissionMapper.selectAll();
    }

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

    @Override
    public void reload() {
        // 为了防止重复添加,我们需要将所有的权限集合先查询出来
        List<Permission> permissionList = this.listAll();
        // 因为我们对比只需要对比 expression。为了防止重复,我们可以定义一个 set 集合
        Set<String> permissionSet = new HashSet<>();
        for (Permission permission : permissionList) {
            permissionSet.add(permission.getExpression());
        }

        // 拿到所有的 Controller。所有的Controller 都存储在容器中。所以我们要拿到 ApplicationContext
        // System.out.println(ctx.getBeansWithAnnotation(Controller.class));
        // 通过 RequestMappingHandlerMapping 方法拿到 所有的 Map<映射路径-->方法信息>
        RequestMappingHandlerMapping rmhm = ctx.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods1 = rmhm.getHandlerMethods();
        // 因为我们只需要拿到所有的方法信息  所以获取对应 Map 的 Value 值即可。
        Collection<HandlerMethod> handlerMethods = handlerMethods1.values();
        // System.out.println(handlerMethods);
        for (HandlerMethod method : handlerMethods) {
            // 获取方法上的注解
            RequirePermission rpAnnotation = method.getMethodAnnotation(RequirePermission.class);
            if (rpAnnotation != null) {
                // 若 rpAnnotation 不为空,说明我们拿到了该方法上的自定义注解 RequirePermission
                // 获取其 name he expression
                String name = rpAnnotation.name();
                String expression = rpAnnotation.expression();
                // 判断数据库中是否已经包含了这个表达式
                if(!permissionSet.contains(expression)){
                    Permission p = new Permission();
                    p.setName(name);
                    p.setExpression(expression);
                    // 保存到数据库中
                    permissionMapper.insert(p);
                }
            }
        }
    }

    @Override
    public List<Permission> queryByRoleId(Long roleId) {
        return permissionMapper.queryByRoleId(roleId);
    }
}

8 controller

@Controller
@RequestMapping("/permission")
public class PermissionController {
    @Autowired
    private IPermissionService permissionService;
    // 处理部门查询所有方法
    @RequestMapping("/list")
    public String list(Model model, QueryObject qo){
        PageInfo<Permission> pageInfo = permissionService.query(qo);
        model.addAttribute("pageInfo",pageInfo);
        return "permission/list";
    }

    @RequestMapping("/reload")
    public String reload(){
        // 加载权限
        permissionService.reload();
        return "redirect:/permission/list";
    }
}

9 前端 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" >
                    <!--高级查询--->
                    <form class="form-inline" id="searchForm" action="/permission/list" method="post">
                        <input type="hidden" name="currentPage" id="currentPage" value="1">
                        <a href="javascript:void(0)" class="btn btn-success btn-reload" style="margin: 10px;">
                            <span class="glyphicon glyphicon-repeat"></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>
                            </tr>
                            </thead>
                            <tbody>
                                <tr th:each="permission,start:${pageInfo.list}">
                                    <td th:text="${start.count}">1</td>
                                    <td th:text="${permission.name}">部门查询</td>
                                    <td th:text="${permission.expression}">department:list</td>
                                </tr>
                            </tbody>
                        </table>
                        <!--引用分页片段--->
                        <div th:replace="common/fragment :: page"></div>
                    </div>
                </div>
            </section>
        </div>
        <div th:replace="common/fragment :: footer"></div>
    </div>
    <script>
        $('.btn-reload').click(function () {
            Swal.fire({
                title: '提示框',
                text: "您确定要重新加载权限吗!",
                icon: 'info',
                showCancelButton: true,
                confirmButtonColor: '#3085d6',
                cancelButtonColor: '#d33',
                confirmButtonText: '确定!',
                cancelButtonText: '取消!'
            }).then((result) => {
                if(result.value) {
                    location.href = "/permission/reload";
                }
            });
        })
    </script>
</body>
</html>

10 自定义注解

// 什么时期生效,以及可以贴在哪里
// 贴在方法上
@Target(ElementType.METHOD)
// 运行期有效 浏览器发来请求时 程序正在运行
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
	// 注解中可传递的参数
    String name();
    String expression();
}

11 为方法贴上注解

① DepartmentController

@Controller
@RequestMapping("/department")
public class DepartmentController {
    @Autowired
    private IDepartmentService departmentService;
    // 处理部门查询所有方法
    @RequestMapping("/list")
    @RequirePermission(name="部门列表",expression = "department:list")
    public String list(Model model, QueryObject qo){
        PageInfo<Department> pageInfo = departmentService.query(qo);
        model.addAttribute("pageInfo",pageInfo);
        return "department/list";  // WEB-INF/views/    department/list     .jsp
    }
    // 处理部门删除方法
    @RequestMapping("/delete")
    @RequirePermission(name="部门删除",expression = "department:delete")
    public String delete(Long id){
        if (id != null) {
            departmentService.delete(id);
        }
        return "redirect:/department/list"; // 再次发起请求 到我们上面的查询所有的控制器方法。
    }
    // 进入部门新增/编辑页面方法
    @RequirePermission(name="进入部门新增/编辑页面",expression = "department:input")
    @RequestMapping("/input")
    public String input(Long id,Model model){
        if (id != null) {
            // 修改
            Department department = departmentService.get(id);
            model.addAttribute("department",department);
        }
        return "department/input";   // WEB-INF/views/    department/input     .jsp
    }
    // 部门新增方法
    @RequestMapping("/saveOrUpdate")
    @RequirePermission(name="部门新增/编辑",expression = "department:saveOrUpdate")
    public String saveOrUpdate(Department department){
        if(department.getId() == null){
            departmentService.save(department);
        } else {
            departmentService.update(department);
        }
        return "redirect:/department/list"; // 再次发起请求 到我们上面的查询所有的控制器方法。
    }
}

12 重新加载 – 扫描所有自定义注解

  单击重新加载发送请求,扫描所有的 controller,拿到所有方法上的注解,如果贴了自定义注解说明非公共方法需要权限控制,取出注解中的 name与expression 封住成一个对象,存入表中,然后展示出来。
① 前端页面

<!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-inline" id="searchForm" action="/permission/list" method="post">
                        <input type="hidden" name="currentPage" id="currentPage" value="1">
                        <a href="javascript:void(0)" class="btn btn-success btn-reload" style="margin: 10px;">
                            <span class="glyphicon glyphicon-repeat"></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>
                            </tr>
                            </thead>
                            <tbody>
                                <tr th:each="permission,start:${pageInfo.list}">
                                    <td th:text="${start.count}">1</td>
                                    <td th:text="${permission.name}">部门查询</td>
                                    <td th:text="${permission.expression}">department:list</td>
                                </tr>
                            </tbody>
                        </table>
                        <div th:replace="common/fragment :: page"></div>
                    </div>
                </div>
            </section>
        </div>
        <div th:replace="common/fragment :: footer"></div>
    </div>
    <script>
        $('.btn-reload').click(function () {
            Swal.fire({
                title: '提示框',
                text: "您确定要重新加载权限吗!",
                icon: 'info',
                showCancelButton: true,
                confirmButtonColor: '#3085d6',
                cancelButtonColor: '#d33',
                confirmButtonText: '确定!',
                cancelButtonText: '取消!'
            }).then((result) => {
                if(result.value) {
                    location.href = "/permission/reload";
                }
            });
        })
    </script>
</body>
</html>

② controller

@Controller
@RequestMapping("/permission")
public class PermissionController {
    @Autowired
    private IPermissionService permissionService;
    // 处理部门查询所有方法
    @RequestMapping("/list")
    public String list(Model model, QueryObject qo){
        PageInfo<Permission> pageInfo = permissionService.query(qo);
        model.addAttribute("pageInfo",pageInfo);
        return "permission/list";
    }

    @RequestMapping("/reload")
    public String reload(){
        // 加载权限
        permissionService.reload();
        // 此处必须用重定向 转发意味着表单重复提交(不能刷新)
        // 找上面的查询所有方法
        return "redirect:/permission/list";
    }
}

③ service接口

public interface IPermissionService {
    void save(Permission permission);
    void delete(Long id);
    void update(Permission permission);
    Permission get(Long id);
    List<Permission> listAll();
    // 查询分页方法
    PageInfo<Permission> query(QueryObject qo);
    /**
     * 加载权限
     */
    void reload();
    /**
     * 根据角色 id 查询权限集合
     * @param roleId 角色 id
     * @return List<Permission> 权限集合
     */
    List<Permission> queryByRoleId(Long roleId);
}

④ service实现类

@Service
public class PermissionServiceImpl implements IPermissionService, ApplicationContextAware {
	// 所有的Controller 都存储在容器中。所以我们要拿到 ApplicationContext 容器
	// 第一种拿到容器的方式 直接DI注入获取
	// 第二种拿到容器的方式 实现 ApplicationContextAware 接口 实现 setApplicationContext 方法 获取
    @Autowired
    private PermissionMapper permissionMapper;
    // @Autowired 拿到容器对象即可获取所有的controller
    private ApplicationContext ctx;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ctx = applicationContext;
    }
    
    public void setPermissionMapper(PermissionMapper permissionMapper) {
        this.permissionMapper = permissionMapper;
    }
    @Override
    @Transactional
    public void save(Permission permission) {
        permissionMapper.insert(permission);
    }
    @Override
    public void delete(Long id) {
        permissionMapper.deleteByPrimaryKey(id);
    }
    @Override
    public void update(Permission permission) {
        permissionMapper.updateByPrimaryKey(permission);
    }
    @Override
    public Permission get(Long id) {
        return permissionMapper.selectByPrimaryKey(id);
    }
    @Override
    public List<Permission> listAll() {
        return permissionMapper.selectAll();
    }
    @Override
    public PageInfo<Permission> query(QueryObject qo) {
        PageHelper.startPage(qo.getCurrentPage(),qo.getPageSize());
        return new PageInfo<>(permissionMapper.selectForList(qo));
    }
    @Override
    public void reload() {
        // 为了防止重复添加,我们需要将所有的权限集合先查询出来,this为当前类
        List<Permission> permissionList = this.listAll();
        // 因为我们对比只需要对比 expression 为了防止重复,我们可以定义一个 set 集合
        Set<String> permissionSet = new HashSet<>();
        for (Permission permission : permissionList) {
            permissionSet.add(permission.getExpression());
        }
        
        // 通过容器获取所有的controller,获取所有controller后即可拿所有方法
        // System.out.println(ctx.getBeansWithAnnotation(Controller.class));

		// 根据springMVC流程,用户请求来到前端控制器后会去找处理器映射器HandlerMapping
		// 这里有方法的首部声明和映射信息(哪一个路径对应哪一个方法),HandlerMapping会返回一个处理器执行链
		// 执行器链中有哪一个路径对应哪一个方法(有方法名没有方法体),包括方法上的注解,以及拦截器链
		// 拿到该对象就拿到了所有的方法,就可以省去拿所有的controller这一步

        // 通过 RequestMappingHandlerMapping 方法拿到 所有的 Map<映射路径-->方法信息>(可以通过DI获取该对象)
        RequestMappingHandlerMapping rmhm = ctx.getBean(RequestMappingHandlerMapping.class);
        // RequestMappingInfo 是请求映射信息,也就是映射路径,如@RequestMapping("/permission")
        // HandlerMethod 是方法名(有传递的参数类型,没有方法体)
        Map<RequestMappingInfo, HandlerMethod> handlerMethods1 = rmhm.getHandlerMethods();
        
        // 我们只需拿到所有的方法上的自定义注解 所以获取对应 Map 的 Value 值即可。
        Collection<HandlerMethod> handlerMethods = handlerMethods1.values();
        // 获取所有方法
        // System.out.println(handlerMethods);

		// 遍历所有方法
        for (HandlerMethod method : handlerMethods) {
            // 获取方法上的注解
            RequirePermission rpAnnotation = method.getMethodAnnotation(RequirePermission.class);
            if (rpAnnotation != null) {
            	// 若 rpAnnotation 为空,说明该方法为公共方法,不需要进行权限判定
                // 若 rpAnnotation 不为空,说明我们拿到了该方法上的自定义注解 RequirePermission
                // 获取其 name he expression
                String name = rpAnnotation.name();
                String expression = rpAnnotation.expression();
                // 判断数据库中是否已经包含了这个表达式,防止重复插入
                if(!permissionSet.contains(expression)){
                    Permission p = new Permission();
                    p.setName(name);
                    p.setExpression(expression);
                    // 保存到数据库中
                    permissionMapper.insert(p);
                }
            }
        }
    }
    @Override
    public List<Permission> queryByRoleId(Long roleId) {
        return permissionMapper.queryByRoleId(roleId);
    }
}

13 权限管理小结

  • 权限查询
    • 创建 permission 表,通过逆向工程生成所需要的代码,完成 Service.Controller 代码。
    • 完成 permission/list 页面的遍历。
  • 权限加载
    • 自定义注解(参考别人的写)
    • 查询所有权限集合,然后封装为 Set 集合。因为我们只需要对比 expression 。所以该集合泛型为
    • 获取所有的控制器方法,我们可以使用 AutoWired 注入RequestMappingHandlerMapping 或者从 ApplicationContext中获取。
    • 通过该对象我们获取到所有的 Controller 中的 控制器方法的集合。
    • 我们去遍历所有这些方法获取每一个 HandlerMethod
    • 通过 method.getMethodAnnotation(RequirePermission.class) 来获取我们的自定义注解
    • 判定该方法上是否贴有该注解,若不存在则说明是公共方法
    • 若存在则需要获取到这个注解的 name 和 expression 。
    • 判定 expression 在之前数据库查询的 Set 集合中是否存在。若不存在则说明不重复。封装对象。插入数据库。
  • 23
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

泰勒疯狂展开

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

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

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

打赏作者

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

抵扣说明:

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

余额充值