需求为文件夹权限设置:管理员可以新建和维护文件夹(目录), 不同用户在拥有的目录的权限可控,权限分为只读、读写和管理。
参考svn文件夹权限后想到两种实现方式:1.阻断式:A->B->C->D 当配置了D目录的权限为只读或者读写或者管理,那么只有用户访问D目录时才有权限,访问A或B或C都没有任何权限,仅仅在输入A->B->C->D完整目录时才有权限。这种配置起来比较繁琐。2.继承式:A->B->C->D 当配置了D目录的权限为只读或者读写或者管理,那么访问A或B或C目录都有“可读”的权限,即用户能分别访问A、B、C目录下的文件,但是不能写入或者删除。
考虑到继承式的方式配置简单,所以采用此方式。
以下为实现过程:
1.文件夹权限表结构设计
CREATE TABLE `disk_file_folder_privilege` (
`id` varchar(32) NOT NULL COMMENT '主键',
`user_id` varchar(32) DEFAULT NULL COMMENT '用户id',
`role_id` varchar(32) DEFAULT NULL,
`present_catalog` varchar(32) DEFAULT NULL COMMENT '当前文件夹目录',
`whole_catalog` varchar(512) DEFAULT NULL COMMENT '完整文件夹目录',
`readable` char(1) DEFAULT NULL COMMENT '可读:1',
`writable` char(1) DEFAULT NULL COMMENT '可写:1',
`manage` char(1) DEFAULT NULL COMMENT '管理:1',
`create_time` varchar(32) DEFAULT NULL,
`update_time` varchar(32) DEFAULT NULL,
`creater` varchar(32) DEFAULT NULL,
`updater` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='文件夹权限表';
数据格式如下图:
2.保存文件夹权限接口服务
/**
* 保存文件夹目录权限
*
* @param diskFileFolderPrivilege
* @return
*/
@Transactional(rollbackFor = Exception.class)
@Override
public ServiceResult<Boolean> save(DiskFileFolderPrivilegeAO diskFileFolderPrivilege) {
if (StringUtils.isEmpty(diskFileFolderPrivilege.getFileId())) {
return ServiceResultHelper.genResultWithFaild("请选择文件夹目录", -1);
}
DiskFileAO diskFile = diskFileService.selectByPrimaryKey(diskFileFolderPrivilege.getFileId()).getData();
if (diskFile == null) {
return ServiceResultHelper.genResultWithFaild("文件或文件夹目录不存在", -1);
}
if (!StringUtils.isEmpty(diskFile.getTypecode())) {
return ServiceResultHelper.genResultWithFaild("请选择文件夹目录", -1);
}
if (StringUtils.isEmpty(diskFileFolderPrivilege.getReadable())
&& StringUtils.isEmpty(diskFileFolderPrivilege.getWritable())
&& StringUtils.isEmpty(diskFileFolderPrivilege.getManage())) {
return ServiceResultHelper.genResultWithFaild("请设置文件夹目录权限", -1);
}
List<DiskFileAO> fileFloderList = diskFileService.listAllFileFolder().getData();
List<DiskFileAO> parentList = new ArrayList<>();
UserAO user = AuthUtil.getCurrentUser();
List<DiskFileFolderPrivilegeAO> result = new ArrayList<>();
if (!CollectionUtils.isEmpty(fileFloderList)) {
//查询所有父级
diskFileService.getAllParentListWithChild(fileFloderList, diskFileFolderPrivilege.getFileId(), parentList);
if (!CollectionUtils.isEmpty(parentList)) {
for (int i = 0; i < parentList.size(); i++) {
DiskFileAO file = parentList.get(i);
DiskFileFolderPrivilegeAO tmp = new DiskFileFolderPrivilegeAO();
tmp.setUserId(diskFileFolderPrivilege.getUserId());
tmp.setRoleId(diskFileFolderPrivilege.getRoleId());
tmp.setCreater(user.getUserName());
tmp.setCreateTime(DateTimeUtil.formatDateTime(new Date()));
tmp.setPresentCatalog(file.getId());//当前文件夹目录
tmp.setReadable(Constant.VALID_FLG.toString());
//设置所选文件夹的可写,管理权限
if (i == parentList.size() - 1) {
if (Constant.VALID_FLG.toString().equals(diskFileFolderPrivilege.getWritable())) {
tmp.setWritable(diskFileFolderPrivilege.getWritable());
}
if (Constant.VALID_FLG.toString().equals(diskFileFolderPrivilege.getManage())) {
tmp.setWritable(Constant.VALID_FLG.toString());
tmp.setManage(diskFileFolderPrivilege.getManage());
}
}
if (i == 0) {
tmp.setWholeCatalog(file.getId());//完整文件夹目录
} else {
StringBuilder fileId = new StringBuilder();
for (int j = 0; j <= i; j++) {
fileId.append(parentList.get(j).getId());
if (j < i) {
fileId.append("/");
}
}
tmp.setWholeCatalog(fileId.toString());
}
result.add(tmp);
}
}
}
//子级目录不覆盖父级目录的权限,需要比较权限的大小
deleteByFolderCatalog(result);
if (!CollectionUtils.isEmpty(result)) {
insertBatch(result);
}
return ServiceResultHelper.genResultWithSuccess();
}
/**
* 删除已存在的权限,需要比较权限的大小
* 子级目录不覆盖父级目录的权限
*
* @param result
* @return
*/
public ServiceResult deleteByFolderCatalog(List<DiskFileFolderPrivilegeAO> result) {
if (!CollectionUtils.isEmpty(result)) {
Iterator<DiskFileFolderPrivilegeAO> resultIt = result.iterator();
while (resultIt.hasNext()) {
DiskFileFolderPrivilegeAO privilege = resultIt.next();
DiskFileFolderPrivilegeCriteria criteria = new DiskFileFolderPrivilegeCriteria();
DiskFileFolderPrivilegeCriteria.Criteria c = criteria.createCriteria();
String userId = privilege.getUserId();
if (StringUtils.isEmpty(userId)) {
return ServiceResultHelper.genResultWithFaild("用户id不能为空", -1);
}
if (StringUtils.isEmpty(privilege.getWholeCatalog())) {
return ServiceResultHelper.genResultWithFaild("文件夹目录不能为空", -1);
}
if (!StringUtils.isEmpty(userId)) {
c.andUserIdEqualTo(userId);
}
c.andWholeCatalogEqualTo(privilege.getWholeCatalog());
List<DiskFileFolderPrivilegeAO> privilegeList = selectByCriteria(criteria).getData();
if (!CollectionUtils.isEmpty(privilegeList)) {
//如果权限已经存在了,并且权限的范围更大,则不再进行删除操作
boolean isRemove = false;//是否做移除操作
DiskFileFolderPrivilegeAO folderPrivilege = privilegeList.get(0);
//如果是最后一级目录,直接删除
if (!resultIt.hasNext()) {
deleteByCriteria(criteria);
isRemove = true;
} else {
if (!StringUtils.isEmpty(privilege.getManage())
&& StringUtils.isEmpty(folderPrivilege.getManage())) {
deleteByCriteria(criteria);
isRemove = true;
} else if (!StringUtils.isEmpty(privilege.getWritable())
&& StringUtils.isEmpty(folderPrivilege.getWritable())) {
deleteByCriteria(criteria);
isRemove = true;
}
}
if (!isRemove) {
resultIt.remove();
}
}
}
}
return ServiceResultHelper.genResultWithSuccess();
}
@Override
public int insertBatch(List<DiskFileFolderPrivilegeAO> privilegeAOList) {
return diskFileFolderPrivilegeCustomizedMapper.insertBatch(privilegeAOList);
}
mybatis中的xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Description: A generated data access implementation of entity DiskFileFolderPrivilege.
Generated at 2020-07-31 11:23:44, do NOT modify!
Author: <a href="mailto:DL88250@gmail.com">Liang Ding</a>
Version: 1.0.0.0, Jul 31, 2020
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.datainvest.dms.system.dao.customized.DiskFileFolderPrivilegeCustomizedMapper" >
<resultMap id="BaseResultMap" type="cn.datainvest.dms.system.entity.customized.DiskFileFolderPrivilegeAO" >
<id column="id" property="id" jdbcType="VARCHAR" />
<result column="user_id" property="userId" jdbcType="VARCHAR" />
<result column="role_id" property="roleId" jdbcType="VARCHAR" />
<result column="present_catalog" property="presentCatalog" jdbcType="VARCHAR" />
<result column="whole_catalog" property="wholeCatalog" jdbcType="VARCHAR" />
<result column="readable" property="readable" jdbcType="CHAR" />
<result column="writable" property="writable" jdbcType="CHAR" />
<result column="manage" property="manage" jdbcType="CHAR" />
<result column="create_time" property="createTime" jdbcType="VARCHAR" />
<result column="update_time" property="updateTime" jdbcType="VARCHAR" />
<result column="creater" property="creater" jdbcType="VARCHAR" />
<result column="updater" property="updater" jdbcType="VARCHAR" />
</resultMap>
<!-- 批量插入 -->
<insert id="insertBatch" parameterType="java.util.List">
insert into disk_file_folder_privilege (id, user_id, role_id,
present_catalog, whole_catalog, readable, writable,
manage, create_time, update_time,
creater, updater)
values
<foreach collection="list" item="item" index="index" separator=",">
(uuid_short(), #{item.userId,jdbcType=VARCHAR}, #{item.roleId,jdbcType=VARCHAR},
#{item.presentCatalog,jdbcType=VARCHAR}, #{item.wholeCatalog,jdbcType=VARCHAR}, #{item.readable,jdbcType=CHAR}, #{item.writable,jdbcType=CHAR},
#{item.manage,jdbcType=CHAR}, #{item.createTime,jdbcType=VARCHAR}, #{item.updateTime,jdbcType=VARCHAR},
#{item.creater,jdbcType=VARCHAR}, #{item.updater,jdbcType=VARCHAR})
</foreach>
</insert>
</mapper>
pojo,配置:
import java.io.Serializable;
import cn.datainvest.dms.system.entity.gen.DiskFileFolderPrivilege;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
/**
* 应用对象 - DiskFileFolderPrivilege.
* <p>
* 该类于 2020-07-31 10:46:58 首次生成,后由开发手工维护。
* </p>
* @author yangfeng
* @version 1.0.0, Jul 31, 2020
*/
@JsonSerialize(include = Inclusion.ALWAYS)
public final class DiskFileFolderPrivilegeAO extends DiskFileFolderPrivilege implements Serializable {
/**
* 默认的序列化 id.
*/
private static final long serialVersionUID = 1L;
private String fileId;
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}
fileId用来接收配置的文件夹id, readable、 writable、 manage三个字段分别对应可读、可写、管理权限。值为1是表示有相应权限没有则不需要传入值,保持null。
presentCatalog字段表示当前文件夹id,wholeCatalog表示完整目录的文件夹id。为什么不存文件名:考虑到文件夹目录会重命名导致权限失效。
wholeCatalog拆分的各级父目录readable均为1,均有可读权限。删除已存在的权限,需要比较权限的大小 ,子级目录不覆盖父级目录的权限。 拆分目录后如果是最后一级目录(即选择的当前文件夹),直接删除对应的权限,重新设置。否则,对应的其他上级目录,如果权限已经存在了,并且权限的范围更大,则不再进行删除操作,防止缩小父级权限。
3. 文件表:
CREATE TABLE `disk_file` (
`id` varchar(50) NOT NULL,
`createtime` datetime DEFAULT NULL,
`createuserid` varchar(50) DEFAULT NULL,
`createusername` varchar(50) DEFAULT NULL,
`filemd5` varchar(200) DEFAULT NULL,
`filename` varchar(255) DEFAULT NULL,
`filesize` bigint(20) NOT NULL,
`filesuffix` varchar(20) DEFAULT NULL,
`filetype` int(11) DEFAULT NULL,
`imgsize` varchar(50) DEFAULT NULL,
`pid` varchar(50) DEFAULT NULL,
`thumbnailurl` varchar(200) DEFAULT NULL,
`typecode` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UKfjg8ov86e52cc1vw2g4cahpu7` (`createuserid`,`pid`,`filetype`,`filename`),
KEY `IDX7h8ca5qp48qow851woryvg8q` (`pid`),
KEY `IDX9j6hnchh4o79nofxhjgy0lc29` (`filename`),
KEY `IDXlhtq88mi2kvwxihin21rvvm9b` (`typecode`),
KEY `IDXb244b04v1wu3ftqfqjt7wodks` (`filesuffix`),
KEY `IDXq0mhp53s6a40gh5w93bqmx5jp` (`filetype`),
KEY `IDXc7snngp84h66d6f64u0l0hb56` (`createuserid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
type_code为空表示文件夹目录。pid=“0”,表示根目录。查询可读的文件和文件夹,关联文件夹目录权限,超级管理员可以查看所有。普通用户可以查看拥有权限的文件夹,以及创建者是本人的文件和文件夹。
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.datainvest.dms.system.dao.customized.DiskFileCustomizedMapper" >
<resultMap id="CustomResultMap" type="cn.datainvest.dms.system.entity.customized.DiskFileAO" >
<id column="id" property="id" jdbcType="VARCHAR" />
<result column="createtime" property="createtime" jdbcType="TIMESTAMP" />
<result column="createuserid" property="createuserid" jdbcType="VARCHAR" />
<result column="createusername" property="createusername" jdbcType="VARCHAR" />
<result column="filemd5" property="filemd5" jdbcType="VARCHAR" />
<result column="filename" property="filename" jdbcType="VARCHAR" />
<result column="filesize" property="filesize" jdbcType="BIGINT" />
<result column="filesuffix" property="filesuffix" jdbcType="VARCHAR" />
<result column="filetype" property="filetype" jdbcType="INTEGER" />
<result column="imgsize" property="imgsize" jdbcType="VARCHAR" />
<result column="pid" property="pid" jdbcType="VARCHAR" />
<result column="fileicon" property="fileicon" jdbcType="VARCHAR" />
<result column="thumbnailurl" property="thumbnailurl" jdbcType="VARCHAR" />
<result column="typecode" property="typecode" jdbcType="VARCHAR" />
</resultMap>
<select id="findAllList" resultMap="CustomResultMap"
parameterType="cn.datainvest.dms.system.dto.request.DiskFileRequest">
SELECT
t2.id,
t2.filename,
t2.filesize,
t2.filetype,
t2.filemd5,
t2.filesuffix,
date_format(t2.createtime,'%Y-%m-%d %H:%i:%S') AS createtime,
dts.icon AS fileicon
FROM
(
SELECT
*
FROM
disk_file t1
<if test="isSuperAdmin != null and isSuperAdmin != '' and isSuperAdmin == 'false' ">
WHERE
createuserid = #{userid, jdbcType=VARCHAR}
AND pid = #{pid, jdbcType=VARCHAR}
UNION
SELECT
df.*
FROM
disk_file df
INNER JOIN (
SELECT
present_catalog
FROM
`disk_file_folder_privilege`
WHERE
user_id = #{userid, jdbcType=VARCHAR}
) tmp ON (tmp.present_catalog = df.id OR tmp.present_catalog = df.pid)
</if>
WHERE
pid = #{pid, jdbcType=VARCHAR}
) t2
LEFT JOIN
disk_type_suffix dts
ON dts.suffix = t2.filesuffix
AND dts.typecode = t2.typecode
<where>
<if test="typecode != null and typecode != '' and typecode != 'all' and typecode != 'ALL' ">
AND t2.typecode = #{typecode, jdbcType=VARCHAR}
</if>
<if test="filesuffix != null and filesuffix != '' ">
AND t2.filesuffix = #{filesuffix, jdbcType=VARCHAR}
</if>
<if test="filename != null and filename != ''">
AND t2.filename LIKE CONCAT('%', #{filename, jdbcType=VARCHAR},'%')
</if>
</where>
<choose>
<when test="orderfield != null and orderfield != '' and ordertype != null and ordertype != ''">
order by ${orderfield} ${ordertype}
</when>
<otherwise>
order by t2.createtime desc
</otherwise>
</choose>
</select>