五 权限管理
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; 注意一点,默认会先尝试使用Integer,Long,Short等来对应DECIMAL和
NUMERIC数据类型; -->
<javaTypeResolver
type="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl">
<!-- true:使用BigDecimal对应DECIMAL和 NUMERIC数据类型 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下): 1,ANNOTATEDMAPPER:会生成使用Mapper接口+Annotation的方式创建(SQL生成在annotation中),不会生成对应的XML;
2,MIXEDMAPPER:使用混合配置,会生成Mapper接口,并适当添加合适的Annotation,但是XML会生成在XML中; 3,XMLMAPPER:会生成Mapper接口,接口完全依赖XML;
注意,如果context是MyBatis3Simple:只支持ANNOTATEDMAPPER和XMLMAPPER -->
<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 集合中是否存在。若不存在则说明不重复。封装对象。插入数据库。