权限:权利(能做的)和限制(不能做的),在权限范围内做好自己的事情
认证:验证用户名密码是否正确的过程
授权:对用户所能访问的资源进行控制(动态显示菜单、URL 级别的权限控制)
首先系统需要进行登陆才能访问
其次不同登陆用户要有不同的权利,而且要有不同的菜单(例如财务经理针对系统中财务相关模块进行操作,人事经理针对系统中人事模块进行操作)
ACL(Access Control Lists)
ACL 是最早也是最基本的一种访问控制机制,它的原理非常简单:每一项资源,都配有一个列表,这个列表记录的就是哪些用户可以对这项资源执行 CRUD 中的哪些操作。当系统试图访问这项资源时,会首先检查这个列表中是否有关于当前用户的访问权限,从而确定当前用户可否执行相应的操作。总得来说,ACL 是一种面向资源的访问控制模型,它的机制是围绕“资源”展开的。
RBAC(Role-Based Access Control)
RBAC 是基于角色的访问控制,通过用户的角色来确定用户能否针对某项资源进行某项操作。RBAC 相对于 ACL 最大的优势就是它简化了用户与权限的管理,通过对用户进行分类,使得角色与权限关联起来,而用户与权限变成了间接关联。RBAC 模型使得访问控制,特别是对用户的授权管理变得非常简单和易于维护,因此有广泛的应用。
规则一:每个登陆的用户,可以有多个角色
规则二:每个角色又可以拥有多个权限(包含菜单和资源)
ACL:用户 -> 权限
RBAC:用户 -> 角色 -> 权限
用户表、角色表、权限表、用户角色中间表、角色权限中间表
权限模块主要细分为角色模块、菜单模块、资源模块,将针对细分的三个模块进行具体功能实现,同时会完成用户登陆、用户关联角色及动态菜单显示
实现以下功能
user
用户表roles
角色表menu
菜单表resource
资源表resource_category
资源分类表user_role_relation
用户角色中间表role_menu_relation
角色菜单中间表role_resource_relation
角色资源中间表多个用户对多个角色
权限表由菜单表和资源表组成;菜单表和资源表对权限表进行了细粒度划分
多个角色对多个权限表;用户表和权限表没有直接关联,是通过角色表进行了间接关联
一个资源分类表对多个资源表
角色表
CREATE TABLE `roles` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '角色id',
`code` VARCHAR(100) NOT NULL COMMENT '角色code',
`name` VARCHAR(200) DEFAULT NULL COMMENT '角色名称',
`description` VARCHAR(500) DEFAULT NULL COMMENT '简介',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` VARCHAR(100) NOT NULL COMMENT '创建人',
`updated_by` VARCHAR(100) NOT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
菜单表
CREATE TABLE `menu` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`parent_id` INT(11) NOT NULL COMMENT '父菜单id',
`href` VARCHAR(200) DEFAULT NULL COMMENT '菜单路径',
`icon` VARCHAR(200) DEFAULT NULL COMMENT '菜单图标',
`name` VARCHAR(200) DEFAULT NULL COMMENT '菜单名称',
`description` VARCHAR(500) DEFAULT NULL COMMENT '描述',
`order_num` INT(11) DEFAULT NULL COMMENT '排序号',
`shown` TINYINT(2) DEFAULT '1' COMMENT '是否展示',
`level` INT(11) NOT NULL COMMENT '菜单层级,从0开始',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` VARCHAR(100) NOT NULL COMMENT '创建人',
`updated_by` VARCHAR(100) NOT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COMMENT='菜单表';
资源表
CREATE TABLE `resource` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '资源id',
`name` VARCHAR(200) NOT NULL COMMENT '资源名称',
`url` VARCHAR(200) NOT NULL COMMENT '资源url',
`category_id` INT(11) DEFAULT NULL COMMENT '分类id',
`description` VARCHAR(500) DEFAULT NULL COMMENT '简介',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` VARCHAR(100) NOT NULL COMMENT '创建人',
`updated_by` VARCHAR(100) NOT NULL COMMENT '更新人',
PRIMARY KEY (`id`),
KEY `idx_category_id` (`category_id`)
) ENGINE=INNODB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 COMMENT='资源表';
资源分类表
CREATE TABLE `resource_category` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(200) DEFAULT NULL COMMENT '分类名称',
`sort` INT(4) DEFAULT NULL COMMENT '排序',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` VARCHAR(100) NOT NULL COMMENT '创建人',
`updated_by` VARCHAR(100) NOT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COMMENT='资源分类表';
用户角色中间表
CREATE TABLE `user_role_relation` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '关系id',
`user_id` INT(11) NOT NULL COMMENT '用户id',
`role_id` INT(11) NOT NULL COMMENT '角色id',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` VARCHAR(100) NOT NULL COMMENT '创建人',
`updated_by` VARCHAR(100) NOT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COMMENT='用户和角色关系表';
角色菜单中间表
CREATE TABLE `role_menu_relation` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`menu_id` INT(11) NOT NULL COMMENT '菜单id',
`role_id` INT(11) NOT NULL COMMENT '角色id',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` VARCHAR(100) NOT NULL COMMENT '创建人',
`updated_by` VARCHAR(100) NOT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=302 DEFAULT CHARSET=utf8mb4 COMMENT='角色和菜单关系表';
角色资源中间表
CREATE TABLE `role_resource_relation` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '关系id',
`resource_id` INT(11) NOT NULL COMMENT '资源id',
`role_id` INT(11) NOT NULL COMMENT '角色id',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` VARCHAR(100) NOT NULL COMMENT '创建人',
`updated_by` VARCHAR(100) NOT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8mb4 COMMENT='角色和资源关系表';
点击角色列表按钮进行角色列表展示
public class Role {
private Integer id;
private String code;
private String name;
private String description;
private Date createdTime;
private Date updatedTime;
private String createdBy;
private String updatedBy;
// getter setter toString ...
}
public interface RoleMapper {
List<Role> findAllRole(Role role);
}
<?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="com.renda.dao.RoleMapper">
<select id="findAllRole" parameterType="Role" resultType="Role">
select * from roles
<where>
<if test="name != null and name != '' ">
and name = #{name}
</if>
</where>
</select>
</mapper>
public interface RoleService {
List<Role> findAllRole(Role role);
}
@Service
public class RoleServiceImpl implements RoleService {
@Autowired
private RoleMapper roleMapper;
@Override
public List<Role> findAllRole(Role role) {
return roleMapper.findAllRole(role);
}
}
@RestController
@RequestMapping("/role")
public class RoleController {
@Autowired
private RoleService roleService;
@RequestMapping("/findAllRole")
public ResponseResult findAllRole(@RequestBody Role role) {
List<Role> roleList = roleService.findAllRole(role);
return new ResponseResult(true, 200, "查询所有角色成功", roleList);
}
}
新建:点击提交按钮,将页面内容保存到数据库
修改:点击编辑按钮,由前端实现数据回显,在回显页面进行数据修改,将修改后值更新到数据库中
void saveRole(Role role);
void updateRole(Role role);
<!-- 新增角色 -->
<insert id="saveRole" parameterType="Role">
insert into roles (`name`, `code`, `description`, `created_time`, `updated_time`, `created_by`, `updated_by`)
values (#{name}, #{code}, #{description}, #{createdTime}, #{updatedTime}, #{createdBy}, #{updatedBy})
</insert>
<!-- 更新角色 -->
<update id="updateRole" parameterType="Role">
update roles
<trim prefix="set" suffixOverrides=",">
<if test="name != null and name != ''">
`name` = #{name},
</if>
<if test="code != null and code != ''">
`code` = #{code},
</if>
<if test="description != null and description != ''">
`description` = #{description},
</if>
<if test=" updatedTime != null">
`updated_time` = #{updatedTime},
</if>
<if test="updatedBy != null and updatedBy != ''">
`updated_by` = #{updatedBy}
</if>
</trim>
<where>
<if test="id != null and id != ''">
`id` = #{id}
</if>
</where>
</update>
void saveRole(Role role);
void updateRole(Role role);
@Override
public void saveRole(Role role) {
// 封装数据
Date date = new Date();
role.setCreatedTime(date);
role.setUpdatedTime(date);
if (role.getCreatedBy() == null || "".equals(role.getCreatedBy())) {
role.setCreatedBy("system");
role.setUpdatedBy("system");
}
// 调用 mapper 方法
roleMapper.saveRole(role);
}
@Override
public void updateRole(Role role) {
// 封装数据
role.setUpdatedTime(new Date());
if (role.getUpdatedBy() == null || "".equals(role.getUpdatedBy())) {
role.setUpdatedBy("system");
}
// 调用 mapper 方法
roleMapper.updateRole(role);
}
@RequestMapping("/saveOrUpdateRole")
public ResponseResult saveOrUpdateRole(@RequestBody Role role) {
if (role.getId() == null) {
// 添加
roleService.saveRole(role);
return new ResponseResult(true, 200, "添加角色成功", null);
} else {
// 更新
roleService.updateRole(role);
return new ResponseResult(true, 200, "更新角色成功", null);
}
}
点击分配菜单,回显可选择菜单信息,并回显选中状态;点击保存按钮为角色保存所选择的菜单信息
public class Menu {
// 主键 id
private Integer id;
// 父菜单 id
private int parentId;
// 菜单路径
private String href;
// 菜单图标
private String icon;
// 菜单名称
private String name;
// 描述
private String description;
// 排序号
private int orderNum;
// 是否展示
private int shown;
// 菜单层级,从 0 开始
private int level;
// 创建时间
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createdTime;
// 更新时间
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updatedTime;
// 创建人
private String createdBy;
// 更新人
private String updatedBy;
// 声明集合:当前父级菜单所关联的子级菜单
private List<Menu> subMenuList;
// getter setter toString ...
}
public interface MenuMapper {
List<Menu> findSubMenuListByPid(int pid);
}
<?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="com.renda.dao.MenuMapper">
<resultMap id="menuResult" type="Menu">
<id column="id" property="id"/>
<result column="href" property="href"/>
<result column="icon" property="icon"/>
<result column="name" property="name"/>
<result column="parent_id" property="parentId"/>
<result column="description" property="description"/>
<result column="order_num" property="orderNum"/>
<result column="shown" property="shown"/>
<result column="level" property="level"/>
<result column="created_time" property="createdTime"/>
<result column="updated_time" property="updatedTime"/>
<result column="created_by" property="createdBy"/>
<result column="updated_by" property="updatedBy"/>
<collection property="subMenuList" ofType="Menu" select="findSubMenuListByPid" column="id"/>
</resultMap>
<select id="findSubMenuListByPid" parameterType="int" resultMap="menuResult">
select * from menu where parent_id = #{id}
</select>
</mapper>
public interface MenuService {
List<Menu> findSubMenuListByPid(int pid);
}
@Service
public class MenuServiceImpl implements MenuService {
@Autowired
private MenuMapper menuMapper;
@Override
public List<Menu> findSubMenuListByPid(int pid) {
return menuMapper.findSubMenuListByPid(pid);
}
}
@RequestMapping("/findAllMenu")
public ResponseResult findAllMenu() {
// -1 表示查询所有的父级菜单以及关联的子级菜单
List<Menu> menuList = menuService.findSubMenuListByPid(-1);
// 响应数据
HashMap<String, Object> map = new HashMap<>();
map.put("parentMenuList", menuList);
return new ResponseResult(true, 200, "查询所有的父子菜单信息成功", map);
}
List<Integer> findMenuByRoleId(Integer roleId);
<select id="findMenuByRoleId" resultType="int" parameterType="int">
SELECT
m.`id`
FROM roles r, role_menu_relation rm, menu m
WHERE
r.`id` = rm.`role_id` AND
rm.`menu_id` = m.`id` AND
r.`id` = #{roleId};
</select>
List<Integer> findMenuByRoleId(Integer roleId);
@Override
public List<Integer> findMenuByRoleId(Integer roleId) {
return roleMapper.findMenuByRoleId(roleId);
}
@RequestMapping("/findMenuByRoleId")
public ResponseResult findMenuByRoleId(Integer roleId) {
List<Integer> roleIdlist = roleService.findMenuByRoleId(roleId);
return new ResponseResult(true, 200, "查询角色关联的菜单信息成功", roleIdlist);
}
public class Role_menu_relation {
private Integer id;
private Integer menuId;
private Integer roleId;
private Date createdTime;
private Date updatedTime;
private String createdBy;
private String updatedby;
// getter setter toString ...
}
View Object 表现层对象,主要用于表现层来接收参数的
public class RoleMenuVo {
private Integer roleId;
private List<Integer> menuIdList;
// getter setter toString ...
}
void deleteRoleContextMenu(Integer roleId);
void roleContextMenu(RoleMenuRelation roleMenuRelation);
<!-- 根据 roleid 删除在中间表与菜单的关联关系 -->
<delete id="deleteRoleContextMenu" parameterType="int">
delete from role_menu_relation where role_id = #{roleId}
</delete>
<!-- 为角色分配菜单 -->
<insert id="roleContextMenu" parameterType="RoleMenuRelation">
insert into role_menu_relation values (null,#{menuId},#{roleId},#{createdTime},#{updatedTime},#{createdBy},#{updatedby})
</insert>
void roleContextMenu(RoleMenuVo roleMenuVo);
@Override
public void roleContextMenu(RoleMenuVo roleMenuVo) {
// 清空中间表的关联关系
roleMapper.deleteRoleContextMenu(roleMenuVo.getRoleId());
// 为角色分配菜单
for (Integer mId : roleMenuVo.getMenuIdList()) {
// 新建中间表数据
RoleMenuRelation roleMenuRelation = new RoleMenuRelation();
roleMenuRelation.setMenuId(mId);
roleMenuRelation.setRoleId(roleMenuVo.getRoleId());
// 封装数据
Date date = new Date();
roleMenuRelation.setCreatedTime(date);
roleMenuRelation.setUpdatedTime(date);
roleMenuRelation.setCreatedBy("system");
roleMenuRelation.setUpdatedby("system");
roleMapper.roleContextMenu(roleMenuRelation);
}
}
@RequestMapping("/RoleContextMenu")
public ResponseResult roleContextMenu(@RequestBody RoleMenuVo roleMenuVo) {
roleService.roleContextMenu(roleMenuVo);
return new ResponseResult(true, 200, "为角色分配菜单成功", null);
}
点击删除按钮,将选中的角色信息删除
void deleteRole(Integer roleId);
<delete id="deleteRole" parameterType="int">
delete from roles where id = #{roleId}
</delete>
void deleteRole(Integer roleId);
@Override
public void deleteRole(Integer roleId) {
// 清空中间表的关联关系
roleMapper.deleteRoleContextMenu(roleId);
// 删除角色
roleMapper.deleteRole(roleId);
}
@RequestMapping("/deleteRole")
public ResponseResult deleteRole(Integer id) {
roleService.deleteRole(id);
return new ResponseResult(true, 200, "删除角色成功", null);
}
点击菜单列表,对菜单信息进行列表展示
List<Menu> findAllMenu();
<select id="findAllMenu" resultType="Menu">
select * from menu
</select>
List<Menu> findAllMenu();
@Override
public List<Menu> findAllMenu() {
return menuMapper.findAllMenu();
}
@RequestMapping("/findAllMenu")
public ResponseResult findAllMenu() {
List<Menu> menuList = menuService.findAllMenu();
return new ResponseResult(true, 200, "查询所有菜单信息成功", menuList);
}
点击添加菜单按钮,跳转到添加菜单页面,回显当前添加菜单可以选择的上级菜单信息
Menu findMenuById(Integer id);
<select id="findMenuById" parameterType="int" resultType="Menu">
select * from menu where id = #{id}
</select>
Menu findMenuById(Integer id);
@Override
public Menu findMenuById(Integer id) {
return menuMapper.findMenuById(id);
}
@RequestMapping("/findMenuInfoById")
public ResponseResult findMenuInfoById(Integer id) {
// 根据 id 的值是否为 -1 判断当前是更新还是添加操作
if (id == -1) {
// 添加操作,回显信息中只需要查询所有父级菜单信息
List<Menu> parentMenuList = menuService.findSubMenuListByPid(-1);
// 封装数据
HashMap<String, Object> map = new HashMap<>();
map.put("menuInfo", null);
map.put("parentMenuList", parentMenuList);
return new ResponseResult(true, 200, "添加回显成功", map);
} else {
// 修改操作,回显信息需要包含所有父级菜单信息和菜单本身的信息
List<Menu> parentMenuList = menuService.findSubMenuListByPid(-1);
Menu menuInfo = menuService.findMenuById(id);
// 封装数据
HashMap<String, Object> map = new HashMap<>();
map.put("menuInfo", menuInfo);
map.put("parentMenuList", parentMenuList);
return new ResponseResult(true, 200, "修改回显成功", map);
}
}
添加:点击提交按钮,将数据提交到数据库
修改:页面回显基础上,点击提交按钮真正进行数据修改
void saveMenu(Menu menu);
void updateMenu(Menu menu);
<!-- 新增菜单 -->
<insert id="saveMenu" parameterType="Menu">
insert into menu (`parent_id`,`href`,`icon`,`name`,`description`,`order_num`,`shown`,`level`,
`created_time`,`updated_time`,`created_by`,`updated_by`)
values (#{parentId},#{href},#{icon},#{name},#{description},#{orderNum},#{shown},#{level},
#{createdTime},#{updatedTime},#{createdBy},#{updatedBy})
</insert>
<!-- 更新菜单 -->
<update id="updateMenu" parameterType="Menu">
update menu
<trim prefix="set" suffixOverrides=",">
<if test="`parentId` != null and `parentId` != ''">
`parent_id` = #{parentId},
</if>
<if test="href != null and href != ''">
`href` = #{href},
</if>
<if test="icon != null and icon != ''">
`icon` = #{icon},
</if>
<if test="name != null and name != ''">
`name` = #{name},
</if>
<if test="description != null and description != ''">
`description` = #{description},
</if>
<if test="`orderNum` != null and `orderNum` != ''">
`order_num` = #{orderNum},
</if>
<if test="`shown` != null and `shown` != ''">
`shown` = #{shown},
</if>
<if test="`level` != null and `level` != ''">
`level` = #{level},
</if>
<if test="createdTime != null">
`created_time` = #{createdTime},
</if>
<if test="updatedTime != null">
`updated_time` = #{updatedTime},
</if>
<if test="createdBy != null and createdBy != ''">
`created_by` = #{createdBy},
</if>
<if test="updatedBy != null and updatedBy != ''">
`updated_by` = #{updatedBy},
</if>
</trim>
<where>
<if test="id != null and id != ''">
`id` = #{id}
</if>
</where>
</update>
void saveMenu(Menu menu);
void updateMenu(Menu menu);
@Override
public void saveMenu(Menu menu) {
// 封装数据
Date date = new Date();
menu.setCreatedTime(date);
menu.setUpdatedTime(date);
if (menu.getCreatedBy() == null || "".equals(menu.getCreatedBy())) {
menu.setCreatedBy("system");
menu.setUpdatedBy("system");
}
// 调用 mapper 方法
menuMapper.saveMenu(menu);
}
@Override
public void updateMenu(Menu menu) {
// 封装数据
menu.setUpdatedTime(new Date());
if (menu.getUpdatedBy() == null || "".equals(menu.getUpdatedBy())) {
menu.setUpdatedBy("system");
}
// 调用 mapper 方法
menuMapper.updateMenu(menu);
}
@RequestMapping("/saveOrUpdateMenu")
public ResponseResult saveOrUpdateMenu(@RequestBody Menu menu) {
if (menu.getId() == null) {
// 新增
menuService.saveMenu(menu);
return new ResponseResult(true, 200, "添加菜单成功", null);
} else {
// 修改
menuService.updateMenu(menu);
return new ResponseResult(true, 200, "修改菜单成功", null);
}
}
查询资源分类信息列表
public class ResourceCategory {
private Integer id;
private String name;
private Integer sort;
private Date createdTime;
private Date updatedTime;
private String createdBy;
private String updatedBy;
// setter getter toString ...
}
public interface ResourceCategoryMapper {
List<ResourceCategory> findAllResourceCategory();
}
<?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="com.renda.dao.ResourceCategoryMapper">
<select id="findAllResourceCategory" resultType="ResourceCategory">
select * from resource_category
</select>
</mapper>
public interface ResourceCategoryService {
List<ResourceCategory> findAllResourceCategory();
}
@Service
public class ResourceCategoryServiceImpl implements ResourceCategoryService {
@Autowired
private ResourceCategoryMapper resourceCategoryMapper;
@Override
public List<ResourceCategory> findAllResourceCategory() {
return resourceCategoryMapper.findAllResourceCategory();
}
}
@RestController
@RequestMapping("/ResourceCategory")
public class ResourceCategoryController {
@Autowired
private ResourceCategoryService resourceCategoryService;
@RequestMapping("/findAllResourceCategory")
public ResponseResult findAllResourceCategory() {
List<ResourceCategory> allResourceCategory = resourceCategoryService.findAllResourceCategory();
return new ResponseResult(true, 200, "查询所有分类信息成功", allResourceCategory);
}
}
资源列表及多条件组合查询
public class Resource {
private Integer id;
private String name;
private String url;
private Integer categoryId;
private String description;
private Date createdTime;
private Date updatedTime;
private String createdBy;
private String updatedBy;
// setter getter toString ...
}
View Object 表现层对象,主要用于表现层来接收参数的
public class ResourseVo {
private Integer currentPage;
private Integer pageSize;
private String name;
private Integer categoryId;
private String url;
// setter getter toString ...
}
public interface ResourceMapper {
List<Resource> findAllResourceByPage(ResourseVo resourseVo);
}
<?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="com.renda.dao.ResourceMapper">
<select id="findAllResourceByPage" parameterType="ResourseVo" resultType="Resource">
select * from resource
<where>
<if test="name != null and name != ''">
and `name` like concat('%', #{name}, '%')
</if>
<if test="url != null and url != ''">
and `url` = #{url}
</if>
<if test="categoryId != null and categoryId != ''">
and `category_id` = #{categoryId}
</if>
</where>
</select>
</mapper>
public interface ResourceService {
PageInfo<Resource> findAllResourceByPage(ResourseVo resourseVo);
}
@Service
public class ResourceServiceImpl implements ResourceService {
@Autowired
private ResourceMapper resourceMapper;
@Override
public PageInfo<Resource> findAllResourceByPage(ResourseVo resourseVo) {
// 分页查询
PageHelper.startPage(resourseVo.getCurrentPage(), resourseVo.getPageSize());
List<Resource> resourceList = resourceMapper.findAllResourceByPage(resourseVo);
return new PageInfo<>(resourceList);
}
}
@RestController
@RequestMapping("/resource")
public class ResourceController {
@Autowired
private ResourceService resourceService;
@RequestMapping("/findAllResource")
public ResponseResult findAllResource(@RequestBody ResourseVo resourseVo) {
PageInfo<Resource> resourceByPage = resourceService.findAllResourceByPage(resourseVo);
return new ResponseResult(true, 200, "资源信息分页多条件查询成功", resourceByPage);
}
}
新建:点击提交按钮,将页面内容保存到数据库
修改:点击编辑按钮,由前端实现数据回显,在回显页面进行数据修改,将修改后值更新到数据库中
void saveResource(Resource resource);
void updateResource(Resource resource);
<!-- 添加资源信息 -->
<insert id="saveResource" parameterType="Resource">
insert into resource values (null,#{name},#{url},#{categoryId},#{description},#{createdTime},
#{updatedTime},#{createdBy},#{updatedBy});
</insert>
<!-- 更新资源信息 -->
<update id="updateResource" parameterType="Resource">
update resource
<trim prefix="set" suffixOverrides=",">
<if test="name != null and name != ''">
`name` = #{name},
</if>
<if test="url != null and url != ''">
`url` = #{url},
</if>
<if test="categoryId != null and categoryId != ''">
`category_id` = #{categoryId},
</if>
<if test="description != null and description != ''">
`description` = #{description},
</if>
<if test="createdTime != null">
`created_time` = #{createdTime},
</if>
<if test="updatedTime != null">
`updated_time` = #{updatedTime},
</if>
<if test="createdBy != null and createdBy != ''">
`created_by` = #{createdBy},
</if>
<if test="updatedBy != null and updatedBy != ''">
`updated_by` = #{updatedBy},
</if>
</trim>
<where>
<if test="id != null and id != ''">
`id` = #{id}
</if>
</where>
</update>
void saveResource(Resource resource);
void updateResource(Resource resource);
@Override
public void saveResource(Resource resource) {
// 封装数据
Date date = new Date();
resource.setCreatedTime(date);
resource.setUpdatedTime(date);
if (resource.getCreatedBy() == null || "".equals(resource.getCreatedBy())) {
resource.setCreatedBy("system");
resource.setUpdatedBy("system");
}
// 调用 mapper 方法
resourceMapper.saveResource(resource);
}
@Override
public void updateResource(Resource resource) {
// 封装数据
resource.setUpdatedTime(new Date());
if (resource.getUpdatedBy() == null || "".equals(resource.getUpdatedBy())) {
resource.setUpdatedBy("system");
}
// 调用 mapper 方法
resourceMapper.updateResource(resource);
}
@RequestMapping("/saveOrUpdateResource")
public ResponseResult saveOrUpdateResource(@RequestBody Resource resource) {
if (resource.getId() == null) {
// 添加
resourceService.saveResource(resource);
return new ResponseResult(true, 200, "添加资源信息成功", null);
} else {
// 更新
resourceService.updateResource(resource);
return new ResponseResult(true, 200, "更新资源信息成功", null);
}
}
点击删除按钮,将对应的资源信息删除
void deleteResource(Integer resourceId);
<delete id="deleteResource" parameterType="int">
delete from resource where id = #{resourceId}
</delete>
void deleteResource(Integer resourceId);
@Override
public void deleteResource(Integer resourceId) {
// 清空中间表的关联关系
resourceMapper.deleteRoleResourceRelation(resourceId);
// 删除资源
resourceMapper.deleteResource(resourceId);
}
@RequestMapping("/deleteResource")
public ResponseResult deleteResource(Integer id) {
resourceService.deleteResource(id);
return new ResponseResult(true, 200, "删除资源成功", null);
}
MD5 加密全程是 Message-Digest Algorithm 5(信息 - 摘要算法),它对信息进行摘要采集,再通过一定的位运算,最终获取加密后的 MD5 字符串。
MD5 加密的特点主要有以下几点:
针对不同长度待加密的数据、字符串等等,其都可以返回一个固定长度的 MD5 加密字符串。(通常 32 位的 16 进制字符串)
其加密过程几乎不可逆,除非维护一个庞大的 Key - Value 数据库来进行碰撞破解,否则几乎无法解开。
运算简便,且可实现方式多样,通过一定的处理方式也可以避免碰撞算法的破解(加盐:随机字符串)。
对于一个固定的字符串。数字等等,MD5 加密后的字符串是固定的,也就是说不管 MD5 加密多少次,都是同样的结果。
输入用户名密码,点击登陆按钮,进行用户登陆
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.3</version>
</dependency>
public class Md5 {
public final static String md5key = "renda";
/**
* MD5 方法
* @param text 明文
* @param key 密钥
* @return 密文
*/
public static String md5(String text, String key) {
// 加密后的字符串
String encodedStr = DigestUtils.md5Hex(text + key);
System.out.println("MD5 加密后的字符串为:encodedStr = " + encodedStr);
return encodedStr;
}
/**
* MD5 验证方法
* @param text 明文
* @param key 密钥
* @param md5 密文
* @return true/false
*/
public static boolean verify(String text, String key, String md5) {
// 根据传入的密钥进行验证
String md5Text = md5(text, key);
if (md5Text.equalsIgnoreCase(md5)) {
System.out.println("MD5 验证通过");
return true;
}
return false;
}
public static void main(String[] args) throws Exception {
// 添加用户的时候,要将明文密码转换成密文密码
System.out.println(Md5.md5("123456", "renda"));
// 调用 verify 方法进行密码的校验
System.out.println(Md5.verify("123456", "renda", "58c40a69ae7e7c92bced90ed1e974624"));
}
}
User login(User user);
<select id="login" parameterType="User" resultType="User">
select * from `user` where `phone` = #{phone}
</select>
User login(User user);
@Override
public User login(User user) {
// 调用 mapper 方法,传递 user 对象
User userFromDatabase = userMapper.login(user);
if (userFromDatabase != null && Md5.verify(user.getPassword(), "renda", userFromDatabase.getPassword())) {
return userFromDatabase;
} else {
return null;
}
}
@RequestMapping("/login")
public ResponseResult login(User user, HttpServletRequest request) {
User userFromDatebase = userService.login(user);
if (userFromDatebase != null) {
// 保存用户 id 及 access_token 到 session 中
HttpSession requestSession = request.getSession();
String accessToken = UUID.randomUUID().toString();
System.out.println(accessToken);
requestSession.setAttribute("access_token", accessToken);
requestSession.setAttribute("user_id", userFromDatebase.getId());
// 将查询出来的信息响应给前台
HashMap<String, Object> map = new HashMap<>();
map.put("access_token", accessToken);
map.put("user_id", userFromDatebase.getId());
return new ResponseResult(true, 200, "登陆成功", map);
} else {
return new ResponseResult(true, 400, "用户名密码错误", null);
}
}
点击分配角色,将该用户所具有的角色信息进行回显
List<Role> findUserRelationRoleById(Integer id);
<select id="findUserRelationRoleById" parameterType="int" resultType="Role">
SELECT * FROM
roles r INNER JOIN user_role_relation ur ON r.`id` = ur.`role_id`
WHERE ur.`user_id` = #{id};
</select>
List<Role> findUserRelationRoleById(Integer id);
@Override
public List<Role> findUserRelationRoleById(Integer id) {
return userMapper.findUserRelationRoleById(id);
}
@RequestMapping("/findUserRoleById")
public ResponseResult findUserRelationRoleById(Integer id) {
List<Role> roleList = userService.findUserRelationRoleById(id);
return new ResponseResult(true, 200, "分配角色回显成功", roleList);
}
点击确定按钮,真正实现用户角色关联
public class UserVo {
private List<Integer> roleIdList;
private Integer userId;
...
// getter setter ...
}
void deleteUserContextRole(Integer userId);
void userContextRole(UserRoleRelation userRoleRelation);
<!-- 根据用户 ID 清空中间表 -->
<delete id="deleteUserContextRole" parameterType="int">
delete from user_role_relation where user_id = #{userId}
</delete>
<!-- 分配角色:用户关联角色 -->
<insert id="userContextRole" parameterType="UserRoleRelation">
insert into user_role_relation values (null,#{userId},#{roleId},#{createdTime},#{updatedTime},#{createdBy},#{updatedBy})
</insert>
void userContextRole(UserVo userVo);
@Override
public void userContextRole(UserVo userVo) {
// 根据用户 ID 清空中间表关联关系
userMapper.deleteUserContextRole(userVo.getUserId());
// 建立关联关系
for (Integer roleId : userVo.getRoleIdList()) {
// 新建 UserRoleRelation
UserRoleRelation userRoleRelation = new UserRoleRelation();
userRoleRelation.setUserId(userVo.getUserId());
userRoleRelation.setRoleId(roleId);
// 封装数据
Date date = new Date();
userRoleRelation.setCreatedTime(date);
userRoleRelation.setUpdatedTime(date);
userRoleRelation.setCreatedBy("system");
userRoleRelation.setUpdatedBy("system");
userMapper.userContextRole(userRoleRelation);
}
}
@RequestMapping("/userContextRole")
public ResponseResult userContextRole(@RequestBody UserVo userVo) {
userService.userContextRole(userVo);
return new ResponseResult(true, 200, "分配角色成功", null);
}
登陆成功后,根据用户所拥有的权限信息,进行菜单列表动态展示
List<Role> findUserRelationRoleById(Integer userId);
List<Menu> findParentMenuByRoleId(List<Integer> roleIdList);
List<Menu> findSubMenuByParentId(Integer parentId);
List<Resource> findResourceByRoleId(List<Integer> roleIdList);
<!-- 根据用户 ID 查询关联的角色信息 -->
<select id="findUserRelationRoleById" parameterType="int" resultType="Role">
SELECT * FROM
roles r INNER JOIN user_role_relation ur ON r.`id` = ur.`role_id`
WHERE ur.`user_id` = #{userId};
</select>
<!-- 根据角色 ID,查询角色所拥有的顶级菜单 -->
<select id="findParentMenuByRoleId" parameterType="List" resultType="Menu">
SELECT
DISTINCT m.*
FROM
roles r INNER JOIN role_menu_relation rm ON r.`id` = rm.`role_id`
INNER JOIN menu m ON m.`id` = rm.`menu_id`
WHERE
m.`parent_id` = -1 AND r.`id` IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- 根据 parentId,查询子菜单信息 -->
<select id="findSubMenuByParentId" parameterType="int" resultType="Menu">
SELECT * FROM `menu` WHERE parent_id = #{parentId}
</select>
<!-- 根据角色 ID,获取用户拥有的资源权限信息 -->
<select id="findResourceByRoleId" parameterType="List" resultType="Resource">
SELECT DISTINCT r.*
FROM
resource r INNER JOIN role_resource_relation rr ON r.`id` = rr.`resource_id`
INNER JOIN roles ro ON ro.`id` = rr.`role_id`
WHERE ro.`id` IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
ResponseResult getUserPermissions(Integer userId);
@Override
public ResponseResult getUserPermissions(Integer userId) {
// 获取当前用户拥有的角色
List<Role> roleList = userMapper.findUserRelationRoleById(userId);
if (roleList == null || roleList.isEmpty()) {
return new ResponseResult(true, 200, "用户权限信息为空", null);
}
// 获取角色 ID,保存到 List 集合中
ArrayList<Integer> roleIds = new ArrayList<>();
for (Role role : roleList) {
roleIds.add(role.getId());
}
// 根据角色 ID 查询父菜单
List<Menu> parentMenuList = userMapper.findParentMenuByRoleId(roleIds);
// 查询并封装父菜单关联的子菜单
for (Menu menu : parentMenuList) {
menu.setSubMenuList(userMapper.findSubMenuByParentId(menu.getId()));
}
// 获取资源信息
List<Resource> resourceList = userMapper.findResourceByRoleId(roleIds);
// 封装数据并返回
HashMap<String, Object> map = new HashMap<>();
map.put("menuList", parentMenuList);
map.put("resourceList", resourceList);
return new ResponseResult(true, 200, "获取用户权限信息成功", map);
}
@RequestMapping("/getUserPermissions")
public ResponseResult getUserPermissions(HttpServletRequest request) {
// 获取请求头中的 token
String accessTokenFromHeader = request.getHeader("Authorization");
// 获取 session 中 token
String accessToken = (String) request.getSession().getAttribute("access_token");
// 判断 accessToken 是否一致
if (accessTokenFromHeader.equals(accessToken)) {
// 获取用户 id
Integer userId = (Integer) request.getSession().getAttribute("user_id");
// 调用 service,进行菜单信息查询
return userService.getUserPermissions(userId);
} else {
return new ResponseResult(false, 400, "获取菜单信息失败", null);
}
}
先进行登录接口测试,获取 access_token
;
然后请求的 Headers 中增加一个 key 为 Authorization
,Value 为登录获取的 access_token
的键值对。