RuoYi-v4.8 代码审计
·
00x1 系统管理
0x1 用户管理
1 登录名称查询
POST /system/user/list HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Content-Length: 157
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/user
Cookie: nav-style=default; JSESSIONID=cfcfd844-21b7-4936-819e-87364cfe6f03
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&deptId=&parentId=&loginName=111&phonenumber=222&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=



@RequiresPermissions("system:user:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(SysUser user)
{
startPage();
//返回用户信息
List<SysUser> list = userService.selectUserList(user);
//响应信息
return getDataTable(list);
}
<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
select u.user_id, u.dept_id, u.login_name, u.user_name, u.user_type, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.salt, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
where u.del_flag = '0'
<if test="userId != null and userId != 0">
AND u.user_id = #{userId}
</if>
<if test="loginName != null and loginName != ''">
AND u.login_name like concat('%', #{loginName}, '%')
</if>
<if test="status != null and status != ''">
AND u.status = #{status}
</if>
<if test="phonenumber != null and phonenumber != ''">
AND u.phonenumber like concat('%', #{phonenumber}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(u.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(u.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
</if>
<if test="deptId != null and deptId != 0">
AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{deptId},ancestors) ))
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>


public static void startPage()
{
PageDomain pageDomain = TableSupport.buildPageRequest();
//获取字段后面值
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
//获取表名,拼接 isAsc排序,无sql
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
//无风险
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}

/**
* 当前记录起始索引
*/
public static final String PAGE_NUM = "pageNum";
/**
* 每页显示记录数
*/
public static final String PAGE_SIZE = "pageSize";
/**
* 排序列
*/
public static final String ORDER_BY_COLUMN = "orderByColumn";
/**
* 排序的方向 "desc" 或者 "asc".
*/
public static final String IS_ASC = "isAsc";
/**
* 分页参数合理化
*/
public static final String REASONABLE = "reasonable";
public static PageDomain getPageDomain()
{
PageDomain pageDomain = new PageDomain();
//获取字段值
//当前记录起始索引(默认)
//判断字段后面的值,转换类型
pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
//每页显示记录数
pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
//排序列
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
//默认顺序方向
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
// 分页参数合理化
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
//返回值
return pageDomain;
}
//字段值
public static Integer toInt(Object value, Integer defaultValue)
{
if (value == null)
{
//默认值
return defaultValue;
}
//instanceof Integer 用于判断一个对象是否为 Integer 类型(即 java.lang.Integer 的实例)
if (value instanceof Integer)
{
//数字转换
return (Integer) value;
}
//判断类型
if (value instanceof Number)
{
//截断小数,强转
return ((Number) value).intValue();
}
//判断是不是字符类型
final String valueStr = toStr(value, null);
//如果不是空
if (StringUtils.isEmpty(valueStr))
{
//返回
return defaultValue;
}
try
{
//都不是强转整数
return Integer.parseInt(valueStr.trim());
}
catch (Exception e)
{
//解析失败抛出异常(预设)
return defaultValue;
}
}
public static String toStr(Object value, String defaultValue)
{//如果是空的
if (null == value)
{
//返回null
return defaultValue;
}
//如果是字符
if (value instanceof String)
{
//返回字符类型
return (String) value;
}
//都不是返回字符类型
return value.toString();
}

public static String getParameter(String name)
{
//获取字段
return getRequest().getParameter(name);
}

/** 排序列 */
private String orderByColumn;
public String getOrderBy()
{//如果排序是空
if (StringUtils.isEmpty(orderByColumn))
{
return "";
}
//自动拼接表名
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
}
//大写转小写,空格_
public static String toUnderScoreCase(String str)
{
if (str == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
// 前置字符是否大写
boolean preCharIsUpperCase = true;
// 当前字符是否大写
boolean curreCharIsUpperCase = true;
// 下一字符是否大写
boolean nexteCharIsUpperCase = true;
//遍历每一个字符
for (int i = 0; i < str.length(); i++)
{//获取字符
char c = str.charAt(i);
if (i > 0)
{
//public static boolean isUpperCase(char ch) {
//return isUpperCase((int)ch);}
//判断前面字符大小写
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
}
else
{
//前面不是大写,或没有字符
preCharIsUpperCase = false;
}
//判断当前字符大小写
curreCharIsUpperCase = Character.isUpperCase(c);
//判断字符串长度,循环次数
if (i < (str.length() - 1))
{
//判断后面是不是大小写
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
}
//如果全大写,下一个不是
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
{
//下划线
//private static final char SEPARATOR = '_';
sb.append(SEPARATOR);
}
//(前面不是第一个且是大写)并且当前大写
else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
{
//前面插入_
sb.append(SEPARATOR);
}
//不然全小写
sb.append(Character.toLowerCase(c));
}
//返回字符串
return sb.toString();
}
![]()
/**
* 检查字符,防止注入绕过
*/
//表名加上 顺序字符
public static String escapeOrderBySql(String value)
{
//有过滤,如果是空或
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
{
throw new UtilException("参数不符合规范,不能进行查询");
}
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH)
{
throw new UtilException("参数已超过最大限制,不能进行查询");
}
return value;
}
public static boolean isValidOrderBySql(String value)
{
return value.matches(SQL_PATTERN);
}
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
*/
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
没有’
全部看一下

2 导出

//日志操作
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
//控制字段
@RequiresPermissions("system:user:export")
@PostMapping("/export")
@ResponseBody
//角色信息
public AjaxResult export(SysUser user)
{
//返回角色权限信息合集加上部门过滤
List<SysUser> list = userService.selectUserList(user);
//获取
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
//表格信息
return util.exportExcel(list, "用户数据");
}
@Override
//部门过滤
public List<SysRole> selectRoleList(SysRole role)
{
//根据角色返回
return roleMapper.selectRoleList(role);
}
public List<SysRole> selectRoleList(SysRole role);
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
@Override
//部门判断,给权限(部门关联以及角色数据范围)
@DataScope(deptAlias = "d")
public List<SysRole> selectRoleList(SysRole role)
{
//根据角色数据信息返回列表
return roleMapper.selectRoleList(role);
}
* @param role 角色信息
* @return 角色数据集合信息
*/
public List<SysRole> selectRoleList(SysRole role);
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
//默认空值,返回String类型,创建deptAlias()属性
public String deptAlias() default "";
/**
* 用户表的别名
*/
//
public String userAlias() default "";
/**
* 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@RequiresPermissions获取,多个权限用逗号分隔开来
*/
public String permission() default "";
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果
*/
public AjaxResult exportExcel(List<T> list, String sheetName)
{// return util.exportExcel(list, "用户数据");
return exportExcel(list, sheetName, StringUtils.EMPTY);
}
/**
* 工作薄对象
*/
private Workbook wb;
public AjaxResult exportExcel(List<T> list, String sheetName, String title)
{
this.init(list, sheetName, title, Type.EXPORT);
//写入,导出
return exportExcel();
}


public void init(List<T> list, String sheetName, String title, Type type)
{
if (list == null)
{
list = new ArrayList<T>();
}
//存储
this.list = list;
this.sheetName = sheetName;
this.type = type;
this.title = title;
//得到字段
createExcelField();
//创建
createWorkbook();
//创建excel第一行标题
createTitle();
//创建对象的子列表名称
createSubHead();
//创建字段后面写入
}
/**
* 得到所有定义字段
*/
private void createExcelField()
{
this.fields = getFields();
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
this.maxHeight = getRowHeight();
}
/**
* 创建一个工作簿
*/
public void createWorkbook()
{
this.wb = new SXSSFWorkbook(500);
this.sheet = wb.createSheet();
wb.setSheetName(0, sheetName);
this.styles = createStyles(wb);
}
public void createTitle()
{
if (StringUtils.isNotEmpty(title))
{
int titleLastCol = this.fields.size() - 1;
if (isSubList())
{
titleLastCol = titleLastCol + subFields.size() - 1;
}
Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
titleRow.setHeightInPoints(30);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(styles.get("title"));
titleCell.setCellValue(title);
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol));
}
}
public void createSubHead()
{
if (isSubList())
{
Row subRow = sheet.createRow(rownum);
int column = 0;
int subFieldSize = subFields != null ? subFields.size() : 0;
for (Object[] objects : fields)
{
Field field = (Field) objects[0];
Excel attr = (Excel) objects[1];
if (Collection.class.isAssignableFrom(field.getType()))
{
Cell cell = subRow.createCell(column);
cell.setCellValue(attr.name());
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
if (subFieldSize > 1)
{
CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);
sheet.addMergedRegion(cellAddress);
}
column += subFieldSize;
}
else
{
Cell cell = subRow.createCell(column++);
cell.setCellValue(attr.name());
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
}
}
rownum++;
}
}

public AjaxResult exportExcel()
{
OutputStream out = null;
try
{
writeSheet();
//文件名字
String filename = encodingFilename(sheetName);
//文件输入流
out = new FileOutputStream(getAbsoluteFile(filename));
//写入文件
//这里有path,真实文件地址
//也就是说导出之后有真实的地址文件在服务器,可以尝试任意文件导出。
wb.write(out);
//返回文件名
return AjaxResult.success(filename);
}
catch (Exception e)
{
log.error("导出Excel异常{}", e.getMessage());
throw new UtilException("导出Excel失败,请联系网站管理员!");
}
finally
{//关闭流
IOUtils.closeQuietly(wb);
IOUtils.closeQuietly(out);
}
}
构造

GET /common/download?fileName=158854b3-507b-469b-aeab-be309bca6ce1_%E7%94%A8%E6%88%B7%E6%95%B0%E6%8D%AE.xlsx&delete=true HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://127.0.0.1/system/user
Cookie: JSESSIONID=7f278409-37a3-45d1-952a-914a9362d69c
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Priority: u=4
http://127.0.0.1/common/download?fileName=../../../1.txt&delete=true

public static boolean checkAllowDownload(String resource)
{
// 禁止目录上跳级别
if (StringUtils.contains(resource, ".."))
{
//有过滤,直接禁止
return false;
}
// 检查允许下载的文件规则
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
{
return true;
}
// 不在允许下载的文件规则
return false;
}
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
"mp4", "avi", "rmvb",
// pdf
"pdf" };
public static boolean contains(Object[] array, Object objectToFind) {
return indexOf(array, objectToFind) != -1;
}
保护严格
3 导入

POST /system/user/importData HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=----geckoformboundary2a4bc98d4ddf6514faedcdd2cc5f5a6
Content-Length: 403
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/user
Cookie: JSESSIONID=186255de-9e56-4fb6-94d1-f1a5d68e100e
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
------geckoformboundary2a4bc98d4ddf6514faedcdd2cc5f5a6
Content-Disposition: form-data; name="file"; filename="1776333122151用户数据.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
------geckoformboundary2a4bc98d4ddf6514faedcdd2cc5f5a6
Content-Disposition: form-data; name="updateSupport"
on
------geckoformboundary2a4bc98d4ddf6514faedcdd2cc5f5a6--
/** 登录名称 */
@Excel(name = "登录名称")
private String loginName;
/** 用户名称 */
@Excel(name = "用户名称")
private String userName;
/** 用户类型 */
private String userType;
/** 用户邮箱 */
@Excel(name = "用户邮箱")
private String email;
/** 手机号码 */
@Excel(name = "手机号码", cellType = ColumnType.TEXT)
private String phonenumber;
/** 用户性别 */
@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
private String sex;
//boolean updateSupport是否更新模板
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
//处理文件流
List<SysUser> userList = util.importExcel(file.getInputStream());
//校验并且执行,返回信息
String message = userService.importUser(userList, updateSupport, getLoginName());
//返回信息
return AjaxResult.success(message);
}
@Override
public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName)
{
if (StringUtils.isNull(userList) || userList.size() == 0)
{
throw new ServiceException("导入用户数据不能为空!");
}
int successNum = 0;
int failureNum = 0;
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (SysUser user : userList)
{
try
{
// 验证是否存在这个用户
SysUser u = userMapper.selectUserByLoginName(user.getLoginName());
if (StringUtils.isNull(u))
{
BeanValidators.validateWithException(validator, user);
deptService.checkDeptDataScope(user.getDeptId());
String password = configService.selectConfigByKey("sys.user.initPassword");
user.setPassword(Md5Utils.hash(user.getLoginName() + password));
user.setCreateBy(operName);
userMapper.insertUser(user);
successNum++;
successMsg.append("<br/>" + successNum + "、账号 " + user.getLoginName() + " 导入成功");
}
else if (isUpdateSupport)
{
BeanValidators.validateWithException(validator, user);
checkUserAllowed(u);
checkUserDataScope(u.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
user.setUserId(u.getUserId());
user.setUpdateBy(operName);
userMapper.updateUser(user);
successNum++;
successMsg.append("<br/>" + successNum + "、账号 " + user.getLoginName() + " 更新成功");
}
else
{
failureNum++;
failureMsg.append("<br/>" + failureNum + "、账号 " + user.getLoginName() + " 已存在");
}
}
catch (Exception e)
{
failureNum++;
String loginName = user.getLoginName();
if (ExceptionUtil.isCausedBy(e, ConstraintViolationException.class))
{
loginName = EscapeUtil.clean(loginName);
}
String msg = "<br/>" + failureNum + "、账号 " + loginName + " 导入失败:";
failureMsg.append(msg + e.getMessage());
log.error(msg, e);
}
}
if (failureNum > 0)
{
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
}
else
{
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
}
return successMsg.toString();
}




//文件流,0
public List<T> importExcel(InputStream is, int titleNum)
{
List<T> list = null;
try
{// public static final String EMPTY = "";
list = importExcel(StringUtils.EMPTY, is, titleNum);
}
catch (Exception e)
{
log.error("导入Excel异常{}", e.getMessage());
throw new UtilException(e.getMessage());
}
finally
{
IOUtils.closeQuietly(is);
}
return list;
}
/**
* 对excel表单指定表格索引名转换成list







/**
* 对excel表单指定表格索引名转换成list
*
* @param sheetName 表格索引名
* @param titleNum 标题占用行数
* @param is 输入流
* @return 转换后集合
*/
//提取信息
public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception
{
this.type = Type.IMPORT;
this.wb = WorkbookFactory.create(is);
List<T> list = new ArrayList<T>();
// 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
//默认空,可以指定
Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
if (sheet == null)
{
throw new IOException("文件sheet不存在");
}
//判断版本后缀
boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
Map<String, PictureData> pictures;
if (isXSSFWorkbook)
{
pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
}
else
{
pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
}
// 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
int rows = sheet.getLastRowNum();
if (rows > 0)
{
// 定义一个map用于存放excel列的序号和field.
Map<String, Integer> cellMap = new HashMap<String, Integer>();
// 获取表头
Row heard = sheet.getRow(titleNum);
//循环获取列名,存储
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
{
Cell cell = heard.getCell(i);
if (StringUtils.isNotNull(cell))
{
String value = this.getCellValue(heard, i).toString();
cellMap.put(value, i);
}
else
{
cellMap.put(null, i);
}
}
// 有数据时才处理 得到类的所有field.
List<Object[]> fields = this.getFields();
Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
for (Object[] objects : fields)
{
Excel attr = (Excel) objects[1];
Integer column = cellMap.get(attr.name());
//有数据存储
if (column != null)
{
fieldsMap.put(column, objects);
}
}
//提取数据
for (int i = titleNum + 1; i <= rows; i++)
{
// 从第2行开始取数据,默认第一行是表头.
Row row = sheet.getRow(i);
// 判断当前行是否是空行
if (isRowEmpty(row))
{
continue;
}
T entity = null;
for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet())
{
Object val = this.getCellValue(row, entry.getKey());
// 如果不存在实例则新建.
entity = (entity == null ? clazz.newInstance() : entity);
// 从map中得到对应列的field.
Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
// 取得类型,并根据对象类型设置值.
Class<?> fieldType = field.getType();
if (String.class == fieldType)
{
String s = Convert.toStr(val);
if (StringUtils.endsWith(s, ".0"))
{
val = StringUtils.substringBefore(s, ".0");
}
else
{
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat))
{
val = parseDateToStr(dateFormat, val);
}
else
{
val = Convert.toStr(val);
}
}
}
else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
{
val = Convert.toInt(val);
}
else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
{
val = Convert.toLong(val);
}
else if (Double.TYPE == fieldType || Double.class == fieldType)
{
val = Convert.toDouble(val);
}
else if (Float.TYPE == fieldType || Float.class == fieldType)
{
val = Convert.toFloat(val);
}
else if (BigDecimal.class == fieldType)
{
val = Convert.toBigDecimal(val);
}
else if (Date.class == fieldType)
{
if (val instanceof String)
{
val = DateUtils.parseDate(val);
}
else if (val instanceof Double)
{
val = DateUtil.getJavaDate((Double) val);
}
}
else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
{
val = Convert.toBool(val, false);
}
if (StringUtils.isNotNull(fieldType))
{
String propertyName = field.getName();
if (StringUtils.isNotEmpty(attr.targetAttr()))
{
propertyName = field.getName() + "." + attr.targetAttr();
}
if (StringUtils.isNotEmpty(attr.readConverterExp()))
{
val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
}
else if (StringUtils.isNotEmpty(attr.dictType()))
{
if (!sysDictMap.containsKey(attr.dictType() + val))
{
String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
sysDictMap.put(attr.dictType() + val, dictValue);
}
val = sysDictMap.get(attr.dictType() + val);
}
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
val = dataFormatHandlerAdapter(val, attr, null);
}
else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
{
PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
if (image == null)
{
val = "";
}
else
{
byte[] data = image.getData();
val = FileUtils.writeImportBytes(data);
}
}
ReflectUtils.invokeSetter(entity, propertyName, val);
}
}
list.add(entity);
}
}
return list;
}
public static String toStr(Object value)
{
return toStr(value, null);
}
//xss
public static String toStr(Object value, String defaultValue)
{
if (null == value)
{
return defaultValue;
}
if (value instanceof String)
{
return (String) value;
}
//xss?
return value.toString();
}
public static String substringBefore(String str, String separator) {
//检查后缀,优化算法
//去掉.0净化
if (!isEmpty(str) && separator != null) {
if (separator.isEmpty()) {
return "";
} else {
int pos = str.indexOf(separator);
return pos == -1 ? str : str.substring(0, pos);
}
} else {
return str;
}
}
* @param dateFormat 日期格式
* @param val 被格式化的日期对象
* @return 格式化后的日期字符
*/
public String parseDateToStr(String dateFormat, Object val)
{
if (val == null)
{
return "";
}
String str;
if (val instanceof Date)
{
str = DateUtils.parseDateToStr(dateFormat, (Date) val);
}
else if (val instanceof LocalDateTime)
{
str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));
}
else if (val instanceof LocalDate)
{
str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));
}
else
{
str = val.toString();
}
return str;
}
/**
* * 判断一个字符串是否为空串
*
* @param str String
* @return true:为空 false:非空
*/
public static boolean isEmpty(String str)
{
return isNull(str) || NULLSTR.equals(str.trim());
}



@Override
public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName)
{
if (StringUtils.isNull(userList) || userList.size() == 0)
{
throw new ServiceException("导入用户数据不能为空!");
}
int successNum = 0;
int failureNum = 0;
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (SysUser user : userList)
{
try
{
// 验证是否存在这个用户
//这里校验
//@Xss(message = "登录账号不能包含脚本字符")
// @NotBlank(message = "登录账号不能为空")
// @Size(min = 0, max = 30, message = "登录账号长度不能超过30个字符")
// public String getLoginName()
// {登录名称
// return loginName;
// }
//校验账号,并查询部门
SysUser u = userMapper.selectUserByLoginName(user.getLoginName());
if (StringUtils.isNull(u))
{//这里校验,xss
//规则校验,这里后面校验才生效
//回滚校验
BeanValidators.validateWithException(validator, user);
//获取现在用户操作部门范围
deptService.checkDeptDataScope(user.getDeptId());
//获取密码
String password = configService.selectConfigByKey("sys.user.initPassword");
//加密密码
user.setPassword(Md5Utils.hash(user.getLoginName() + password));
//获取名字
user.setCreateBy(operName);
//存储
/**
* 新增用户信息
*
* @param user 用户信息
* @return 结果
*/
// public int insertUser(SysUser user);
userMapper.insertUser(user);
//+1循环
successNum++;
successMsg.append("<br/>" + successNum + "、账号 " + user.getLoginName() + " 导入成功");
}
else if (isUpdateSupport)
{
BeanValidators.validateWithException(validator, user);
checkUserAllowed(u);
checkUserDataScope(u.getUserId());
deptService.checkDeptDataScope(user.getDeptId());
user.setUserId(u.getUserId());
user.setUpdateBy(operName);
userMapper.updateUser(user);
successNum++;
successMsg.append("<br/>" + successNum + "、账号 " + user.getLoginName() + " 更新成功");
}
else
{
failureNum++;
failureMsg.append("<br/>" + failureNum + "、账号 " + user.getLoginName() + " 已存在");
}
}
catch (Exception e)
{
failureNum++;
String loginName = user.getLoginName();
if (ExceptionUtil.isCausedBy(e, ConstraintViolationException.class))
{//这里过滤
loginName = EscapeUtil.clean(loginName);
}
String msg = "<br/>" + failureNum + "、账号 " + loginName + " 导入失败:";
failureMsg.append(msg + e.getMessage());
log.error(msg, e);
}
}
if (failureNum > 0)
{
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
}
else
{
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
}
return successMsg.toString();
}
/**
* bean对象属性验证
*
* @author ruoyi
*/
public class BeanValidators
{
public static void validateWithException(Validator validator, Object object, Class<?>... groups)
throws ConstraintViolationException
{
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty())
{
throw new ConstraintViolationException(constraintViolations);
}
}
}

/**
* 清除所有HTML标签,但是不删除标签内的内容
*
* @param content 文本
* @return 清除标签后的文本
*/
public static String clean(String content)
{
return new HTMLFilter().filter(content);
}
@Nullable
public Object proceed() throws Throwable {
try {
return super.proceed();
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
if (!ReflectionUtils.declaresException(this.getMethod(), ex.getClass()) && !KotlinDetector.isKotlinType(this.getMethod().getDeclaringClass())) {
throw new UndeclaredThrowableException(ex);
} else {
throw ex;
}
}
}
@Override
public void checkDeptDataScope(Long deptId)
{//不是admin并且部门id不是空
if (!SysUser.isAdmin(ShiroUtils.getUserId()) && StringUtils.isNotNull(deptId))
{
//
SysDept dept = new SysDept();
dept.setDeptId(deptId);
//部门范围校验,列表
List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
//为空的话
if (StringUtils.isEmpty(depts))
{
throw new ServiceException("没有权限访问部门数据!");
}}
/**
* 查询部门管理数据
*
* @param dept 部门信息
* @return 部门信息集合
*/
@Override
@DataScope(deptAlias = "d")
public List<SysDept> selectDeptList(SysDept dept)
{
return deptMapper.selectDeptList(dept);
}
/**
* 通过用户名查询用户
*
* @param userName 用户名
* @return 用户对象信息
*/
public SysUser selectUserByLoginName(String userName);
/**
* 根据键名查询参数配置信息
*
* @param configKey 参数key
* @return 参数键值
*/
@Override
public String selectConfigByKey(String configKey)
{
String configValue = Convert.toStr(CacheUtils.get(getCacheName(), getCacheKey(configKey)));
if (StringUtils.isNotEmpty(configValue))
{
return configValue;
}
SysConfig config = new SysConfig();
config.setConfigKey(configKey);
SysConfig retConfig = configMapper.selectConfig(config);
if (StringUtils.isNotNull(retConfig))
{
CacheUtils.put(getCacheName(), getCacheKey(configKey), retConfig.getConfigValue());
return retConfig.getConfigValue();
}
return StringUtils.EMPTY;
}
@Xss(message = "登录账号不能包含脚本字符")
@NotBlank(message = "登录账号不能为空")
@Size(min = 0, max = 30, message = "登录账号长度不能超过30个字符")
public String getLoginName()
{
return loginName;
}
多重重复导入(不存在这个风险)
/**
* 根据条件分页查询用户列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user)
{
return userMapper.selectUserList(user);
}
/**
* 通过用户名查询用户
*
* @param userName 用户名
* @return 用户对象信息
*/
public SysUser selectUserByLoginName(String userName);
<select id="selectUserByLoginName" parameterType="String" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.login_name = #{userName} and u.del_flag = '0'
</select>
//返回查询值
SysUser u = userMapper.selectUserByLoginName(user.getLoginName());
if (StringUtils.isNull(u))
{
//查询
BeanValidators.validateWithException(validator, user);
//
deptService.checkDeptDataScope(user.getDeptId());
//
String password = configService.selectConfigByKey("sys.user.initPassword");
user.setPassword(Md5Utils.hash(user.getLoginName() + password));
user.setCreateBy(operName);
userMapper.insertUser(user);
successNum++;
successMsg.append("<br/>" + successNum + "、账号 " + user.getLoginName() + " 导入成功");
}
<select id="selectUserByLoginName" parameterType="String" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.login_name = #{userName} and u.del_flag = '0'
4 模板下载
GET /system/user/importTemplate HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Connection: keep-alive
Referer: http://127.0.0.1/index
Cookie: JSESSIONID=d8665548-01a0-4db3-bdd5-52fc5014227c
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
@RequiresPermissions("system:user:view")
@GetMapping("/importTemplate")
@ResponseBody
public AjaxResult importTemplate()
{
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
return util.importTemplateExcel("用户数据");
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @return 结果
*/
public AjaxResult importTemplateExcel(String sheetName)
{
return importTemplateExcel(sheetName, StringUtils.EMPTY);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
*/
//public static final String EMPTY = "";
public AjaxResult importTemplateExcel(String sheetName, String title)
{
this.init(null, sheetName, title, Type.IMPORT);
return exportExcel();
}
public void init(List<T> list, String sheetName, String title, Type type)
{
if (list == null)
{
list = new ArrayList<T>();
}
this.list = list;
this.sheetName = sheetName;
this.type = type;
this.title = title;
//数据获取
createExcelField();
createWorkbook();
createTitle();
createSubHead();
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @return 结果
*/
public AjaxResult exportExcel()
{
OutputStream out = null;
try
{
//与之前一样,无任意导出
writeSheet();
String filename = encodingFilename(sheetName);
out = new FileOutputStream(getAbsoluteFile(filename));
wb.write(out);
return AjaxResult.success(filename);
}
catch (Exception e)
{
log.error("导出Excel异常{}", e.getMessage());
throw new UtilException("导出Excel失败,请联系网站管理员!");
}
finally
{
IOUtils.closeQuietly(wb);
IOUtils.closeQuietly(out);
}
}
public String encodingFilename(String filename)
{
filename = UUID.randomUUID() + "_" + filename + ".xlsx";
return filename;
}
5 新增
GET /system/user/add HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://127.0.0.1/index
Cookie: JSESSIONID=7ac5be7b-a467-472b-917f-dd070ffdf922
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Priority: u=4
@GetMapping("/add")
public String add(ModelMap mmap)
{
mmap.put("roles", roleService.selectRoleAll().stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
mmap.put("posts", postService.selectPostAll());
return prefix + "/add";
}
POST /system/user/add HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 195
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/user/add
Cookie: JSESSIONID=86100370-c9f0-4800-aca6-f125066d5129
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
deptId=100&userName=111&deptName=%E8%8B%A5%E4%BE%9D%E7%A7%91%E6%8A%80&phonenumber=13889996633&email=33%40111&loginName=1234567890&password=123456&sex=0&role=2&remark=&status=0&roleIds=2&postIds=1
*/
@RequiresPermissions("system:user:add")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysUser user)
{
//校验部门是否有数据权限,查看校验(鉴权校验),这里部门添加了
deptService.checkDeptDataScope(user.getDeptId());
//这里校验角色是否有权限,新增加角色。
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkLoginNameUnique(user))
{
return error("新增用户'" + user.getLoginName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("新增用户'" + user.getLoginName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("新增用户'" + user.getLoginName() + "'失败,邮箱账号已存在");
}
//获取盐
user.setSalt(ShiroUtils.randomSalt());
//获取密码加密,加盐
user.setPassword(passwordService.encryptPassword(user.getLoginName(), user.getPassword(), user.getSalt()));
//获取日期
user.setPwdUpdateDate(DateUtils.getNowDate());
//获取创建者
user.setCreateBy(getLoginName());
//返回信息,存储插入
return toAjax(userService.insertUser(user));
}
public boolean matches(SysUser user, String newPassword)
{
return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt()));
}
<insert id="batchUserRole">
insert into sys_user_role(user_id, role_id) values
<foreach item="item" index="index" collection="list" separator=",">
(#{item.userId},#{item.roleId})
</foreach>
</insert>
/**
* 新增保存用户信息
*
* @param user 用户信息
* @return 结果
*/
@Override
@Transactional
public int insertUser(SysUser user)
{
// 新增用户信息,这里增加部门关联,可能有异常(sql没有漏洞,无联合关联)
int rows = userMapper.insertUser(user);
// 新增用户岗位关联,取出空指针异常
insertUserPost(user);
// 新增用户与角色关联,提取漏洞
insertUserRole(user.getUserId(), user.getRoleIds());
return rows;
}
//selectPostsByUserId
/**
* 新增用户信息
*
* @param user 用户信息
* @return 结果
*/
public int insertUser(SysUser user);
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
insert into sys_user(
<if test="userId != null and userId != 0">user_id,</if>
<if test="deptId != null and deptId != 0">dept_id,</if>
<if test="loginName != null and loginName != ''">login_name,</if>
<if test="userName != null and userName != ''">user_name,</if>
<if test="userType != null and userType != ''">user_type,</if>
<if test="email != null and email != ''">email,</if>
<if test="avatar != null and avatar != ''">avatar,</if>
<if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
<if test="sex != null and sex != ''">sex,</if>
<if test="password != null and password != ''">password,</if>
<if test="salt != null and salt != ''">salt,</if>
<if test="status != null and status != ''">status,</if>
<if test="pwdUpdateDate != null">pwd_update_date,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="remark != null and remark != ''">remark,</if>
create_time
)values(
<if test="userId != null and userId != ''">#{userId},</if>
<if test="deptId != null and deptId != ''">#{deptId},</if>
<if test="loginName != null and loginName != ''">#{loginName},</if>
<if test="userName != null and userName != ''">#{userName},</if>
<if test="userType != null and userType != ''">#{userType},</if>
<if test="email != null and email != ''">#{email},</if>
<if test="avatar != null and avatar != ''">#{avatar},</if>
<if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
<if test="sex != null and sex != ''">#{sex},</if>
<if test="password != null and password != ''">#{password},</if>
<if test="salt != null and salt != ''">#{salt},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="pwdUpdateDate != null">#{pwdUpdateDate},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="remark != null and remark != ''">#{remark},</if>
sysdate()
)
</insert>

* @param userId 用户ID
* @param roleIds 角色组
*/
public void insertUserRole(Long userId, Long[] roleIds)
{
if (StringUtils.isNotNull(roleIds))
{
// 新增用户与角色管理
List<SysUserRole> list = new ArrayList<SysUserRole>();
//角色添加,可能存在空指针
for (Long roleId : roleIds)
{
SysUserRole ur = new SysUserRole();
//设置用户id
ur.setUserId(userId);
//设置角色id
ur.setRoleId(roleId);
//添加
list.add(ur);
}
//批量插入用户与角色关联
if (list.size() > 0)
{
userRoleMapper.batchUserRole(list);
}
}
}
public void insertUserPost(SysUser user)
{
//返回用户岗位组
Long[] posts = user.getPostIds();
//如果不是空
if (StringUtils.isNotNull(posts))
{
// 新增用户与岗位管理
List<SysUserPost> list = new ArrayList<SysUserPost>();
for (Long postId : posts)
{
SysUserPost up = new SysUserPost();
//获取用户顺序号,赋值,越权角色关联漏洞
up.setUserId(user.getUserId());
//获取岗位号,赋值,空指针
up.setPostId(postId);
//添加
//空指针风险,后面取出时候
list.add(up);
}
//批量绑定岗位,关联用户
if (list.size() > 0)
{
//岗位绑定,批量新增
userPostMapper.batchUserPost(list);
}
}
}
/**
* 新增用户信息
*
* @param user 用户信息
* @return 结果
*/
public int insertUser(SysUser user);
/**
* 生成随机盐
*/
public static String randomSalt()
{
// 一个Byte占两个字节,此处生成的3字节,字符串长度为6
SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
String hex = secureRandom.nextBytes(3).toHex();
return hex;
}
public String encryptPassword(String loginName, String password, String salt)
{
return new Md5Hash(loginName + password + salt).toHex();
}


@Override
public void checkDeptDataScope(Long deptId)
{//不是admin并且部门id不是空
if (!SysUser.isAdmin(ShiroUtils.getUserId()) && StringUtils.isNotNull(deptId))
{
SysDept dept = new SysDept();
dept.setDeptId(deptId);
//部门范围校验,返回列表,防止越权查看
List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
//为空的话,只允许相应部门修改
if (StringUtils.isEmpty(depts))
{
throw new ServiceException("没有权限访问部门数据!");
}}
/**
* 查询部门管理数据
*
* @param dept 部门信息
* @return 部门信息集合
*/
@Override
//这里过滤范围
@DataScope(deptAlias = "d")
public List<SysDept> selectDeptList(SysDept dept)
{
//返回所有部门信息
return deptMapper.selectDeptList(dept);
}
/**
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
@Override
@DataScope(deptAlias = "d")
public List<SysRole> selectRoleList(SysRole role)
{
return roleMapper.selectRoleList(role);
}
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色id
*/
@Override
public void checkRoleDataScope(Long... roleIds)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
for (Long roleId : roleIds)
{
SysRole role = new SysRole();
role.setRoleId(roleId);
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
if (StringUtils.isEmpty(roles))
{
throw new ServiceException("没有权限访问角色数据!");
}
}
}
}
6 预览


GET /system/user/view/108 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-GPC: 1
Connection: keep-alive
Referer: http://127.0.0.1/index
Cookie: JSESSIONID=e174703e-6f52-4593-9706-a3a4591c143c
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Priority: u=4

角色确实没有过滤,但有防呆机制

@RequiresPermissions("system:user:list")
@GetMapping("/view/{userId}")
public String view(@PathVariable("userId") Long userId, ModelMap mmap)
{
userService.checkUserDataScope(userId);
//selectRolesByUserId有问题,sql问题
//角色查询有过滤
mmap.put("user", userService.selectUserById(userId));
mmap.put("roleGroup", userService.selectUserRoleGroup(userId));
mmap.put("postGroup", userService.selectUserPostGroup(userId));
return prefix + "/view";
}
<sql id="selectUserVo">
select u.user_id, u.dept_id, u.login_name, u.user_name, u.user_type, u.email, u.avatar, u.phonenumber, u.sex, u.password, u.salt, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.update_by, u.update_time, u.remark,
d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
left join sys_user_role ur on u.user_id = ur.user_id
left join sys_role r on r.role_id = ur.role_id
</sql>
这里提取关联部门
主健不是空,就不会空指针。


/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{//获取用户id,判断是不是管理员
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
SysUser user = new SysUser();
user.setUserId(userId);
//查询
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
//返回不是空的
if (StringUtils.isEmpty(users))
{
throw new ServiceException("没有权限访问用户数据!");
}
}
}
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user)
{
return userMapper.selectUserList(user);
}
/**
* 根据条件分页查询用户列表
*
* @param sysUser 用户信息
* @return 用户信息集合信息
*/
public List<SysUser> selectUserList(SysUser sysUser);
/**
* 查询用户所属角色组
*
* @param userId 用户ID
* @return 结果
*/
@Override
public String selectUserRoleGroup(Long userId)
{// <select id="selectRolesByUserId" parameterType="Long" resultMap="SysRoleResult">
//<include refid="selectRoleContactVo"/>
//WHERE r.del_flag = '0' and ur.user_id = #{userId}
//</select>
List<SysRole> list = roleMapper.selectRolesByUserId(userId);
if (CollectionUtils.isEmpty(list))
{// public static final String EMPTY = "";
return StringUtils.EMPTY;
}
//无空指针,获取角色
return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(","));
}


@Override
@SuppressWarnings("unchecked")
public <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
A container;
if (isParallel()
&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
&& (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
container = collector.supplier().get();
BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
forEach(u -> accumulator.accept(container, u));
}
else {
container = evaluate(ReduceOps.makeRef(collector));
}
return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
? (R) container
: collector.finisher().apply(container);
}
**
* 查询用户所属岗位组
*
* @param userId 用户ID
* @return 结果
*/
@Override
public String selectUserPostGroup(Long userId)
{
List<SysPost> list = postMapper.selectPostsByUserId(userId);
if (CollectionUtils.isEmpty(list))
{
return StringUtils.EMPTY;
}
//这里null.get...,抛出空指针
return list.stream().map(SysPost::getPostName).collect(Collectors.joining(","));
}
<select id="selectPostsByUserId" parameterType="Long" resultMap="SysPostResult">
SELECT p.post_id, p.post_name, p.post_code
FROM sys_user u
LEFT JOIN sys_user_post up ON u.user_id = up.user_id
LEFT JOIN sys_post p ON up.post_id = p.post_id
WHERE up.user_id = #{userId}
</select>
//这里有问题,返回null
//u.user_id = up.user_id表关联
为什么没有空指针
20:05:48.970 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectUserById - [debug,135] - <== Total: 1
20:05:48.972 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectRolesByUserId - [debug,135] - ==> Preparing: select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status, r.del_flag, r.create_time, r.remark from sys_role r left join sys_user_role ur on ur.role_id = r.role_id left join sys_user u on u.user_id = ur.user_id left join sys_dept d on u.dept_id = d.dept_id WHERE r.del_flag = '0' and ur.user_id = ?
20:05:48.974 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectRolesByUserId - [debug,135] - ==> Parameters: 108(Long)
这里返回空
r.del_flag = '0'过滤了
20:05:48.990 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectRolesByUserId - [debug,135] - <== Total: 0
20:05:48.993 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectPostsByUserId - [debug,135] - ==> Preparing: SELECT p.post_id, p.post_name, p.post_code FROM sys_user u LEFT JOIN sys_user_post up ON u.user_id = up.user_id LEFT JOIN sys_post p ON up.post_id = p.post_id WHERE up.user_id = ?
20:05:48.995 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectPostsByUserId - [debug,135] - ==> Parameters: 108(Long)
20:05:48.998 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectPostsByUserId - [debug,135] - <== Total: 1
20:05:49.035 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectDictLabel - [debug,135] - ==> Preparing: select dict_label from sys_dict_data where dict_type = ? and dict_value = ?
20:05:49.037 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectDictLabel - [debug,135] - ==> Parameters: sys_user_sex(String), 1(String)
20:05:49.041 [http-nio-80-exec-38] DEBUG c.r.s.m.S.selectDictLabel - [debug,135] - <== Total: 1
7 删除
POST /system/user/remove HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 7
Origin: http://127.0.0.1
Sec-GPC: 1
Connection: keep-alive
Referer: http://127.0.0.1/system/user
Cookie: JSESSIONID=c86a2026-cadf-4d45-b63d-2d74e072433d
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
ids=107
@RequiresPermissions("system:user:remove")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids)
{//匹配数组有没有删除自己
if (ArrayUtils.contains(Convert.toLongArray(ids), getUserId()))
{
return error("当前用户不能删除");
}
//错误输出0报错
return toAjax(userService.deleteUserByIds(ids));
}
public static boolean contains(Object[] array, Object objectToFind) {
return indexOf(array, objectToFind) != -1;
}
public static int indexOf(Object[] array, Object objectToFind) {
return indexOf(array, objectToFind, 0);
}
//删除数组,登录用户,0
public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
//如果数组为空
if (array == null) {
return -1;
} else {
//不会到这里
if (startIndex < 0) {
startIndex = 0;
}
//登录用户为空
if (objectToFind == null) {
for(int i = startIndex; i < array.length; ++i) {
//数组为空
if (array[i] == null) {
//返回0
return i;
}
}
} else {
//循环
for(int i = startIndex; i < array.length; ++i) {
//匹配有没有
if (objectToFind.equals(array[i])) {
//返回索引
return i;
}
}
}
return -1;
}
}
/**
* 转换为Long数组<br>
*
* @param str 被转换的值
* @return 结果
*/
public static Long[] toLongArray(String str)
{
return toLongArray(",", str);
}
/**
* 获取登录用户id
*/
public Long getUserId()
{
return getSysUser().getUserId();
}
public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
if (array == null) {
return -1;
} else {
if (startIndex < 0) {
startIndex = 0;
}
if (objectToFind == null) {
for(int i = startIndex; i < array.length; ++i) {
if (array[i] == null) {
return i;
}
}
} else {
for(int i = startIndex; i < array.length; ++i) {
if (objectToFind.equals(array[i])) {
return i;
}
}
}
return -1;
}
}
public int deleteUserByIds(String ids)
{
//删除用户转换数组
Long[] userIds = Convert.toLongArray(ids);
//循环
for (Long userId : userIds)
{//不让操作超级管理员
checkUserAllowed(new SysUser(userId));
//校验操作范围
checkUserDataScope(userId);
}
// 删除用户与角色关联
userRoleMapper.deleteUserRole(userIds);
// 删除用户与岗位关联
userPostMapper.deleteUserPost(userIds);
return userMapper.deleteUserByIds(userIds);
}
<delete id="deleteUserRole" parameterType="Long">
delete from sys_user_role where user_id in
<foreach collection="array" item="userId" open="(" separator="," close=")">
#{userId}
</foreach>
</delete>
**
* 转换为Long数组<br>
*
* @param str 被转换的值
* @return 结果
*/
public static Long[] toLongArray(String str)
{
return toLongArray(",", str);
}
/**
* 校验用户是否允许操作
*
* @param user 用户信息
*/
@Override
public void checkUserAllowed(SysUser user)
{//被删除的用户不是空的,是管理员
if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin())
{
throw new ServiceException("不允许操作超级管理员用户");
}
}
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{//如果不是超级管理员
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
SysUser user = new SysUser();
user.setUserId(userId);
//校验,返回,这里不存在越权
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
//如果不是数组中的用户
if (StringUtils.isEmpty(users))
{//不让操作
throw new ServiceException("没有权限访问用户数据!");
}
}
}
* 根据条件分页查询用户列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
//部门,用户数据范围
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user)
{
return userMapper.selectUserList(user);
}
/**
* * 判断一个Collection是否为空, 包含List,Set,Queue
*
* @param coll 要判断的Collection
* @return true:为空 false:非空
*/
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}


protected AjaxResult toAjax(int rows)
{//查询数据库对比
return rows > 0 ? success() : error();
}
8 修改用户
GET /system/user/edit/100 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://127.0.0.1/index
Cookie: JSESSIONID=544b6d89-dc9e-45a3-8105-4118d3ba1679
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Priority: u=4
POST /system/user/edit HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 205
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/user/edit/106
Cookie: JSESSIONID=eb6c5d56-e051-4140-813e-e8d245455b11
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
userId=106&deptId=100&userName=123456&dept.deptName=%E8%8B%A5%E4%BE%9D%E7%A7%91%E6%8A%80&phonenumber=13886112233&email=1%401111&loginName=13222&sex=0&role=2&remark=&status=0&roleIds=2&postIds=1%2C2%2C3%2C4
@RequiresPermissions("system:user:edit")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(@Validated SysUser user)
{//不让操作超级管理员
userService.checkUserAllowed(user);
//获取要操作的用户id
userService.checkUserDataScope(user.getUserId());
//获取部门id对比,不让提权
deptService.checkDeptDataScope(user.getDeptId());
//获取角色对比,不让提权
roleService.checkRoleDataScope(user.getRoleIds());
if (!userService.checkLoginNameUnique(user))
{
return error("修改用户'" + user.getLoginName() + "'失败,登录账号已存在");
}
else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
{
return error("修改用户'" + user.getLoginName() + "'失败,手机号码已存在");
}
else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
{
return error("修改用户'" + user.getLoginName() + "'失败,邮箱账号已存在");
}
//填写登录用户名
user.setUpdateBy(getLoginName());
AuthorizationUtils.clearAllCachedAuthorizationInfo();
//返回并更新数据库
return toAjax(userService.updateUser(user));
}
@Override
@Transactional
public int updateUser(SysUser user)
{
Long userId = user.getUserId();
// 删除用户与角色关联
userRoleMapper.deleteUserRoleByUserId(userId);
// 新增用户与角色管理
insertUserRole(user.getUserId(), user.getRoleIds());
// 删除用户与岗位关联
userPostMapper.deleteUserPostByUserId(userId);
// 新增用户与岗位管理
insertUserPost(user);
return userMapper.updateUser(user);
}
public class AuthorizationUtils
{
/**
* 清理所有用户授权信息缓存
*/
public static void clearAllCachedAuthorizationInfo()
{
getUserRealm().clearAllCachedAuthorizationInfo();
}
/**
* 获取登录用户名
*/
public String getLoginName()
{
return getSysUser().getLoginName();
}
public void setUpdateBy(String updateBy)
{
this.updateBy = updateBy;
}
/** 更新者 */
private String updateBy;
public Long getDeptId()
{
return deptId;
}
/** 部门ID */
@Excel(name = "部门编号", type = Type.IMPORT)
private Long deptId;
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
SysUser user = new SysUser();
//赋值
user.setUserId(userId);
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
if (StringUtils.isEmpty(users))
{
throw new ServiceException("没有权限访问用户数据!");
}
}
}
/**
* 修改用户
*/
@RequiresPermissions("system:user:edit")
@GetMapping("/edit/{userId}")
public String edit(@PathVariable("userId") Long userId, ModelMap mmap)
{//鉴权返回用户与岗位相关,部门范围与用户校验(仅本人)的用户信息
//可以给修改的合法用户
userService.checkUserDataScope(userId);
//获取角色信息
List<SysRole> roles = roleService.selectRolesByUserId(userId);
//获取用户信息,比如邮箱什么的,添加
mmap.put("user", userService.selectUserById(userId));
//显示
mmap.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
//存储岗位信息,空指针漏洞
mmap.put("posts", postService.selectPostsByUserId(userId));
//这里才是修改
return prefix + "/edit";
}
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
@Override
public SysUser selectUserById(Long userId)
{
return userMapper.selectUserById(userId);
}
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
public SysUser selectUserById(Long userId);
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
SysUser user = new SysUser();
user.setUserId(userId);
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
if (StringUtils.isEmpty(users))
{
throw new ServiceException("没有权限访问用户数据!");
}
}
}
/**
* 根据条件分页查询用户列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user)
{
return userMapper.selectUserList(user);
}
/**
* 记录登录信息
*
* @param userId 用户ID
*/
public void recordLoginInfo(Long userId)
{
SysUser user = new SysUser();
//获取用户id
user.setUserId(userId);
//不可控
user.setLoginIp(ShiroUtils.getIp());
//
user.setLoginDate(DateUtils.getNowDate());
userService.updateUserInfo(user);
}
public static String getIp()
{
//字符串字数限制,不可控
return StringUtils.substring(getSubject().getSession().getHost(), 0, 128);
}
**
* 截取字符串
*
* @param str 字符串
* @param start 开始
* @param end 结束
* @return 结果
*/
public static String substring(final String str, int start, int end)
{//如果获取为空
if (str == null)
{
// ** 空字符串 */
// private static final String NULLSTR = "";
return NULLSTR;
}
if (end < 0)
{
end = str.length() + end;
}
if (start < 0)
{
start = str.length() + start;
}
if (end > str.length())
{
end = str.length();
}
if (start > end)
{
return NULLSTR;
}
if (start < 0)
{
start = 0;
}
if (end < 0)
{
end = 0;
}
return str.substring(start, end);
}
@Override
public List<SysRole> selectRolesByUserId(Long userId)
{//获取用户角色
List<SysRole> userRoles = roleMapper.selectRolesByUserId(userId);
//获取所有角色
List<SysRole> roles = selectRoleAll();
for (SysRole role : roles)
{//对比相同的
for (SysRole userRole : userRoles)
{//角色遍历
if (role.getRoleId().longValue() == userRole.getRoleId().longValue())
{
role.setFlag(true);
break;
}
}
}
//返回所有角色
return roles;
}
9 重置密码
这里没有做校验,可能引起拒绝服务就是登录名和密码都是一样的。
POST /system/user/resetPwd HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 48
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/user/resetPwd/108
Cookie: JSESSIONID=1c271d69-ed84-4dad-a667-70af943e95eb
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
userId=108&loginName=12345678901&password=123456
@RequiresPermissions("system:user:resetPwd")
@GetMapping("/resetPwd/{userId}")
public String resetPwd(@PathVariable("userId") Long userId, ModelMap mmap)
{//显示,无空指针漏洞
mmap.put("user", userService.selectUserById(userId));
return prefix + "/resetPwd";
}
/** 用户ID */
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
@RequiresPermissions("system:user:resetPwd")
@Log(title = "重置密码", businessType = BusinessType.UPDATE)
@PostMapping("/resetPwd")
@ResponseBody
public AjaxResult resetPwdSave(SysUser user)
{//检查修改用户是不是超级管理员
userService.checkUserAllowed(user);
//检查可以操作的用户
userService.checkUserDataScope(user.getUserId());
//获取盐
user.setSalt(ShiroUtils.randomSalt());
//密码加盐
user.setPassword(passwordService.encryptPassword(user.getLoginName(), user.getPassword(), user.getSalt()));
//校验密码
if (userService.resetUserPwd(user) > 0)
{//检查是不是修改自己
if (ShiroUtils.getUserId().longValue() == user.getUserId().longValue())
{//获取id查询用户,更新缓存,同步信息
setSysUser(userService.selectUserById(user.getUserId()));
}
//成功
return success();
}
return error();
}
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
@Override
public SysUser selectUserById(Long userId)
{
return userMapper.selectUserById(userId);
}
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
public SysUser selectUserById(Long userId);
/**
* 校验用户是否允许操作
*
* @param user 用户信息
*/
@Override
public void checkUserAllowed(SysUser user)
{
if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin())
{
throw new ServiceException("不允许操作超级管理员用户");
}
}
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
//获取现在用户
SysUser user = new SysUser();
user.setUserId(userId);
//检查是否可以操作,返回可以操作的用户
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
if (StringUtils.isEmpty(users))
{
throw new ServiceException("没有权限访问用户数据!");
}
}
}
/**
* 根据条件分页查询用户列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user)
{
return userMapper.selectUserList(user);
}
public void setSalt(String salt)
{
this.salt = salt;
}
10 用户状态
POST /system/user/changeStatus HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 19
Origin: http://127.0.0.1
Sec-GPC: 1
Connection: keep-alive
Referer: http://127.0.0.1/system/user
Cookie: JSESSIONID=14eabfc6-4454-478e-a3ce-03784c7d8887
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
userId=108&status=0
/**
* 用户状态修改
*/
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@RequiresPermissions("system:user:edit")
@PostMapping("/changeStatus")
@ResponseBody
public AjaxResult changeStatus(SysUser user)
{//不允许操作管理员
userService.checkUserAllowed(user);
//看用户有没有修改用户的权力
userService.checkUserDataScope(user.getUserId());
//返回并关闭
return toAjax(userService.changeStatus(user));
}
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
SysUser user = new SysUser();
user.setUserId(userId);
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
if (StringUtils.isEmpty(users))
{
throw new ServiceException("没有权限访问用户数据!");
}
}
}
11 分配角色
GET /system/user/authRole/108 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-GPC: 1
Connection: keep-alive
Referer: http://127.0.0.1/index
Cookie: JSESSIONID=1bcdef78-9cf6-4c45-b03a-d5e492b0f2fb
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Priority: u=4
POST /system/user/authRole/insertAuthRole HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 20
Origin: http://127.0.0.1
Sec-GPC: 1
Connection: keep-alive
Referer: http://127.0.0.1/system/user/authRole/108
Cookie: JSESSIONID=1bcdef78-9cf6-4c45-b03a-d5e492b0f2fb
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
userId=108&roleIds=2
@RequiresPermissions("system:user:edit")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PostMapping("/authRole/insertAuthRole")
@ResponseBody
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{//检查有没有权限修改用户,数组
userService.checkUserDataScope(userId);
//检查自己角色是不是在里面
roleService.checkRoleDataScope(roleIds);
//解除,并绑定
userService.insertUserAuth(userId, roleIds);
//清理缓存
AuthorizationUtils.clearAllCachedAuthorizationInfo();
return success();
}
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
SysUser user = new SysUser();
user.setUserId(userId);
//根据部门返回可控用户
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
if (StringUtils.isEmpty(users))
{
throw new ServiceException("没有权限访问用户数据!");
}
}
}
/**
* 根据条件分页查询用户列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
@Override
@DataScope(deptAlias = "d", userAlias = "u")
//部门加上仅本人校验返回
public List<SysUser> selectUserList(SysUser user)
{
return userMapper.selectUserList(user);
}
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色id
*/
@Override
public void checkRoleDataScope(Long... roleIds)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{//查看在不在其中
for (Long roleId : roleIds)
{
SysRole role = new SysRole();
//存储
role.setRoleId(roleId);
//根据部门返回角色,这里有风险
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
if (StringUtils.isEmpty(roles))
{
throw new ServiceException("没有权限访问角色数据!");
}
}
}
}
**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
@Override
@DataScope(deptAlias = "d")
public List<SysRole> selectRoleList(SysRole role)
{
return roleMapper.selectRoleList(role);
}
/**
* 用户授权角色
*
* @param userId 用户ID
* @param roleIds 角色组
*/
@Override
@Transactional
public void insertUserAuth(Long userId, Long[] roleIds)
{
//删除原来角色关联
userRoleMapper.deleteUserRoleByUserId(userId);
//添加
insertUserRole(userId, roleIds);
}
/**
* 通过用户ID删除用户和角色关联
*
* @param userId 用户ID
* @return 结果
*/
public int deleteUserRoleByUserId(Long userId)
/**
* 新增用户角色信息
*
* @param userId 用户ID
* @param roleIds 角色组
*/
public void insertUserRole(Long userId, Long[] roleIds)
{//角色不是空
if (StringUtils.isNotNull(roleIds))
{
// 新增用户与角色管理,批量绑定
List<SysUserRole> list = new ArrayList<SysUserRole>();
for (Long roleId : roleIds)
{
SysUserRole ur = new SysUserRole();
ur.setUserId(userId);
ur.setRoleId(roleId);
list.add(ur);
}
if (list.size() > 0)
{
userRoleMapper.batchUserRole(list);
}
}
}
0x2 角色管理
1 添加
POST /system/role/add HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 58
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/role/add
Cookie: JSESSIONID=abccc535-5474-4884-880d-1b238916ec9d
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
roleName=1&roleKey=2&roleSort=3&status=0&remark=1&menuIds=
通过了检测之后,什么鉴权都没有,直接就添加了角色。
我也没看到菜单权限的鉴权。
@RequiresPermissions("system:role:add")
@Log(title = "角色管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysRole role)
{
if (!roleService.checkRoleNameUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (!roleService.checkRoleKeyUnique(role))
{
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
//添加创建者
role.setCreateBy(getLoginName());
//清理缓存
AuthorizationUtils.clearAllCachedAuthorizationInfo();
//添加
return toAjax(roleService.insertRole(role));
}
/**
* 新增保存角色信息
*
* @param role 角色信息
* @return 结果
*/
@Override
@Transactional
public int insertRole(SysRole role)
{
// 新增角色信息,自动状态
roleMapper.insertRole(role);
//添加菜单
return insertRoleMenu(role);
}
/**
/**
* 新增角色信息
*
* @param role 角色信息
* @return 结果
*/
public int insertRole(SysRole role);
<insert id="insertRole" parameterType="SysRole" useGeneratedKeys="true" keyProperty="roleId">
insert into sys_role(
<if test="roleId != null and roleId != 0">role_id,</if>
<if test="roleName != null and roleName != ''">role_name,</if>
<if test="roleKey != null and roleKey != ''">role_key,</if>
<if test="roleSort != null and roleSort != ''">role_sort,</if>
<if test="dataScope != null and dataScope != ''">data_scope,</if>
<if test="status != null and status != ''">status,</if>
<if test="remark != null and remark != ''">remark,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
create_time
)values(
/**
* 新增角色菜单信息
*
* @param role 角色对象
*/
//一点校验也没有
public int insertRoleMenu(SysRole role)
{
int rows = 1;
// 新增用户与角色管理
List<SysRoleMenu> list = new ArrayList<SysRoleMenu>();
//获取菜单组绑定
for (Long menuId : role.getMenuIds())
{
//
SysRoleMenu rm = new SysRoleMenu();
rm.setRoleId(role.getRoleId());
rm.setMenuId(menuId);
list.add(rm);
}
if (list.size() > 0)
{
rows = roleMenuMapper.batchRoleMenu(list);
}
return rows;
}
public void setCreateBy(String createBy)
{
this.createBy = createBy;
}
/**
* 获取登录用户名
*/
public String getLoginName()
{
return getSysUser().getLoginName();
}
/**
* 校验角色名称是否唯一
*
* @param role 角色信息
* @return 结果
*/
@Override
public boolean checkRoleNameUnique(SysRole role)
{//获取角色id
Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId();
//角色名
SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName());
//不是空,检查角色名字是否重复,一条查重
if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue())
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
/**
* 校验角色名称是否唯一
*
* @param roleName 角色名称
* @return 角色信息
*/
public SysRole checkRoleNameUnique(String roleName);
Preparing: SELECT count(0) FROM (SELECT DISTINCT r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status, r.del_flag, r.create_time, r.remark FROM sys_role r LEFT JOIN sys_user_role ur ON ur.role_id = r.role_id LEFT JOIN sys_user u ON u.user_id = ur.user_id LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE r.del_flag = '0') table_count
2 查询
POST /system/role/list HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Content-Length: 127
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/role
Cookie: JSESSIONID=abccc535-5474-4884-880d-1b238916ec9d
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
pageSize=10&pageNum=1&orderByColumn=roleSort&isAsc=asc&roleName=1&roleKey=1&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=
@RequiresPermissions("system:role:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(SysRole role)
{
startPage();
//返回部门限制下角色可见
List<SysRole> list = roleService.selectRoleList(role);
return getDataTable(list);
}
/**
* 设置请求分页数据
*/
protected void startPage()
{
PageUtils.startPage();
}
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
@Override
//仅自己操作都操作不了
@DataScope(deptAlias = "d")
public List<SysRole> selectRoleList(SysRole role)
{
return roleMapper.selectRoleList(role);
}
<select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult">
<include refid="selectRoleContactVo"/>
where r.del_flag = '0'
<if test="roleId != null and roleId != 0">
AND r.role_id = #{roleId}
</if>
<if test="roleName != null and roleName != ''">
AND r.role_name like concat('%', #{roleName}, '%')
</if>
<if test="status != null and status != ''">
AND r.status = #{status}
</if>
<if test="roleKey != null and roleKey != ''">
AND r.role_key like concat('%', #{roleKey}, '%')
</if>
<if test="dataScope != null and dataScope != ''">
AND r.data_scope = #{dataScope}
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
and date_format(r.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(r.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
仅自己
23:47:51.430 [http-nio-80-exec-2] DEBUG c.r.s.m.S.selectRolesByUserId - [debug,135] - ==> Preparing: select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status, r.del_flag, r.create_time, r.remark from sys_role r left join sys_user_role ur on ur.role_id = r.role_id left join sys_user u on u.user_id = ur.user_id left join sys_dept d on u.dept_id = d.dept_id WHERE r.del_flag = '0' and ur.user_id = ?
23:47:51.432 [http-nio-80-exec-2] DEBUG c.r.s.m.S.selectRolesByUserId - [debug,135] - ==> Parameters: 104(Long)
23:47:51.437 [http-nio-80-exec-2] DEBUG c.r.s.m.S.selectRolesByUserId - [debug,135] - <== Total: 1
23:47:51.438 [http-nio-80-exec-2] DEBUG c.r.s.m.S.selectPermsByUserId - [debug,135] - ==> Preparing: select distinct m.perms from sys_menu m left join sys_role_menu rm on m.menu_id = rm.menu_id left join sys_user_role ur on rm.role_id = ur.role_id left join sys_role r on r.role_id = ur.role_id where m.visible = '0' and r.status = '0' and ur.user_id = ?
23:47:51.439 [http-nio-80-exec-2] DEBUG c.r.s.m.S.selectPermsByUserId - [debug,135] - ==> Parameters: 104(Long)
23:47:51.443 [http-nio-80-exec-2] DEBUG c.r.s.m.S.selectPermsByUserId - [debug,135] - <== Total: 58
23:47:51.476 [http-nio-80-exec-2] INFO o.a.s.c.e.EhCacheManager - [getCache,169] - Using existing EHCache named [sys-dict]
23:47:51.684 [http-nio-80-exec-13] DEBUG c.r.s.m.S.selectRoleList_COUNT - [debug,135] - ==> Preparing: SELECT count(0) FROM (SELECT DISTINCT r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status, r.del_flag, r.create_time, r.remark FROM sys_role r LEFT JOIN sys_user_role ur ON ur.role_id = r.role_id LEFT JOIN sys_user u ON u.user_id = ur.user_id LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE r.del_flag = '0' AND (d.dept_id = 0)) table_count
23:47:51.684 [http-nio-80-exec-13] DEBUG c.r.s.m.S.selectRoleList_COUNT - [debug,135] - ==> Preparing: SELECT count(0) FROM (SELECT DISTINCT r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status, r.del_flag, r.create_time, r.remark FROM sys_role r LEFT JOIN sys_user_role ur ON ur.role_id = r.role_id LEFT JOIN sys_user u ON u.user_id = ur.user_id LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE r.del_flag = '0' AND (d.dept_id = 0)) table_count
永久为假
3 删除
POST /system/role/remove HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 7
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/role
Cookie: JSESSIONID=2578fd0d-11e6-4a24-b10c-936fd6839dff
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
ids=101
@RequiresPermissions("system:role:remove")
@Log(title = "角色管理", businessType = BusinessType.DELETE)
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
return toAjax(roleService.deleteRoleByIds(ids));
}
/**
* 批量删除角色信息
*
* @param ids 需要删除的数据ID
* @throws Exception
*/
@Override
@Transactional
public int deleteRoleByIds(String ids)
{//删除的角色合集
Long[] roleIds = Convert.toLongArray(ids);
//循环
for (Long roleId : roleIds)
{//不让改管理员角色
checkRoleAllowed(new SysRole(roleId));
//部门关联角色
checkRoleDataScope(roleId);
//
SysRole role = selectRoleById(roleId);
if (countUserRoleByRoleId(roleId) > 0)
{//这里有校验
throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName()));
}
}
// 删除角色与菜单关联
roleMenuMapper.deleteRoleMenu(roleIds);
// 删除角色与部门关联
roleDeptMapper.deleteRoleDept(roleIds);
return roleMapper.deleteRoleByIds(roleIds);
}
**
* 通过角色ID查询角色
*
* @param roleId 角色ID
* @return 角色对象信息
*/
@Override
public SysRole selectRoleById(Long roleId)
{
return roleMapper.selectRoleById(roleId);
}
/**
* 校验角色是否允许操作
*
* @param role 角色信息
*/
@Override
public void checkRoleAllowed(SysRole role)
{
if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin())
{
throw new ServiceException("不允许操作超级管理员角色");
}
}
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色id
*/
@Override
public void checkRoleDataScope(Long... roleIds)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
for (Long roleId : roleIds)
{
SysRole role = new SysRole();
role.setRoleId(roleId);
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
//这里有校验,部门范围绑定
if (StringUtils.isEmpty(roles))
{
throw new ServiceException("没有权限访问角色数据!");
}
}
}
}
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
@Override
@DataScope(deptAlias = "d")
public List<SysRole> selectRoleList(SysRole role)
{
return roleMapper.selectRoleList(role);
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
/**
* 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@RequiresPermissions获取,多个权限用逗号分隔开来
*/
public String permission() default "";
}
<select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult">
<include refid="selectRoleContactVo"/>
where r.del_flag = '0'
<if test="roleId != null and roleId != 0">
AND r.role_id = #{roleId}
</if>
<if test="roleName != null and roleName != ''">
AND r.role_name like concat('%', #{roleName}, '%')
</if>
<if test="status != null and status != ''">
AND r.status = #{status}
</if>
<if test="roleKey != null and roleKey != ''">
AND r.role_key like concat('%', #{roleKey}, '%')
</if>
<if test="dataScope != null and dataScope != ''">
AND r.data_scope = #{dataScope}
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
and date_format(r.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(r.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
4 修改
POST /system/role/edit HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 636
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/role/edit/101
Cookie: JSESSIONID=15ea8b64-665f-4c6d-9d22-6d3e0de5a52d
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
roleId=101&roleName=1222&roleKey=11&roleSort=1&status=0&remark=123&menuIds=1%2C100%2C1000%2C1001%2C1002%2C1003%2C1004%2C1005%2C1006%2C101%2C1007%2C1008%2C1009%2C1010%2C1011%2C102%2C1012%2C1013%2C1014%2C1015%2C103%2C1016%2C1017%2C1018%2C1019%2C104%2C1020%2C1021%2C1022%2C1023%2C1024%2C105%2C1025%2C1026%2C1027%2C1028%2C1029%2C106%2C1030%2C1031%2C1032%2C1033%2C1034%2C107%2C1035%2C1036%2C1037%2C1038%2C108%2C500%2C1039%2C1040%2C1041%2C1042%2C501%2C1043%2C1044%2C1045%2C1046%2C2%2C109%2C1047%2C1048%2C1049%2C110%2C1050%2C1051%2C1052%2C1053%2C1054%2C1055%2C1056%2C111%2C112%2C113%2C3%2C114%2C115%2C1057%2C1058%2C1059%2C1060%2C1061%2C116%2C4

/**
* 修改保存角色
*/
@RequiresPermissions("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(@Validated SysRole role)
{
//不允许修改管理员角色
roleService.checkRoleAllowed(role);
//获取本人角色
roleService.checkRoleDataScope(role.getRoleId());
if (!roleService.checkRoleNameUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
}
else if (!roleService.checkRoleKeyUnique(role))
{
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
//赋值登录用户名
role.setUpdateBy(getLoginName());
//清理缓存
AuthorizationUtils.clearAllCachedAuthorizationInfo();
//没有菜单校验
return toAjax(roleService.updateRole(role));
}
public Long getRoleId()
{
return roleId;
}
/** 角色ID */
@Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
private Long roleId;


<select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult">
<include refid="selectRoleContactVo"/>
where r.del_flag = '0'
<if test="roleId != null and roleId != 0">
AND r.role_id = #{roleId}
</if>
<if test="roleName != null and roleName != ''">
AND r.role_name like concat('%', #{roleName}, '%')
</if>
<if test="status != null and status != ''">
AND r.status = #{status}
</if>
<if test="roleKey != null and roleKey != ''">
AND r.role_key like concat('%', #{roleKey}, '%')
</if>
<if test="dataScope != null and dataScope != ''">
AND r.data_scope = #{dataScope}
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
and date_format(r.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
and date_format(r.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色id
*/
@Override
public void checkRoleDataScope(Long... roleIds)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
//循环
for (Long roleId : roleIds)
{
//获取角色id
SysRole role = new SysRole();
//赋值角色id
role.setRoleId(roleId);
//这里有缺陷,根据部门范围返回角色,检查是否可以更改
//返回用户角色信息
//这里根据用户的部门与权限查询
//角色查询结果
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
//判断,一个一个判断返回是否是空
if (StringUtils.isEmpty(roles))
{
//不让访问
throw new ServiceException("没有权限访问角色数据!");
}
}
}
}
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
@Override
//这里根据部门返回,先校验
@DataScope(deptAlias = "d")
public List<SysRole> selectRoleList(SysRole role)
{
return roleMapper.selectRoleList(role);
}
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
public List<SysRole> selectRoleList(SysRole role);
/**
* 修改保存角色信息
*
* @param role 角色信息
* @return 结果
*/
@Override
@Transactional
public int updateRole(SysRole role)
{
// 修改角色信息
roleMapper.updateRole(role);
// 删除角色与菜单关联
roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId());
return insertRoleMenu(role);
}
/**
* 新增角色菜单信息
*
* @param role 角色对象
*/
public int insertRoleMenu(SysRole role)
{
int rows = 1;
// 新增用户与角色管理
List<SysRoleMenu> list = new ArrayList<SysRoleMenu>();
for (Long menuId : role.getMenuIds())
{
SysRoleMenu rm = new SysRoleMenu();
rm.setRoleId(role.getRoleId());
rm.setMenuId(menuId);
list.add(rm);
}
if (list.size() > 0)
{
rows = roleMenuMapper.batchRoleMenu(list);
}
return rows;
}
5 修改数据权限范围
POST /system/role/authDataScope HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 57
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/role/authDataScope/100
Cookie: JSESSIONID=15ea8b64-665f-4c6d-9d22-6d3e0de5a52d
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
roleId=100&roleName=1234&roleKey=123&dataScope=1&deptIds=
/**
* 校验角色是否允许操作
*
* @param role 角色信息
*/
@Override
public void checkRoleAllowed(SysRole role)
{
if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin())
{
throw new ServiceException("不允许操作超级管理员角色");
}
}
/**
* 保存角色分配数据权限
*/
@RequiresPermissions("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PostMapping("/authDataScope")
@ResponseBody
public AjaxResult authDataScopeSave(SysRole role)
{
roleService.checkRoleAllowed(role);
//检查角色返回
roleService.checkRoleDataScope(role.getRoleId());
//更新名字赋值
role.setUpdateBy(getLoginName());
//存储,一点校验都没有
if (roleService.authDataScope(role) > 0)
{
//返回相应可以查询的用户
setSysUser(userService.selectUserById(getUserId()));
//成功
return success();
}
return error();
}
/**
* 修改数据权限信息
*
* @param role 角色信息
* @return 结果
*/
@Override
@Transactional
public int authDataScope(SysRole role)
{
// 修改角色信息
roleMapper.updateRole(role);
// 删除角色与部门关联
roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId());
// 新增角色和部门信息(数据权限)
return insertRoleDept(role);
}
/**
* 设置用户缓存信息
*/
public void setSysUser(SysUser user)
{
ShiroUtils.setSysUser(user);
}
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
@Override
public SysUser selectUserById(Long userId)
{
return userMapper.selectUserById(userId);
}
/**
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
public SysUser selectUserById(Long userId);
/**
* 校验角色是否允许操作
*
* @param role 角色信息
*/
@Override
public void checkRoleAllowed(SysRole role)
{
if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin())
{
throw new ServiceException("不允许操作超级管理员角色");
}
}
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色id
*/
@Override
public void checkRoleDataScope(Long... roleIds)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
for (Long roleId : roleIds)
{
SysRole role = new SysRole();
role.setRoleId(roleId);
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
if (StringUtils.isEmpty(roles))
{
throw new ServiceException("没有权限访问角色数据!");
}
}
}
}
/**
/**
* 修改数据权限信息
*
* @param role 角色信息
* @return 结果
*/
@Override
@Transactional
public int authDataScope(SysRole role)
{
// 修改角色信息
roleMapper.updateRole(role);
// 删除角色与部门关联
roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId());
// 新增角色和部门信息(数据权限)
return insertRoleDept(role);
}
6 用户状态
POST /system/user/changeStatus HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 19
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/user
Cookie: JSESSIONID=3388ae99-fabd-498b-9e94-e7254b3f8d2d
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
userId=101&status=1
单独处理的接口
/**
* 用户状态修改
*/
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@RequiresPermissions("system:user:edit")
@PostMapping("/changeStatus")
@ResponseBody
public AjaxResult changeStatus(SysUser user)
{//不允许操作管理员
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
//修改用户信息,存储
return toAjax(userService.changeStatus(user));
}
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
@Override
public void checkUserDataScope(Long userId)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
SysUser user = new SysUser();
user.setUserId(userId);
//校验问题
List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
if (StringUtils.isEmpty(users))
{
throw new ServiceException("没有权限访问用户数据!");
}
}
}
/**
* 用户状态修改
*
* @param user 用户信息
* @return 结果
*/
@Override
public int changeStatus(SysUser user)
{
return userMapper.updateUser(user);
}
}
<update id="updateUser" parameterType="SysUser">
update sys_user
<set>
<if test="deptId != null and deptId != 0">dept_id = #{deptId},</if>
<if test="loginName != null and loginName != ''">login_name = #{loginName},</if>
<if test="userName != null and userName != ''">user_name = #{userName},</if>
<if test="userType != null and userType != ''">user_type = #{userType},</if>
<if test="email != null and email != ''">email = #{email},</if>
<if test="phonenumber != null and phonenumber != ''">phonenumber = #{phonenumber},</if>
<if test="sex != null and sex != ''">sex = #{sex},</if>
<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
<if test="password != null and password != ''">password = #{password},</if>
<if test="salt != null and salt != ''">salt = #{salt},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
<if test="loginDate != null">login_date = #{loginDate},</if>
<if test="pwdUpdateDate != null">pwd_update_date = #{pwdUpdateDate},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="remark != null">remark = #{remark},</if>
update_time = sysdate()
</set>
where user_id = #{userId}
</update>
7 分配角色
POST /system/role/authUser/selectAll HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 22
Origin: http://127.0.0.1
Connection: keep-alive
Referer: http://127.0.0.1/system/role/authUser/selectUser/101
Cookie: JSESSIONID=e9a08e64-8695-4079-81aa-a6277f92a1a6
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
roleId=101&userIds=101
/**
* 批量选择用户授权
*/
@RequiresPermissions("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PostMapping("/authUser/selectAll")
@ResponseBody
public AjaxResult selectAuthUserAll(Long roleId, String userIds)
{//这里可以分配管理员角色(没有校验)
//根据部门判断角色是否可以添加
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
/**
* 校验角色是否有数据权限
*
* @param roleIds 角色id
*/
@Override
public void checkRoleDataScope(Long... roleIds)
{//不看角色
if (!SysUser.isAdmin(ShiroUtils.getUserId()))
{
for (Long roleId : roleIds)
{
SysRole role = new SysRole();
role.setRoleId(roleId);
//返回角色信息
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
if (StringUtils.isEmpty(roles))
{
throw new ServiceException("没有权限访问角色数据!");
}
}
}
}
/**
* 批量选择授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要授权的用户数据ID
* @return 结果
*/
@Override
public int insertAuthUsers(Long roleId, String userIds)
{
Long[] users = Convert.toLongArray(userIds);
// 新增用户与角色管理
List<SysUserRole> list = new ArrayList<SysUserRole>();
// 绑定
for (Long userId : users)
{
SysUserRole ur = new SysUserRole();
ur.setUserId(userId);
ur.setRoleId(roleId);
list.add(ur);
}
return userRoleMapper.batchUserRole(list);
}
}
8 批量取消
POST /system/role/authUser/cancelAll HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 20
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/role/authUser/2
Cookie: JSESSIONID=cbfdb206-a299-4f49-b673-35d06e8d0d82
Priority: u=0
roleId=2&userIds=101
/**
* 批量取消授权
*/
@RequiresPermissions("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PostMapping("/authUser/cancelAll")
@ResponseBody
//这里没有校验
public AjaxResult cancelAuthUserAll(Long roleId, String userIds)
{
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
}
/**
* 批量取消授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要删除的用户数据ID
* @return 结果
*/
@Override
public int deleteAuthUsers(Long roleId, String userIds)
{//任意取消授权
return userRoleMapper.deleteUserRoleInfos(roleId, Convert.toLongArray(userIds));
}
/**
* 批量取消授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要删除的用户数据ID
* @return 结果
*/
public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds);
/**
* 转换为Long数组<br>
*
* @param str 被转换的值
* @return 结果
*/
public static Long[] toLongArray(String str)
{
return toLongArray(",", str);
}


0x3 菜单管理
1 添加菜单
POST /system/menu/add HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 193
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/menu/add/0
Cookie: JSESSIONID=fee388db-e2d4-466d-aeb0-bcd68fae6135
Priority: u=0
parentId=0&menuType=C&menuName=1&url=http%3A%2F%2Fwww.baidu.com&target=menuItem&perms=+%40RequiresPermissions(%22system%3Amenu%3Aadd%22)&orderNum=1&icon=fa+fa-address-book&visible=0&isRefresh=1
/**
* 新增保存菜单
*/
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
@RequiresPermissions("system:menu:add")
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysMenu menu)
{//获取名字
if (!menuService.checkMenuNameUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
//添加登录者名字(操作者)
menu.setCreateBy(getLoginName());
//清理缓存
AuthorizationUtils.clearAllCachedAuthorizationInfo();
//保存
return toAjax(menuService.insertMenu(menu));
}
/**
* 校验菜单名称是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public boolean checkMenuNameUnique(SysMenu menu)
{
//判断菜单id是不是空的
Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
//校验名字名称是否唯一(父菜单下)
SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
//返回了值
if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue())
{
//报错
//public final static boolean NOT_UNIQUE = false
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
@NotBlank(message = "菜单名称不能为空")
@Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
public String getMenuName()
{
return menuName;
}
/** 父菜单ID */
private Long parentId;
/**
* 校验菜单名称是否唯一
*
* @param menuName 菜单名称
* @param parentId 父菜单ID
* @return 结果
*/
public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId);
/**
* 新增保存菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public int insertMenu(SysMenu menu)
{
return menuMapper.insertMenu(menu);
}
/**
* 新增菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
public int insertMenu(SysMenu menu);
2 修改菜单
没有任何校验,直接添加(验证成功)
过滤逻辑(可以直接改状态(隐藏))
package com.ruoyi.common.xss;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;
/**
* 防止XSS攻击的过滤器
*
* @author ruoyi
*/
public class XssFilter implements Filter
{
/**
* 排除链接
*/
public List<String> excludes = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] urls = tempExcludes.split(",");
for (String url : urls)
{
excludes.add(url);
}
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (handleExcludeURL(req, resp))
{
chain.doFilter(request, response);
return;
}
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
{
String url = request.getServletPath();
String method = request.getMethod();
// GET DELETE 不过滤
if (method == null || method.matches("GET") || method.matches("DELETE"))
{
return true;
}
return StringUtils.matches(url, excludes);
}
@Override
public void destroy()
{
}
}
POST /system/menu/edit HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 204
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/menu/edit/1063
Cookie: JSESSIONID=fee388db-e2d4-466d-aeb0-bcd68fae6135
Priority: u=0
menuId=1063&parentId=0&menuType=F&menuName=1&url=http%3A%2F%2Fwww.baidu.com&target=menuItem&perms=%40RequiresPermissions(%22system%3Amenu%3Aadd%22)&orderNum=1&icon=fa+fa-address-book&visible=0&isRefresh=1
/**
* 修改保存菜单
*/
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
@RequiresPermissions("system:menu:edit")
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(@Validated SysMenu menu)
{
if (!menuService.checkMenuNameUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
//更新者
menu.setUpdateBy(getLoginName());
AuthorizationUtils.clearAllCachedAuthorizationInfo();
return toAjax(menuService.updateMenu(menu));
}
/**
* 清理所有用户授权信息缓存
*/
public static void clearAllCachedAuthorizationInfo()
{
getUserRealm().clearAllCachedAuthorizationInfo();
}
/**
* 校验菜单名称是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public boolean checkMenuNameUnique(SysMenu menu)
{
Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue())
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
/**
* 校验菜单名称是否唯一
*
* @param menuName 菜单名称
* @param parentId 父菜单ID
* @return 结果
*/
public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId);
}
/**
* 修改保存菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public int updateMenu(SysMenu menu)
{
return menuMapper.updateMenu(menu);
}
/**
* 修改菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
public int updateMenu(SysMenu menu);
<update id="updateMenu" parameterType="SysMenu">
update sys_menu
<set>
<if test="menuName != null and menuName != ''">menu_name = #{menuName},</if>
<if test="parentId != null and parentId != 0">parent_id = #{parentId},</if>
<if test="orderNum != null">order_num = #{orderNum},</if>
<if test="url != null">url = #{url},</if>
<if test="target != null and target != ''">target = #{target},</if>
<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>
<if test="visible != null">visible = #{visible},</if>
<if test="isRefresh != null">is_refresh = #{isRefresh},</if>
<if test="perms !=null">perms = #{perms},</if>
<if test="icon !=null and icon != ''">icon = #{icon},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
update_time = sysdate()
</set>
where menu_id = #{menuId}
</update>
3 寻找

POST /system/menu/list HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 21
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/menu
Cookie: JSESSIONID=c0f156aa-f905-4596-8c2f-822fcfa68589
Priority: u=0
menuName=111&visible=
@RequiresPermissions("system:menu:list")
@PostMapping("/list")
@ResponseBody
public List<SysMenu> list(SysMenu menu)
{//获取用户序号
Long userId = ShiroUtils.getUserId();
//
List<SysMenu> menuList = menuService.selectMenuList(menu, userId);
return menuList;
}



/**
* 查询菜单集合
*
* @return 所有菜单信息
*/
@Override
public List<SysMenu> selectMenuList(SysMenu menu, Long userId)
{
List<SysMenu> menuList = null;
if (SysUser.isAdmin(userId))
{//返回所有菜单
menuList = menuMapper.selectMenuList(menu);
}
else
{//添加用户
menu.getParams().put("userId", userId);
//返回菜单
menuList = menuMapper.selectMenuListByUserId(menu);
}
return menuList;
}
/**
* 查询系统菜单列表
*
* @param menu 菜单信息
* @return 菜单列表
*/
public List<SysMenu> selectMenuListByUserId(SysMenu menu);
public Map<String, Object> getParams()
{
if (params == null)
{
params = new HashMap<>();
}
return params;
}
<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.url, m.visible, m.is_refresh, ifnull(m.perms,'') as perms, m.target, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_user_role ur on rm.role_id = ur.role_id
LEFT JOIN sys_role ro on ur.role_id = ro.role_id
where ur.user_id = #{params.userId}
<if test="menuName != null and menuName != ''">
AND m.menu_name like concat('%', #{menuName}, '%')
</if>
<if test="visible != null and visible != ''">
AND m.visible = #{visible}
</if>
order by m.parent_id, m.order_num
</select>
4 删除
GET /system/menu/remove/1067 HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Connection: keep-alive
Referer: http://cctv/system/menu
Cookie: JSESSIONID=873b94f2-4178-48a4-8084-14fd7826997b
Priority: u=0
/**
* 删除菜单
*/
@Log(title = "菜单管理", businessType = BusinessType.DELETE)
@RequiresPermissions("system:menu:remove")
@GetMapping("/remove/{menuId}")
@ResponseBody
public AjaxResult remove(@PathVariable("menuId") Long menuId)
{//查询父菜单
if (menuService.selectCountMenuByParentId(menuId) > 0)
{
return AjaxResult.warn("存在子菜单,不允许删除");
}
if (menuService.selectCountRoleMenuByMenuId(menuId) > 0)
{
return AjaxResult.warn("菜单已分配,不允许删除");
}
AuthorizationUtils.clearAllCachedAuthorizationInfo();
return toAjax(menuService.deleteMenuById(menuId));
}
/**
* 查询子菜单数量
*
* @param parentId 父级菜单ID
* @return 结果
*/
@Override
public int selectCountMenuByParentId(Long parentId)
{
return menuMapper.selectCountMenuByParentId(parentId);
}
**
* 查询菜单数量
*
* @param parentId 菜单父ID
* @return 结果
*/
public int selectCountMenuByParentId(Long parentId)
/**
* 查询菜单使用数量
*
* @param menuId 菜单ID
* @return 结果
*/
@Override
public int selectCountRoleMenuByMenuId(Long menuId)
{
return roleMenuMapper.selectCountRoleMenuByMenuId(menuId);
}
0x4 部门管理
1 删除

GET /system/dept/remove/108 HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Connection: keep-alive
Referer: http://cctv/system/dept
Cookie: JSESSIONID=751cab31-3108-4f44-8573-014e7bb9a223
Priority: u=0
/**
* 删除
*/
@Log(title = "部门管理", businessType = BusinessType.DELETE)
@RequiresPermissions("system:dept:remove")
@GetMapping("/remove/{deptId}")
@ResponseBody
public AjaxResult remove(@PathVariable("deptId") Long deptId)
{//查询父目录有没有部门
if (deptService.selectDeptCount(deptId) > 0)
{
return AjaxResult.warn("存在下级部门,不允许删除");
}
if (deptService.checkDeptExistUser(deptId))
{
return AjaxResult.warn("部门存在用户,不允许删除");
}
//
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));
}
/**
* 校验部门是否有数据权限
*
* @param deptId 部门id
*/
@Override
public void checkDeptDataScope(Long deptId)
{
if (!SysUser.isAdmin(ShiroUtils.getUserId()) && StringUtils.isNotNull(deptId))
{
SysDept dept = new SysDept();
dept.setDeptId(deptId);
//根据数据权限用户部门限制返回部门菜单(验证成功)
List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
//如果是空的
if (StringUtils.isEmpty(depts))
{
throw new ServiceException("没有权限访问部门数据!");
}
}
}
}
/**
* 查询部门管理数据
*
* @param dept 部门信息
* @return 部门信息集合
*/
@Override
@DataScope(deptAlias = "d")
public List<SysDept> selectDeptList(SysDept dept)
{
return deptMapper.selectDeptList(dept);
}
/**
* 根据父部门ID查询下级部门数量
*
* @param parentId 部门ID
* @return 结果
*/
@Override
public int selectDeptCount(Long parentId)
{
SysDept dept = new SysDept();
//
dept.setParentId(parentId);
return deptMapper.selectDeptCount(dept);
}
/**
* 查询下级部门数量
*
* @param dept 部门信息
* @return 结果
*/
public int selectDeptCount(SysDept dept);
public void setParentId(Long parentId)
{
this.parentId = parentId;
}
/** 父部门ID */
private Long parentId;
/**
* 查询部门是否存在用户
*
* @param deptId 部门ID
* @return 结果 true 存在 false 不存在
*/
@Override
public boolean checkDeptExistUser(Long deptId)
{
int result = deptMapper.checkDeptExistUser(deptId);
return result > 0;
}
2 编辑
POST /system/menu/edit HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 195
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/menu/edit/113
Cookie: JSESSIONID=9aba2d58-7edd-4eff-bec5-c2ea66acc064
Priority: u=0
menuId=113&parentId=2&menuType=C&menuName=%E7%BC%93%E5%AD%98%E7%9B%91%E6%8E%A7&url=%2Fmonitor%2Fcache&target=menuItem&perms=monitor%3Acache%3Aview&orderNum=5&icon=fa+fa-cube&visible=0&isRefresh=1
/**
* 修改保存菜单
*/
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
@RequiresPermissions("system:menu:edit")
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(@Validated SysMenu menu)
{
if (!menuService.checkMenuNameUnique(menu))
{
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
menu.setUpdateBy(getLoginName());
AuthorizationUtils.clearAllCachedAuthorizationInfo();
return toAjax(menuService.updateMenu(menu));
}
@Override
public int updateMenu(SysMenu menu)
{
return menuMapper.updateMenu(menu);
}
/**
* 修改菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
public int updateMenu(SysMenu menu);
3 添加
POST /system/menu/add HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 99
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/menu/add/3
Cookie: JSESSIONID=9aba2d58-7edd-4eff-bec5-c2ea66acc064
Priority: u=0
parentId=3&menuType=C&menuName=1&url=&target=menuItem&perms=&orderNum=1&icon=&visible=0&isRefresh=1
/**
* 新增保存菜单
*/
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
@RequiresPermissions("system:menu:add")
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysMenu menu)
{
if (!menuService.checkMenuNameUnique(menu))
{
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
}
menu.setCreateBy(getLoginName());
AuthorizationUtils.clearAllCachedAuthorizationInfo();
//添加菜单
return toAjax(menuService.insertMenu(menu));
}
/**
* 新增保存菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public int insertMenu(SysMenu menu)
{
return menuMapper.insertMenu(menu);
}
/**
* 新增菜单信息
*
* @param menu 菜单信息
* @return 结果
*/
public int insertMenu(SysMenu menu);
/**
* 校验菜单名称是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
@Override
public boolean checkMenuNameUnique(SysMenu menu)
{
Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue())
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
4 查找
POST /system/dept/list HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 21
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/dept
Cookie: JSESSIONID=8de25305-f225-4d8d-a8fa-9aa991f67928
Priority: u=0
deptName=1111&status=
@RequiresPermissions("system:dept:list")
@PostMapping("/list")
@ResponseBody
public List<SysDept> list(SysDept dept)
{//根据所在部门返回信息
List<SysDept> deptList = deptService.selectDeptList(dept);
return deptList;
}
/**
* 查询部门管理数据
*
* @param dept 部门信息
* @return 部门信息集合
*/
@Override
@DataScope(deptAlias = "d")
public List<SysDept> selectDeptList(SysDept dept)
{
return deptMapper.selectDeptList(dept);
}
/**
* 查询部门管理数据
*
* @param dept 部门信息
* @return 部门信息集合
*/
public List<SysDept> selectDeptList(SysDept dept);
<select id="selectDeptList" parameterType="SysDept" resultMap="SysDeptResult">
<include refid="selectDeptVo"/>
where d.del_flag = '0'
<if test="deptId != null and deptId != 0">
AND dept_id = #{deptId}
</if>
<if test="parentId != null and parentId != 0">
AND parent_id = #{parentId}
</if>
<if test="deptName != null and deptName != ''">
AND dept_name like concat('%', #{deptName}, '%')
</if>
<if test="status != null and status != ''">
AND status = #{status}
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
order by d.parent_id, d.order_num
</select>
0x5 岗位管理
1 添加
POST /system/post/add HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 49
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/post/add
Cookie: JSESSIONID=8de25305-f225-4d8d-a8fa-9aa991f67928
Priority: u=0
postName=1&postCode=1&postSort=1&status=0&remark=
/**
* 新增保存岗位
*/
@RequiresPermissions("system:post:add")
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysPost post)
{
if (!postService.checkPostNameUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (!postService.checkPostCodeUnique(post))
{
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setCreateBy(getLoginName());
return toAjax(postService.insertPost(post));
}
/**
* 校验岗位名称是否唯一
*
* @param post 岗位信息
* @return 结果
*/
@Override
public boolean checkPostNameUnique(SysPost post)
{
Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId();
SysPost info = postMapper.checkPostNameUnique(post.getPostName());
if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue())
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
/**
* 新增保存岗位信息
*
* @param post 岗位信息
* @return 结果
*/
@Override
public int insertPost(SysPost post)
{
return postMapper.insertPost(post);
}
2 修改
POST /system/post/edit HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 94
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/post/edit/2
Cookie: JSESSIONID=8de25305-f225-4d8d-a8fa-9aa991f67928
Priority: u=0
postId=2&postName=%E9%A1%B9%E7%9B%AE%E7%BB%8F%E7%90%86&postCode=se&postSort=2&status=0&remark=
@RequiresPermissions("system:post:edit")
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(@Validated SysPost post)
{
if (!postService.checkPostNameUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
}
else if (!postService.checkPostCodeUnique(post))
{
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setUpdateBy(getLoginName());
return toAjax(postService.updatePost(post));
}
/**
* 修改保存岗位信息
*
* @param post 岗位信息
* @return 结果
*/
@Override
public int updatePost(SysPost post)
{
return postMapper.updatePost(post);
}
3 删除
POST /system/post/remove HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 5
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/post
Cookie: JSESSIONID=8de25305-f225-4d8d-a8fa-9aa991f67928
Priority: u=0
ids=5
@RequiresPermissions("system:post:remove")
@Log(title = "岗位管理", businessType = BusinessType.DELETE)
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
try
{
return toAjax(postService.deletePostByIds(ids));
}
catch (Exception e)
{
return error(e.getMessage());
}
}
/**
* 批量删除岗位信息
*
* @param ids 需要删除的数据ID
* @return 结果
*/
@Override
public int deletePostByIds(String ids)
{//数组
Long[] postIds = Convert.toLongArray(ids);
//遍历
for (Long postId : postIds)
{
//岗位查询用户
SysPost post = selectPostById(postId);
if (countUserPostById(postId) > 0)
{
throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName()));
}
}
return postMapper.deletePostByIds(postIds);
}
/**
* 通过岗位ID查询岗位信息
*
* @param postId 岗位ID
* @return 角色对象信息
*/
public SysPost selectPostById(Long postId);
/**
* 通过岗位ID查询岗位信息
*
* @param postId 岗位ID
* @return 角色对象信息
*/
@Override
public SysPost selectPostById(Long postId)
{
return postMapper.selectPostById(postId);
}
/**
* 转换为Long数组<br>
*
* @param str 被转换的值
* @return 结果
*/
public static Long[] toLongArray(String str)
{
return toLongArray(",", str);
}
雷同过多,不再赘述,之后研究前端渲染。
0x6 参数设置
1 删除
POST /system/config/remove HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 6
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/config
Cookie: JSESSIONID=800a3a9e-1f06-453f-be34-797f2e2ff5b9
Priority: u=0
ids=10
/**
* 删除参数配置
*/
@RequiresPermissions("system:config:remove")
@Log(title = "参数管理", businessType = BusinessType.DELETE)
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
configService.deleteConfigByIds(ids);
return success();
}
/**
* 批量删除参数配置对象
*
* @param ids 需要删除的数据ID
*/
@Override
public void deleteConfigByIds(String ids)
{
Long[] configIds = Convert.toLongArray(ids);
for (Long configId : configIds)
{
SysConfig config = selectConfigById(configId);
//标识判断
if (StringUtils.equals(UserConstants.YES, config.getConfigType()))
{
throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey()));
}
//删除
configMapper.deleteConfigById(configId);
//缓存删除
//前面缓存名,后面建
CacheUtils.remove(getCacheName(), getCacheKey(config.getConfigKey()));
}
}
/**
/**
* 从缓存中移除
*
* @param cacheName
* @param key
*/
public static void remove(String cacheName, String key)
{
getCache(cacheName).remove(getKey(key));
}
/**
* 设置cache key
*
* @param configKey 参数键
* @return 缓存键key
*/
private String getCacheKey(String configKey)
{
return Constants.SYS_CONFIG_KEY + configKey;
}
}
public static final String SYS_CONFIG_KEY = "sys_config:";
public String getConfigType()
{
return configType;
}
/** 系统内置(Y是 N否) */
@Excel(name = "系统内置", readConverterExp = "Y=是,N=否")
private String configType;
/**
* 删除参数配置
*
* @param configId 参数主键
* @return 结果
*/
public int deleteConfigById(Long configId);
2 修改
POST /system/config/edit HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 383
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/config/edit/1
Cookie: JSESSIONID=b2662bdb-9721-4ae3-9f3f-836605194a46
Priority: u=0
configId=1&configName=%E4%B8%BB%E6%A1%86%E6%9E%B6%E9%A1%B5-%E9%BB%98%E8%AE%A4%E7%9A%AE%E8%82%A4%E6%A0%B7%E5%BC%8F%E5%90%8D%E7%A7%B0&configKey=sys.index.skinName&configValue=skin-blue&configType=Y&remark=%E8%93%9D%E8%89%B2+skin-blue%E3%80%81%E7%BB%BF%E8%89%B2+skin-green%E3%80%81%E7%B4%AB%E8%89%B2+skin-purple%E3%80%81%E7%BA%A2%E8%89%B2+skin-red%E3%80%81%E9%BB%84%E8%89%B2+ski
@RequiresPermissions("system:config:edit")
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
public AjaxResult editSave(@Validated SysConfig config)
{
if (!configService.checkConfigKeyUnique(config))
{
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setUpdateBy(getLoginName());
return toAjax(configService.updateConfig(config));
}
@Override
public boolean checkConfigKeyUnique(SysConfig config)
{
Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId();
SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey());
if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue())
{
return UserConstants.NOT_UNIQUE;
}
return UserConstants.UNIQUE;
}
雷同过多,不再赘述,之后研究前端渲染。
0x7 通知公告
1 添加
POST /system/notice/add HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 84
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/system/notice/add
Cookie: JSESSIONID=b2662bdb-9721-4ae3-9f3f-836605194a46
Priority: u=0
noticeTitle=1111¬iceType=1¬iceContent=%3Cp%3E11111111111111%3C%2Fp%3E&status=0
单看这里没有任何过滤,不知道是哪里触发了过滤。
/**
* 新增保存公告
*/
@RequiresPermissions("system:notice:add")
@Log(title = "通知公告", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysNotice notice)
{
notice.setCreateBy(getLoginName());
return toAjax(noticeService.insertNotice(notice));
}
**
* 新增公告
*
* @param notice 公告信息
* @return 结果
*/
@Override
public int insertNotice(SysNotice notice)
{
return noticeMapper.insertNotice(notice);
}
<insert id="insertNotice" parameterType="SysNotice">
insert into sys_notice (
<if test="noticeTitle != null and noticeTitle != '' ">notice_title, </if>
<if test="noticeType != null and noticeType != '' ">notice_type, </if>
<if test="noticeContent != null and noticeContent != '' ">notice_content, </if>
<if test="status != null and status != '' ">status, </if>
<if test="remark != null and remark != ''">remark,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
create_time
)values(
<if test="noticeTitle != null and noticeTitle != ''">#{noticeTitle}, </if>
<if test="noticeType != null and noticeType != ''">#{noticeType}, </if>
<if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if>
<if test="status != null and status != ''">#{status}, </if>
<if test="remark != null and remark != ''">#{remark},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
sysdate()
)
</insert>
渲染

GET /monitor/operlog/detail/400 HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://cctv/
Cookie: JSESSIONID=b2662bdb-9721-4ae3-9f3f-836605194a46
Upgrade-Insecure-Requests: 1
Priority: u=4
@RequiresPermissions("monitor:operlog:detail")
@GetMapping("/detail/{operId}")
public String detail(@PathVariable("operId") Long operId, ModelMap mmap)
{
mmap.put("operLog", operLogService.selectOperLogById(operId));
return prefix + "/detail";
}
**
* 查询操作日志详细
*
* @param operId 操作ID
* @return 操作日志对象
*/
@Override
public SysOperLog selectOperLogById(Long operId)
{
return operLogMapper.selectOperLogById(operId);
}
**
* 查询操作日志详细
*
* @param operId 操作ID
* @return 操作日志对象
*/
public SysOperLog selectOperLogById(Long operId);
public class BindingAwareModelMap extends ExtendedModelMap {
public Object put(String key, @Nullable Object value) {
this.removeBindingResultIfNecessary(key, value);
return super.put(key, value);
}
0x8 系统监控
POST /monitor/online/batchForceLogout HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 40
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/monitor/online
Cookie: JSESSIONID=b2662bdb-9721-4ae3-9f3f-836605194a46
Priority: u=0
ids=b2662bdb-9721-4ae3-9f3f-836605194a46

@RequiresPermissions(value = { "monitor:online:batchForceLogout", "monitor:online:forceLogout" }, logical = Logical.OR)
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@PostMapping("/batchForceLogout")
@ResponseBody
public AjaxResult batchForceLogout(String ids)
{
for (String sessionId : Convert.toStrArray(ids))
{
//数据库查询
SysUserOnline online = userOnlineService.selectOnlineById(sessionId);
if (online == null)
{
return error("用户已下线");
}
//获取会话寻找读取缓存会话,字段查询缓存,然后数据库信息返回
OnlineSession onlineSession = (OnlineSession) onlineSessionDAO.readSession(online.getSessionId());
//会话是空的,或者数据库是空
if (onlineSession == null)
{
return error("用户已下线");
}
//检查当前用户
if (sessionId.equals(ShiroUtils.getSessionId()))
{
return error("当前登录用户无法强退");
}
//删除会话
onlineSessionDAO.delete(onlineSession);
//状态标识关闭
online.setStatus(OnlineStatus.off_line);
//保存会话信息
userOnlineService.saveOnline(online);
//清理缓存
userOnlineService.removeUserCache(online.getLoginName(), sessionId);
}
return success();
}
}
/**
* 保存会话信息
*
* @param online 会话信息
*/
@Override
public void saveOnline(SysUserOnline online)
{
userOnlineDao.saveOnline(online);
}
/**
* 保存会话信息
*
* @param online 会话信息
* @return 结果
*/
public int saveOnline(SysUserOnline online);
<insert id="saveOnline" parameterType="SysUserOnline">
replace into sys_user_online(sessionId, login_name, dept_name, ipaddr, login_location, browser, os, status, start_timestamp, last_access_time, expire_time)
values (#{sessionId}, #{loginName}, #{deptName}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{status}, #{startTimestamp}, #{lastAccessTime}, #{expireTime})
</insert>


/**
* 清理用户缓存
*
* @param loginName 登录名称
* @param sessionId 会话ID
*/
@Override
public void removeUserCache(String loginName, String sessionId)
{
EhCacheManager ehCacheManager = SpringUtils.getBean(EhCacheManager.class);
Cache<String, Deque<Serializable>> cache = ehCacheManager.getCache(ShiroConstants.SYS_USERCACHE);
Deque<Serializable> deque = cache.get(loginName);
if (StringUtils.isEmpty(deque) || deque.size() == 0)
{
return;
}
deque.remove(sessionId);
}
public V get(K key) throws CacheException {
try {
if (log.isTraceEnabled()) {
log.trace("Getting object from cache [" + this.cache.getName() + "] for key [" + key + "]");
}
if (key == null) {
return null;
} else {
Element element = this.cache.get(key);
if (element == null) {
if (log.isTraceEnabled()) {
log.trace("Element for [" + key + "] is null.");
}
return null;
} else {
return (V)element.getObjectValue();
}
}
} catch (Throwable t) {
throw new CacheException(t);
}
}
public final Object getObjectValue() {
return this.value;
}
private final Object value;
/** 在线状态 */
private OnlineStatus status = OnlineStatus.on_line;
/** 用户状态 */
on_line("在线"), off_line("离线");



public void delete(Session session) {
//获取会话
this.uncache(session);
//删除sql
this.doDelete(session);
}
public V remove(K key) throws CacheException {
if (log.isTraceEnabled()) {
log.trace("Removing object from cache [" + this.cache.getName() + "] for key [" + key + "]");
}
try {
V previous = (V)this.get(key);
this.cache.remove(key);
return previous;
} catch (Throwable t) {
throw new CacheException(t);
}
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
//判断是不是空
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}

/**
* 当会话过期/停止(如用户退出时)属性等会调用
*/
@Override
protected void doDelete(Session session)
{
OnlineSession onlineSession = (OnlineSession) session;
if (null == onlineSession)
{
return;
}
onlineSession.setStatus(OnlineStatus.off_line);
//删除sql会话
sysShiroService.deleteSession(onlineSession);
}
}
/**
* 删除会话
*
* @param onlineSession 会话信息
*/
public void deleteSession(OnlineSession onlineSession)
{
onlineService.deleteOnlineById(String.valueOf(onlineSession.getId()));
}
/**
* 通过会话序号删除信息
*
* @param sessionId 会话ID
* @return 在线用户信息
*/
@Override
public void deleteOnlineById(String sessionId)
{
SysUserOnline userOnline = selectOnlineById(sessionId);
if (StringUtils.isNotNull(userOnline))
{
//删除会话
userOnlineDao.deleteOnlineById(sessionId);
}
}
/**
* 通过会话序号删除信息
*
* @param sessionId 会话ID
* @return 在线用户信息
*/
public int deleteOnlineById(String sessionId);
com.ruoyi.framework.shiro.session.OnlineSession@4fc89058[
userId=1
loginName=admin
deptName=研发部门
avatar=
host=127.0.0.1
browser=Firefox 15
os=Windows 10
status=on_line
attributeChanged=false
]

public static String getSessionId()
{
return String.valueOf(getSubject().getSession().getId());
}
public Session readSession(Serializable sessionId) throws UnknownSessionException {
//按键从缓存取出
Session s = this.getCachedSession(sessionId);
if (s == null) {
//数据库获取加进去
s = super.readSession(sessionId);
}
return s;
}
protected Session getCachedSession(Serializable sessionId) {
Session cached = null;
if (sessionId != null) {
Cache<Serializable, Session> cache = this.getActiveSessionsCacheLazy();
if (cache != null) {
cached = this.getCachedSession(sessionId, cache);
}
}
return cached;
}
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = this.doReadSession(sessionId);
if (s == null) {
throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
} else {
return s;
}
}
@Override
protected Session doReadSession(Serializable sessionId)
{
return sysShiroService.getSession(sessionId);
}
public Session getSession(Serializable sessionId)
{
SysUserOnline userOnline = onlineService.selectOnlineById(String.valueOf(sessionId));
return StringUtils.isNull(userOnline) ? null : createSession(userOnline);
}
public Session createSession(SysUserOnline userOnline)
{//数据库获取值装进去
OnlineSession onlineSession = new OnlineSession();
if (StringUtils.isNotNull(userOnline))
{
onlineSession.setId(userOnline.getSessionId());
onlineSession.setHost(userOnline.getIpaddr());
onlineSession.setBrowser(userOnline.getBrowser());
onlineSession.setOs(userOnline.getOs());
onlineSession.setDeptName(userOnline.getDeptName());
onlineSession.setLoginName(userOnline.getLoginName());
onlineSession.setStartTimestamp(userOnline.getStartTimestamp());
onlineSession.setLastAccessTime(userOnline.getLastAccessTime());
onlineSession.setTimeout(userOnline.getExpireTime());
}
return onlineSession;
}
}
/**
* 通过会话序号查询信息
*
* @param sessionId 会话ID
* @return 在线用户信息
*/
@Override
public SysUserOnline selectOnlineById(String sessionId)
{
return userOnlineDao.selectOnlineById(sessionId);
}
/**
* 通过会话序号查询信息
*
* @param sessionId 会话ID
* @return 在线用户信息
*/
public SysUserOnline selectOnlineById(String sessionId);
<select id="selectOnlineById" parameterType="String" resultMap="SysUserOnlineResult">
<include refid="selectOnlineVo"/>
where sessionId = #{sessionId}
</select>
private Cache<Serializable, Session> getActiveSessionsCacheLazy() {
if (this.activeSessions == null) {
this.activeSessions = this.createActiveSessionsCache();
}
return this.activeSessions;
}


protected Cache<Serializable, Session> createActiveSessionsCache() {
Cache<Serializable, Session> cache = null;
//获取缓存信息
CacheManager mgr = this.getCacheManager();
if (mgr != null) {
//获取固定值
String name = this.getActiveSessionsCacheName();
//获取cache,从缓存按名字取出值
cache = mgr.getCache(name);
}
return cache;
}
/**
* 获得一个Cache,没有则显示日志。
*
* @param cacheName
* @return
*/
public static Cache<String, Object> getCache(String cacheName)
{
Cache<String, Object> cache = cacheManager.getCache(cacheName);
if (cache == null)
{
throw new RuntimeException("当前系统中没有定义“" + cacheName + "”这个缓存。");
}
return cache;
}
public CacheManager getCacheManager() {
return this.cacheManager;
}
public String getActiveSessionsCacheName() {
return this.activeSessionsCacheName;
}
private String activeSessionsCacheName = "shiro-activeSessionCache";
private CacheManager cacheManager;
执行一次
POST /monitor/job/run HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 7
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/monitor/job
Cookie: JSESSIONID=b7967196-7fe5-4c27-9087-95d6b9328c2b
Priority: u=0
jobId=1
/**
* 任务调度立即执行一次
*/
@Log(title = "定时任务", businessType = BusinessType.UPDATE)
@RequiresPermissions("monitor:job:changeStatus")
@PostMapping("/run")
@ResponseBody
public AjaxResult run(SysJob job) throws SchedulerException
{//直接执行并返回
boolean result = jobService.run(job);
return result ? success() : error("任务不存在或已过期!");
}


/**
* 立即运行任务
*
* @param job 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean run(SysJob job) throws SchedulerException
{
boolean result = false;
Long jobId = job.getJobId();
//
SysJob tmpObj = selectJobById(job.getJobId());
// 参数
JobDataMap dataMap = new JobDataMap();
//
dataMap.put(ScheduleConstants.TASK_PROPERTIES, tmpObj);
JobKey jobKey = ScheduleUtils.getJobKey(jobId, tmpObj.getJobGroup());
if (scheduler.checkExists(jobKey))
{
//
result = true;
scheduler.triggerJob(jobKey, dataMap);
}
return result;
}
public V put(K key, V val) {
this.dirty = true;
return (V)this.map.put(key, val);
}
/**
* 构建任务键对象
*/
public static JobKey getJobKey(Long jobId, String jobGroup)
{
//
return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
}
/**
* 通过调度任务ID查询调度信息
*
* @param jobId 调度任务ID
* @return 调度任务对象信息
*/
@Override
public SysJob selectJobById(Long jobId)
{
return jobMapper.selectJobById(jobId);
}

/**
* 通过调度ID查询调度任务信息
*
* @param jobId 调度ID
* @return 角色对象信息
*/
public SysJob selectJobById(Long jobId);
<select id="selectJobById" parameterType="Long" resultMap="SysJobResult">
<include refid="selectJobVo"/>
where job_id = #{jobId}
</select>
public static JobKey jobKey(String name, String group) {
return new JobKey(name, group);
}
public JobKey(String name, String group) {
super(name, group);
}


public Key(String name, String group) {
if (name == null) {
throw new IllegalArgumentException("Name cannot be null.");
} else {
this.name = name;
if (group != null) {
this.group = group;
} else {
this.group = "DEFAULT";
}
}
}
<select id="selectOnlineById" parameterType="String" resultMap="SysUserOnlineResult">
<include refid="selectOnlineVo"/>
where sessionId = #{sessionId}
</select>
<sql id="selectOnlineVo">
select sessionId, login_name, dept_name, ipaddr, login_location, browser, os, status, start_timestamp, last_access_time, expire_time
from sys_user_online
</sql>

退出登录
GET /logout HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://cctv/index
Cookie: JSESSIONID=801ea6c8-e1e2-4539-a4ba-a41722753c52
Upgrade-Insecure-Requests: 1
Priority: u=0, i
00x4 444
<?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.ruoyi.system.mapper.SysPostMapper">
//· namespace:命名空间,必须与 Mapper 接口的全限定类名(包名+接口名)一致。
//· 作用:MyBatis 通过这个 namespace + SQL 标签的 id 来唯一确定一个 SQL 语句,并绑定到接口的方法上。
//查询结果如何映射到别名SysPost别名,id唯一标识
//类
<resultMap type="SysPost" id="SysPostResult">
//post_id数据库名,代入查询
//别名,映射类
<id property="postId" column="post_id" />
<result property="postCode" column="post_code" />
<result property="postName" column="post_name" />
<result property="postSort" column="post_sort" />
<result property="status" column="status" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
</resultMap>
//
<sql id="selectPostVo">
select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark
from sys_post
</sql>
<select id="selectPostList" parameterType="SysPost" resultMap="SysPostResult">
<include refid="selectPostVo"/>
<where>
<if test="postCode != null and postCode != ''">
AND post_code like concat('%', #{postCode}, '%')
</if>
<if test="status != null and status != ''">
AND status = #{status}
</if>
<if test="postName != null and postName != ''">
AND post_name like concat('%', #{postName}, '%')
</if>
</where>
</select>
<select id="selectPostAll" resultMap="SysPostResult">
<include refid="selectPostVo"/>
</select>
<select id="selectPostsByUserId" parameterType="Long" resultMap="SysPostResult">
SELECT p.post_id, p.post_name, p.post_code
FROM sys_user u
LEFT JOIN sys_user_post up ON u.user_id = up.user_id
LEFT JOIN sys_post p ON up.post_id = p.post_id
WHERE up.user_id = #{userId}
</select>
<select id="selectPostById" parameterType="Long" resultMap="SysPostResult">
<include refid="selectPostVo"/>
where post_id = #{postId}
</select>
<select id="checkPostNameUnique" parameterType="String" resultMap="SysPostResult">
<include refid="selectPostVo"/>
where post_name=#{postName} limit 1
</select>
<select id="checkPostCodeUnique" parameterType="String" resultMap="SysPostResult">
<include refid="selectPostVo"/>
where post_code=#{postCode} limit 1
</select>
<delete id="deletePostByIds" parameterType="Long">
delete from sys_post where post_id in
<foreach collection="array" item="postId" open="(" separator="," close=")">
#{postId}
</foreach>
</delete>
<update id="updatePost" parameterType="SysPost">
update sys_post
<set>
<if test="postCode != null and postCode != ''">post_code = #{postCode},</if>
<if test="postName != null and postName != ''">post_name = #{postName},</if>
<if test="postSort != null and postSort != ''">post_sort = #{postSort},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
update_time = sysdate()
</set>
where post_id = #{postId}
</update>
<insert id="insertPost" parameterType="SysPost" useGeneratedKeys="true" keyProperty="postId">
insert into sys_post(
<if test="postId != null and postId != 0">post_id,</if>
<if test="postCode != null and postCode != ''">post_code,</if>
<if test="postName != null and postName != ''">post_name,</if>
<if test="postSort != null and postSort != ''">post_sort,</if>
<if test="status != null and status != ''">status,</if>
<if test="remark != null and remark != ''">remark,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
create_time
)values(
<if test="postId != null and postId != 0">#{postId},</if>
<if test="postCode != null and postCode != ''">#{postCode},</if>
<if test="postName != null and postName != ''">#{postName},</if>
<if test="postSort != null and postSort != ''">#{postSort},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="remark != null and remark != ''">#{remark},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
sysdate()
)
</insert>
</mapper>
SELECT p.post_id, p.post_name, p.post_code
FROM sys_user u
LEFT JOIN sys_user_post up ON u.user_id = up.user_id
LEFT JOIN sys_post p ON up.post_id = p.post_id
WHERE up.user_id = #{userId}
这里空指针了
00x3 333 绑定分析
/**
* 查询部门管理数据
*
* @param dept 部门信息
* @return 部门信息集合
*/
@Override
@DataScope(deptAlias = "d")
public List<SysDept> selectDeptList(SysDept dept)
{
return deptMapper.selectDeptList(dept);
}
/**
* 查询部门管理树
*
* @param dept 部门信息
* @return 所有部门信息
*/
@Override
@DataScope(deptAlias = "d")
public List<Ztree> selectDeptTree(SysDept dept)
{
List<SysDept> deptList = deptMapper.selectDeptList(dept);
List<Ztree> ztrees = initZtree(deptList);
return ztrees;
}
/**
* 查询部门管理树(排除下级)
*
* @param deptId 部门ID
* @return 所有部门信息
*/
@Override
@DataScope(deptAlias = "d")
public List<Ztree> selectDeptTreeExcludeChild(SysDept dept)
{
Long excludeId = dept.getExcludeId();
List<SysDept> depts = deptMapper.selectDeptList(dept);
if (excludeId.intValue() > 0)
{
depts.removeIf(d -> d.getDeptId().intValue() == excludeId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), excludeId + ""));
}
List<Ztree> ztrees = initZtree(depts);
return ztrees;
}
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
@Override
@DataScope(deptAlias = "d")
//这里有问题
public List<SysRole> selectRoleList(SysRole role)
{
return roleMapper.selectRoleList(role);
}
0x5 过滤器(get时获取过滤)
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice/*
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
哪个请求发送的时候都会先经过过滤器。虽然我不知道他具体配置是怎么生效的。
应该是重写方法,细节不懂,怎么通过配置生效



package com.ruoyi.common.xss;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;
/**
* 防止XSS攻击的过滤器
*
* @author ruoyi
*/
public class XssFilter implements Filter
{
/**
* 排除链接
*/
//这里通过配置(不知道过程获取排除路由)
public List<String> excludes = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] urls = tempExcludes.split(",");
for (String url : urls)
{
excludes.add(url);
}
}
}
//先经过这里(拦截)
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
//
if (handleExcludeURL(req, resp))
{
chain.doFilter(request, response);
return;
}
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
{//获取路由
String url = request.getServletPath();
//获取请求方式
String method = request.getMethod();
// GET DELETE 不过滤
if (method == null || method.matches("GET") || method.matches("DELETE"))
{
return true;
}
return StringUtils.matches(url, excludes);
}
@Override
public void destroy()
{
}
}
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
0x9 定时任务
POST /monitor/job/checkCronExpressionIsValid HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 29
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/monitor/job/cron
Cookie: JSESSIONID=a0e22904-686a-4c1e-b22d-0ad5db459bbd
Priority: u=0
cronExpression=*+*+*+*+*+%3F+

@PostMapping("/checkCronExpressionIsValid")
@ResponseBody
public boolean checkCronExpressionIsValid(SysJob job)
{
return jobService.checkCronExpressionIsValid(job.getCronExpression());
}
@NotBlank(message = "Cron执行表达式不能为空")
@Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
public String getCronExpression()
{
return cronExpression;
}
/** cron执行表达式 */
@Excel(name = "执行表达式 ")
private String cronExpression;
/**
* 校验cron表达式是否有效
*
* @param cronExpression 表达式
* @return 结果
*/
@Override
public boolean checkCronExpressionIsValid(String cronExpression)
{
return CronUtils.isValid(cronExpression);
}
/**
* 返回一个布尔值代表一个给定的Cron表达式的有效性
*
* @param cronExpression Cron表达式
* @return boolean 表达式是否有效
*/
public static boolean isValid(String cronExpression)
{
return CronExpression.isValidExpression(cronExpression);
}

public static boolean isValidExpression(String cronExpression) {
try {
new CronExpression(cronExpression);
return true;
} catch (ParseException var2) {
return false;
}
}
public CronExpression(String cronExpression) throws ParseException {
if (cronExpression == null) {
throw new IllegalArgumentException("cronExpression cannot be null");
} else {
//
this.cronExpression = cronExpression.toUpperCase(Locale.US);
//
this.buildExpression(this.cronExpression);
}
}
protected transient TreeSet<Integer> seconds;
protected transient TreeSet<Integer> minutes;
protected transient TreeSet<Integer> hours;
protected transient TreeSet<Integer> daysOfMonth;
protected transient TreeSet<Integer> months;
protected transient TreeSet<Integer> daysOfWeek;
protected transient TreeSet<Integer> years;
protected transient boolean lastdayOfWeek = false;


protected void buildExpression(String expression) throws ParseException {
this.expressionParsed = true;
try {
if (this.seconds == null) {
this.seconds = new TreeSet();
}
if (this.minutes == null) {
this.minutes = new TreeSet();
}
if (this.hours == null) {
this.hours = new TreeSet();
}
if (this.daysOfMonth == null) {
this.daysOfMonth = new TreeSet();
}
if (this.months == null) {
this.months = new TreeSet();
}
if (this.daysOfWeek == null) {
this.daysOfWeek = new TreeSet();
}
if (this.years == null) {
this.years = new TreeSet();
}
int exprOn = 0;
for(StringTokenizer exprsTok = new StringTokenizer(expression, " \t", false); exprsTok.hasMoreTokens() && exprOn <= 6; ++exprOn) {
String expr = exprsTok.nextToken().trim();
if (exprOn == 3 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {
throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1);
}
if (exprOn == 5 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {
throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1);
}
if (exprOn == 5 && expr.indexOf(35) != -1 && expr.indexOf(35, expr.indexOf(35) + 1) != -1) {
throw new ParseException("Support for specifying multiple \"nth\" days is not implemented.", -1);
}
StringTokenizer vTok = new StringTokenizer(expr, ",");
while(vTok.hasMoreTokens()) {
String v = vTok.nextToken();
this.storeExpressionVals(0, v, exprOn);
}
}
if (exprOn <= 5) {
throw new ParseException("Unexpected end of expression.", expression.length());
} else {
if (exprOn <= 6) {
this.storeExpressionVals(0, "*", 6);
}
TreeSet<Integer> dow = this.getSet(5);
TreeSet<Integer> dom = this.getSet(3);
boolean dayOfMSpec = !dom.contains(NO_SPEC);
boolean dayOfWSpec = !dow.contains(NO_SPEC);
if ((!dayOfMSpec || dayOfWSpec) && (!dayOfWSpec || dayOfMSpec)) {
throw new ParseException("Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0);
}
}
} catch (ParseException pe) {
throw pe;
} catch (Exception e) {
throw new ParseException("Illegal cron expression format (" + e.toString() + ")", 0);
}
}
for(StringTokenizer exprsTok = new StringTokenizer(expression, " \t", false); exprsTok.hasMoreTokens() && exprOn <= 6; ++exprOn) {
String expr = exprsTok.nextToken().trim();
if (exprOn == 3 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {
throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1);
}
if (exprOn == 5 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {
throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1);
}
if (exprOn == 5 && expr.indexOf(35) != -1 && expr.indexOf(35, expr.indexOf(35) + 1) != -1) {
throw new ParseException("Support for specifying multiple \"nth\" days is not implemented.", -1);
}
StringTokenizer vTok = new StringTokenizer(expr, ",");
while(vTok.hasMoreTokens()) {
String v = vTok.nextToken();
this.storeExpressionVals(0, v, exprOn);
}
}
if (exprOn <= 5) {
throw new ParseException("Unexpected end of expression.", expression.length());
} else {
if (exprOn <= 6) {
this.storeExpressionVals(0, "*", 6);
}
TreeSet<Integer> dow = this.getSet(5);
TreeSet<Integer> dom = this.getSet(3);
boolean dayOfMSpec = !dom.contains(NO_SPEC);
boolean dayOfWSpec = !dow.contains(NO_SPEC);
if ((!dayOfMSpec || dayOfWSpec) && (!dayOfWSpec || dayOfMSpec)) {
throw new ParseException("Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0);
}
}
} catch (ParseException pe) {
throw pe;
} catch (Exception e) {
throw new ParseException("Illegal cron expression format (" + e.toString() + ")", 0);
}
}
public String toUpperCase(Locale locale) {
return isLatin1() ? StringLatin1.toUpperCase(this, value, locale)
: StringUTF16.toUpperCase(this, value, locale);
}
public static final Locale US;
public String toUpperCase(Locale locale) {
//
return isLatin1() ? StringLatin1.toUpperCase(this, value, locale)
: StringUTF16.toUpperCase(this, value, locale);
}
boolean isLatin1() {
return COMPACT_STRINGS && coder == LATIN1;
}
@Native static final byte LATIN1 = 0;
static final boolean COMPACT_STRINGS;
static {
COMPACT_STRINGS = true;
}
public static String toUpperCase(String str, byte[] value, Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
int first;
//获取长度
final int len = value.length;
for (first = 0 ; first < len; first++ ) {
//判断是否大于255
int cp = value[first] & 0xff;
boolean notUpperCaseEx = cp >= 'a' && (cp <= 'z' || cp == 0xb5 || (cp >= 0xdf && cp != 0xf7));
if (notUpperCaseEx) {
break;
}
}
if (first == len) {
return str;
}
String lang = locale.getLanguage();
if (lang == "tr" || lang == "az" || lang == "lt") {
return toUpperCaseEx(str, value, first, locale, true);
}
byte[] result = new byte[len];
System.arraycopy(value, 0, result, 0, first); // Just copy the first few
// upperCase characters.
for (int i = first; i < len; i++) {
int cp = CharacterDataLatin1.instance.toUpperCaseEx(value[i] & 0xff);
if (!canEncode(cp)) { // not a latin1 character
return toUpperCaseEx(str, value, first, locale, false);
}
result[i] = (byte)cp;
}
return new String(result, LATIN1);
}
查看
GET /monitor/job/detail/1 HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://cctv/index
Cookie: JSESSIONID=b7967196-7fe5-4c27-9087-95d6b9328c2b
Upgrade-Insecure-Requests: 1
Priority: u=4
@RequiresPermissions("monitor:job:detail")
@GetMapping("/detail/{jobId}")
public String detail(@PathVariable("jobId") Long jobId, ModelMap mmap)
{
//直接放入
mmap.put("name", "job");
//
mmap.put("job", jobService.selectJobById(jobId));
return prefix + "/detail";
}

/**
* 通过调度任务ID查询调度信息
*
* @param jobId 调度任务ID
* @return 调度任务对象信息
*/
@Override
public SysJob selectJobById(Long jobId)
{
return jobMapper.selectJobById(jobId);
}
/**
* 通过调度ID查询调度任务信息
*
* @param jobId 调度ID
* @return 角色对象信息
*/
public SysJob selectJobById(Long jobId);
GET /monitor/jobLog?jobId=3 HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://cctv/index
Cookie: JSESSIONID=b7967196-7fe5-4c27-9087-95d6b9328c2b
Upgrade-Insecure-Requests: 1
Priority: u=4
@RequiresPermissions("monitor:job:view")
@GetMapping()
public String jobLog(@RequestParam(value = "jobId", required = false) Long jobId, ModelMap mmap)
{
if (StringUtils.isNotNull(jobId))
{
SysJob job = jobService.selectJobById(jobId);
//直接放入
mmap.put("job", job);
}
return prefix + "/jobLog";
}
/**
* 通过调度任务ID查询调度信息
*
* @param jobId 调度任务ID
* @return 调度任务对象信息
*/
@Override
public SysJob selectJobById(Long jobId)
{
return jobMapper.selectJobById(jobId);
}

/**
* 通过调度ID查询调度任务信息
*
* @param jobId 调度ID
* @return 角色对象信息
*/
public SysJob selectJobById(Long jobId);
清空
POST /monitor/jobLog/clean HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/monitor/jobLog?jobId=1
Cookie: JSESSIONID=2049ebb1-44db-4291-b3bb-15dba252e4bc
Priority: u=0
Content-Length: 0
@Log(title = "调度日志", businessType = BusinessType.CLEAN)
@RequiresPermissions("monitor:job:remove")
@PostMapping("/clean")
@ResponseBody
public AjaxResult clean()
{
jobLogService.cleanJobLog();
return success();
}
/**
* 删除任务日志
*
* @param jobId 调度日志ID
*/
@Override
public int deleteJobLogById(Long jobId)
{
return jobLogMapper.deleteJobLogById(jobId);
}
/**
* 删除任务日志
*
* @param jobId 调度日志ID
* @return 结果
*/
public int deleteJobLogById(Long jobId);
<delete id="deleteJobLogById" parameterType="Long">
delete from sys_job_log where job_log_id = #{jobLogId}
</delete>
查看
GET /monitor/jobLog/detail/318 HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://cctv/index
Cookie: JSESSIONID=2049ebb1-44db-4291-b3bb-15dba252e4bc
Upgrade-Insecure-Requests: 1
Priority: u=4
@RequiresPermissions("monitor:job:detail")
@GetMapping("/detail/{jobLogId}")
public String detail(@PathVariable("jobLogId") Long jobLogId, ModelMap mmap)
{
mmap.put("name", "jobLog");
mmap.put("jobLog", jobLogService.selectJobLogById(jobLogId));
return prefix + "/detail";
}
增加
POST /monitor/job/add HTTP/1.1
Host: cctv
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 128
Origin: http://cctv
Connection: keep-alive
Referer: http://cctv/monitor/job/add
Cookie: JSESSIONID=2049ebb1-44db-4291-b3bb-15dba252e4bc
Priority: u=0
createBy=admin&jobName=111111&jobGroup=DEFAULT&invokeTarget=1111111&cronExpression=11111111&misfirePolicy=1&concurrent=1&remark=

public static boolean isValidExpression(String cronExpression) {
try {
new CronExpression(cronExpression);
return true;
} catch (ParseException var2) {
return false;
}
}
/**
* 新增保存调度
*/
@Log(title = "定时任务", businessType = BusinessType.INSERT)
@RequiresPermissions("monitor:job:add")
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysJob job) throws SchedulerException, TaskException
{
if (!CronUtils.isValid(job.getCronExpression()))
{
return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
}
else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
}
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
}
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
}
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
}
else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
}
job.setCreateBy(getLoginName());
return toAjax(jobService.insertJob(job));
}
/**
* 新增任务
*
* @param job 调度信息 调度信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertJob(SysJob job) throws SchedulerException, TaskException
{
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
int rows = jobMapper.insertJob(job);
if (rows > 0)
{
ScheduleUtils.createScheduleJob(scheduler, job);
}
return rows;
}
/**
* 创建定时任务
*/
public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException
{
Class<? extends Job> jobClass = getQuartzJobClass(job);
// 构建job信息
Long jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
// 表达式调度构建器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
.withSchedule(cronScheduleBuilder).build();
// 放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
// 判断是否存在
if (scheduler.checkExists(getJobKey(jobId, jobGroup)))
{
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(getJobKey(jobId, jobGroup));
}
// 判断任务是否过期
if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression())))
{
// 执行调度任务
scheduler.scheduleJob(jobDetail, trigger);
}
// 暂停任务
if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
{
scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
}
}
public String getValue()
{
return value;
}
/**
* 暂停
*/
PAUSE("1");
/**
* 新增调度任务信息
*
* @param job 调度任务信息
* @return 结果
*/
public int insertJob(SysJob job);
<insert id="insertJobLog" parameterType="SysJobLog">
insert into sys_job_log(
<if test="jobLogId != null and jobLogId != 0">job_log_id,</if>
<if test="jobName != null and jobName != ''">job_name,</if>
<if test="jobGroup != null and jobGroup != ''">job_group,</if>
<if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
<if test="jobMessage != null and jobMessage != ''">job_message,</if>
<if test="status != null and status != ''">status,</if>
<if test="exceptionInfo != null and exceptionInfo != ''">exception_info,</if>
create_time
)values(
<if test="jobLogId != null and jobLogId != 0">#{jobLogId},</if>
<if test="jobName != null and jobName != ''">#{jobName},</if>
<if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
<if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
<if test="jobMessage != null and jobMessage != ''">#{jobMessage},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="exceptionInfo != null and exceptionInfo != ''">#{exceptionInfo},</if>
sysdate()
)
</insert>
/** 调用目标字符串 */
@Excel(name = "调用目标字符串")
private String invokeTarget;
@NotBlank(message = "调用目标字符串不能为空")
@Size(min = 0, max = 1000, message = "调用目标字符串长度不能超过500个字符")
public String getInvokeTarget()
{
return invokeTarget;
}
@NotBlank(message = "Cron执行表达式不能为空")
@Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
public String getCronExpression()
{
return cronExpression;
}
/** cron执行表达式 */
@Excel(name = "执行表达式 ")
private String cronExpression;
/**
* 返回一个布尔值代表一个给定的Cron表达式的有效性
*
* @param cronExpression Cron表达式
* @return boolean 表达式是否有效
*/
public static boolean isValid(String cronExpression)
{
return CronExpression.isValidExpression(cronExpression);
}
public static boolean isValidExpression(String cronExpression) {
try {
new CronExpression(cronExpression);
return true;
} catch (ParseException var2) {
return false;
}
}
//字符串,关键字
public static boolean containsIgnoreCase(CharSequence str, CharSequence searchStr) {
//如果都不是空
if (str != null && searchStr != null) {
//关键字长度
int len = searchStr.length();
//差
int max = str.length() - len;
//循环
for(int i = 0; i <= max; ++i) {
if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, len)) {
return true;
}
}
return false;
} else {
return false;
}
}
//(str, true, i, searchStr, 0, len),字符串,ture,差,关键字,0,关键字长度
static boolean regionMatches(CharSequence cs, boolean ignoreCase, int thisStart, CharSequence substring, int start, int length) {
if (cs instanceof String && substring instanceof String) {
return ((String)cs).regionMatches(ignoreCase, thisStart, (String)substring, start, length);
} else {
//差
int index1 = thisStart;
//0
int index2 = start;
//关键字长度
int tmpLen = length;
//字符串长度,差
int srcLen = cs.length() - thisStart;
//关键字长度
int otherLen = substring.length() - start;
//条件
if (thisStart >= 0 && start >= 0 && length >= 0) {
if (srcLen >= length && otherLen >= length) {
//循环,关键字长度自减
while(tmpLen-- > 0) {
//返回索引字符,字符串差索引
char c1 = cs.charAt(index1++);
//关键字索引
char c2 = substring.charAt(index2++);
//如果字符不相等
if (c1 != c2) {
//true
if (!ignoreCase) {
return false;
}
//大写
char u1 = Character.toUpperCase(c1);
//大写
char u2 = Character.toUpperCase(c2);
//其核心功能是将字符串中的字母字符转换为小写形式,非字母字符保持不变。
if (u1 != u2 && Character.toLowerCase(u1) != Character.toLowerCase(u2)) {
return false;
}
}
}
//true
return true;
} else {
return false;
}
} else {
return false;
}
}
}
}
public static char toUpperCase(char ch) {
return (char)toUpperCase((int)ch);
}
public static int toUpperCase(int codePoint) {
return CharacterData.of(codePoint).toUpperCase(codePoint);
}
char c1 = 'a';
char upperC1 = Character.toUpperCase(c1);
System.out.println(upperC1); // 输出: A
protected void buildExpression(String expression) throws ParseException {
this.expressionParsed = true;
try {
if (this.seconds == null) {
this.seconds = new TreeSet();
}
if (this.minutes == null) {
this.minutes = new TreeSet();
}
if (this.hours == null) {
this.hours = new TreeSet();
}
if (this.daysOfMonth == null) {
this.daysOfMonth = new TreeSet();
}
if (this.months == null) {
this.months = new TreeSet();
}
if (this.daysOfWeek == null) {
this.daysOfWeek = new TreeSet();
}
if (this.years == null) {
this.years = new TreeSet();
}
int exprOn = 0;
for(StringTokenizer exprsTok = new StringTokenizer(expression, " \t", false); exprsTok.hasMoreTokens() && exprOn <= 6; ++exprOn) {
String expr = exprsTok.nextToken().trim();
if (exprOn == 3 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {
throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1);
}
if (exprOn == 5 && expr.indexOf(76) != -1 && expr.length() > 1 && expr.contains(",")) {
throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1);
}
if (exprOn == 5 && expr.indexOf(35) != -1 && expr.indexOf(35, expr.indexOf(35) + 1) != -1) {
throw new ParseException("Support for specifying multiple \"nth\" days is not implemented.", -1);
}
StringTokenizer vTok = new StringTokenizer(expr, ",");
while(vTok.hasMoreTokens()) {
String v = vTok.nextToken();
this.storeExpressionVals(0, v, exprOn);
}
}
if (exprOn <= 5) {
throw new ParseException("Unexpected end of expression.", expression.length());
} else {
if (exprOn <= 6) {
this.storeExpressionVals(0, "*", 6);
}
TreeSet<Integer> dow = this.getSet(5);
TreeSet<Integer> dom = this.getSet(3);
boolean dayOfMSpec = !dom.contains(NO_SPEC);
boolean dayOfWSpec = !dow.contains(NO_SPEC);
if ((!dayOfMSpec || dayOfWSpec) && (!dayOfWSpec || dayOfMSpec)) {
throw new ParseException("Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0);
}
}
} catch (ParseException pe) {
throw pe;
} catch (Exception e) {
throw new ParseException("Illegal cron expression format (" + e.toString() + ")", 0);
}
}
protected int storeExpressionVals(int pos, String s, int type) throws ParseException {
int incr = 0;
int i = this.skipWhiteSpace(pos, s);
if (i >= s.length()) {
return i;
} else {
char c = s.charAt(i);
if (c >= 'A' && c <= 'Z' && !s.equals("L") && !s.equals("LW") && !s.matches("^L-[0-9]*[W]?")) {
String sub = s.substring(i, i + 3);
int sval = -1;
int eval = -1;
if (type == 4) {
sval = this.getMonthNumber(sub) + 1;
if (sval <= 0) {
throw new ParseException("Invalid Month value: '" + sub + "'", i);
}
if (s.length() > i + 3) {
c = s.charAt(i + 3);
if (c == '-') {
i += 4;
sub = s.substring(i, i + 3);
eval = this.getMonthNumber(sub) + 1;
if (eval <= 0) {
throw new ParseException("Invalid Month value: '" + sub + "'", i);
}
}
}
} else {
if (type != 5) {
throw new ParseException("Illegal characters for this position: '" + sub + "'", i);
}
sval = this.getDayOfWeekNumber(sub);
if (sval < 0) {
throw new ParseException("Invalid Day-of-Week value: '" + sub + "'", i);
}
if (s.length() > i + 3) {
c = s.charAt(i + 3);
if (c == '-') {
i += 4;
sub = s.substring(i, i + 3);
eval = this.getDayOfWeekNumber(sub);
if (eval < 0) {
throw new ParseException("Invalid Day-of-Week value: '" + sub + "'", i);
}
} else if (c == '#') {
try {
i += 4;
this.nthdayOfWeek = Integer.parseInt(s.substring(i));
if (this.nthdayOfWeek < 1 || this.nthdayOfWeek > 5) {
throw new Exception();
}
} catch (Exception var11) {
throw new ParseException("A numeric value between 1 and 5 must follow the '#' option", i);
}
} else if (c == 'L') {
this.lastdayOfWeek = true;
++i;
}
}
}
if (eval != -1) {
incr = 1;
}
this.addToSet(sval, eval, incr, type);
return i + 3;
} else if (c == '?') {
++i;
if (i + 1 < s.length() && s.charAt(i) != ' ' && s.charAt(i + 1) != '\t') {
throw new ParseException("Illegal character after '?': " + s.charAt(i), i);
} else if (type != 5 && type != 3) {
throw new ParseException("'?' can only be specified for Day-of-Month or Day-of-Week.", i);
} else {
if (type == 5 && !this.lastdayOfMonth) {
int val = (Integer)this.daysOfMonth.last();
if (val == 98) {
throw new ParseException("'?' can only be specified for Day-of-Month -OR- Day-of-Week.", i);
}
}
this.addToSet(98, -1, 0, type);
return i;
}
} else if (c != '*' && c != '/') {
if (c == 'L') {
++i;
if (type == 3) {
this.lastdayOfMonth = true;
}
if (type == 5) {
this.addToSet(7, 7, 0, type);
}
if (type == 3 && s.length() > i) {
c = s.charAt(i);
if (c == '-') {
ValueSet vs = this.getValue(0, s, i + 1);
this.lastdayOffset = vs.value;
if (this.lastdayOffset > 30) {
throw new ParseException("Offset from last day must be <= 30", i + 1);
}
i = vs.pos;
}
if (s.length() > i) {
c = s.charAt(i);
if (c == 'W') {
this.nearestWeekday = true;
++i;
}
}
}
return i;
} else if (c >= '0' && c <= '9') {
int val = Integer.parseInt(String.valueOf(c));
++i;
if (i >= s.length()) {
this.addToSet(val, -1, -1, type);
return i;
} else {
c = s.charAt(i);
if (c >= '0' && c <= '9') {
ValueSet vs = this.getValue(val, s, i);
val = vs.value;
i = vs.pos;
}
i = this.checkNext(i, s, val, type);
return i;
}
} else {
throw new ParseException("Unexpected character: " + c, i);
}
} else if (c == '*' && i + 1 >= s.length()) {
this.addToSet(99, -1, incr, type);
return i + 1;
} else if (c != '/' || i + 1 < s.length() && s.charAt(i + 1) != ' ' && s.charAt(i + 1) != '\t') {
if (c == '*') {
++i;
}
c = s.charAt(i);
if (c == '/') {
++i;
if (i >= s.length()) {
throw new ParseException("Unexpected end of string.", i);
}
incr = this.getNumericValue(s, i);
++i;
if (incr > 10) {
++i;
}
this.checkIncrementRange(incr, type, i);
} else {
incr = 1;
}
this.addToSet(99, -1, incr, type);
return i;
} else {
throw new ParseException("'/' must be followed by an integer.", i);
}
}
}
更多推荐




所有评论(0)