权限管理
分析
项目为什么需要做权限功能?
把系统的资源保护起来,给合理的人访问和使用
权限控制表应该如何设计?
首先需要根据需求分析,以员工为例
- 员工 —>员工表
- 员工角色表—>员工是什么角色(身份职位)
- 角色表 ---->存员工职位
- 角色权限表 ---->该角色有什么权限
- 权限表 ----->管理分配权限
权限限制
要做资源的权限限制,就是要对系统中动态资源(控制器)的处理方法做出限制,包含了CRUD等操作. 即控制器中的处理方法就是一个个权限,所以数据库中的权限表的数据来源于所有控制器的处理方法.
权限表达式的值需要有唯一性,所以约束其格式:控制器类名首字母小写除去 Controller 加上方法名 .比如:employee:list
数据录入
如何把所有控制器中的每个处理方法转换成数据库中权限表达式的数据
- 手动: 数据太多,首先排除
- 自动:
- 可以自定义注解,在控制器的处理方法贴上该注解,并在注解上设置权限名称与权限表达式
- 通过第三方程序扫描贴了注解的方法,从而获取注解里面的数据
- 贴了注解的方法,还可以进行权限限制,贴了就代表要限制,没有就是所有人都可以访问
权限加载流程图
后台实现
利用mybatis映射工具生成Permission实体类,mapper,mapper.xml等文件
自定义相应方法
- 实体类Permission
@Data
public class Permission {
private Long id;
private String name;
private String expression;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Permission that = (Permission) o;
return expression.equals(that.expression);
}
@Override
public int hashCode() {
return expression.hashCode();
}
}
- 实体类JSONResult
@Data
public class JSONResult {
private boolean success;
private String msg;
public JSONResult(boolean success, String msg) {
this.success=success;
this.msg=msg;}}
- 自定义注解RequirePermission
贴在控制器对应方法的头上
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String name();
String expression();}
- PermissionMapper
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);
//自定义方法
//批量插入
void batchInsert(@Param("permissions") Set<Permission> permissions);
//角色权限表关系
List<Permission> selectRoleById(Long roleId);
//查询员工对应的权限
List<String> selectExpressionByEmployeeId(Long employeeId);
}
- 权限实现类PermissionServiceImpl
@Service
public class PermissionServiceImpl implements IPermissionService {
@Autowired
private PermissionMapper permissionMapper;
@Override
@Transactional
public void insert(Permission permission) {
permissionMapper.insert(permission);
}
@Override
public void delete(Long id) {
permissionMapper.deleteByPrimaryKey(id); }
@Override
@Transactional
public void update(Permission permission) {
permissionMapper.updateByPrimaryKey(permission);}
@Override
public Permission get(Long id) {
Permission permission = permissionMapper.selectByPrimaryKey(id);
return permission;
}
@Override
public List<Permission> selectAll() {
List<Permission> permissions = permissionMapper.selectAll();
return permissions; }
@Override
public List<Permission> selectForList(QueryObject qo) {
List<Permission> permissions = permissionMapper.selectForList(qo);
return permissions;
}
@Override
public PageInfo<Permission> query(QueryObject qo) {
//告诉插件我们是第几页,每页条数
PageHelper.startPage(qo.getCurrentPage(),qo.getPageSize());
List<Permission> permissions = permissionMapper.selectForList(qo);
return new PageInfo<>(permissions);}
//自定义方法
@Override
public void batchSave(Set<Permission> permissions) {
permissionMapper.batchInsert(permissions);
}
@Override
public List<Permission> queryByRoleId(Long roleId) {
return permissionMapper.queryByRoleId(roleId);
}
@Override
public List<String> queryExpressionByEmployeeId(Long employeeId) {
return permissionMapper.selectExpressionByEmployeeId(employeeId);
}
}
- PermissionMapper.xml
//批量添加的sql
<insert id="batchInsert">
insert into permission(name, expression)
VALUES
<foreach collection="permissions" item="permission" separator=",">
(#{permission.name}, #{permission.expression})
</foreach>
</insert>
- 权限控制器PermissionController
@Controller
@RequestMapping("/permission")
public class PermissionController {
@Autowired
private ApplicationContext ctx;
@Autowired
private IPermissionService permissionService;
//处理分页查询页面请求
@RequestMapping("/list")
public String list(QueryObject qo, Model model) {
PageInfo<Permission> pageInfo = permissionService.query(qo);
model.addAttribute("pageInfo", pageInfo);
return "permission/list";
}
//响应JSON格式数据 {"success":true,"msg":"加载成功"}
@RequestMapping("/load")
@ResponseBody
public JSONResult load() {
try {
//查询数据库中所有的权限
List<Permission> permissionAll = permissionService.selectAll();
//定义一个存权限Set集合
Set<Permission> permissions = new LinkedHashSet<>();
//如何知道所有控制器的处理方法,转成数据库权限表中的一条条数据
Map<String, Object> map = ctx.getBeansWithAnnotation(Controller.class);
//获取所有控制器对象
Collection<Object> controllers = map.values();
for (Object controller : controllers) {
//获取控制器对象本类中的方法
Method[] methods = controller.getClass().getDeclaredMethods();
for (Method method : methods) {
//获取方法的注解
RequirePermission annotation = method.getAnnotation(RequirePermission.class);
if (annotation != null) {
Permission permission = new Permission();
permission.setName(annotation.name());
permission.setExpression(annotation.expression());
//存到set之前判断一下,看这个权限是否在数据库中已存在,若不存在才存到set,之后插入
if (!permissionAll.contains(permission)) {
permissions.add(permission);
}
}
}
}
if (permissions.size() > 0) {
//循环把所有要保存的权限数据封装好,存到set集合中
System.out.println(permissions.size());
permissionService.batchSave(permissions);
}
return new JSONResult(true, "加载成功");
} catch (Exception e) {
e.printStackTrace();
return new JSONResult(false, "加载失败");
}
}
}
前端实现
使用SweetAlert2,替代 JavaScript 的弹出框
引用插件
<link rel="stylesheet" href="/js/plugins/sweetalert2/sweetalert2.min.css">
<script src="/js/plugins/sweetalert2/sweetalert2.min.js"></script>
使用插件,看官方文档实例,拷贝修改即可,样式
Swal.fire({
title: 'Are you sure?',
text: "You won't be able to revert this!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'No, cancel!'
}).then((result) => {
if(result.value) {
// 点了确定做什么,由开发者决定
}
});
使用样式: