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=&params%5BbeginTime%5D=&params%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') &gt;= 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') &lt;= 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=&params%5BbeginTime%5D=&params%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') &gt;= 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') &lt;= 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') &gt;= 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') &lt;= 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') &gt;= 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') &lt;= 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&noticeType=1&noticeContent=%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);
            }
        }
    }

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐