当Mybatis的mapper文件传入的order by 为动态参数说的时候发现排序无法生效:
像下面这样,在choose when中的order by后的参数是用预编译的方式,用的是#号,这样是可以防止sql注入的问题,但是在传入order by参数的时候无法解析:
<select id="feescaleList" resultType="com.hasagei.modules.mebespoke.entity.HasageiMeBespoke" parameterType="com.hasagei.modules.mebespoke.entity.HasageiMeBespoke"> SELECT A.ID AS id, TO_CHAR(A.BESPOKE_DATE,'yyyy-mm-dd') AS bsepokeDate, A.USER_ID AS userId, A.BESPOKE_DATE AS bespokeDate, A.BESPOKE_STATUS bespokeStatus, A.PROJECT_ID AS projectId, A.PERIOD_START AS periodStart, A.PERIOD_END AS periodEnd, A.FEESCALE_MONEY AS feescaleMoney, A.FEESCALE_NAME AS feescaleName, A.PAYMENT_TIME AS paymentTime, A.PAYMENT_MONEY AS paymentMoney, B.IDENTITY_CARD AS identityCard, B.REALNAME AS realname, B.SJ AS sj, B.GZZH AS gzzh, B.PHOTOELECTRIC_CARD AS photoelectricCard, B.SEX AS sex, B.BIRTH_DATE AS birthDate, B.STUDENT_ID AS studentId, B.EXAMINE_NUMBER AS examineNumber, B.DEPARTMENT AS department, B.FACULTY AS faculty, C.MEC_NAME AS mecName FROM TSINGHUA_ME_BESPOKE A LEFT JOIN TSINGHUA_USERINFO B ON A.USER_ID=B.ID LEFT JOIN MEC_ITEM C ON A.PROJECT_ID=C.MEC_NUMBER WHERE 1=1 <if test='bespokeDateGteJ != null and bespokeDateLteJ != null '> <if test='bespokeDateLteJ != "" and bespokeDateLteJ != ""'> AND A.PAYMENT_TIME <![CDATA[<=]]> to_date(#{bespokeDateLteJ,jdbcType=DATE},'yyyy-MM-dd HH24:MI:SS') AND A.PAYMENT_TIME <![CDATA[>=]]> to_date(#{bespokeDateGteJ,jdbcType=DATE},'yyyy-MM-dd HH24:MI:SS') </if> </if> <choose> <when test='orderByFiled!=null and orderByFiled!="" and orderBySe!=null and orderBySe!=""'> ORDER BY #{orderByFiled} #{orderBySe} </when> <otherwise> ORDER BY A.PAYMENT_TIME DESC </otherwise> </choose> </select>
最简单的解决办法是将#换为$,但是这样会有sql注入的问题
参数符号 | 编译 | 安全 | 值 |
#{} | 预编译 | 安全 | 处理后的值,字符类型都带双引号 |
${} | 未预编译 | 不安全,存在SQL注入问题 | 传进来啥就是啥 |
order by 后面参数值是表字段或者SQL关键字
所以使用#{} 是无效的,只能使用${}
那么SQL注入问题就来了
1、使用正则表达式规避
特殊字符 * + - / _ 等等
使用indexOf判断到了直接返回
有可能会存在其它情况,不能完全规避,但是可以逐步排查
2、技巧解决
组合情况只有这两种
所以我们只要找到对应集合,判断我们动态排序条件是否在其中就可以了
有些勤劳的小伙伴可能就开始写了
userOrderSet = ['id','id desc','age','age desc',.......] scoreOrderSet = ['yuwen','yuwen desc','shuxue','shuxue desc',.......] .............
要是有n多表n多字段可是要急死人
我们使用注解+映射来获取
/** 首先改造实体类,有@Column注解映射,也有使用@JsonProperty,都行,没有用@Column映射,就加个@JsonProperty,不影响,我偷懒使用@JsonProperty **/ @Data @Builder @JsonIgnoreProperties(ignoreUnknown = true) public class ContentEntity { @JsonProperty("id") private Long id;//主键ID @JsonProperty("code") private String code;//编码 @JsonProperty("content") private String content;//内容 @JsonProperty("is_del") private Integer isDel;//是否删除,0未删除,1已删除 @JsonProperty("creator") private String creator;//创建人 @JsonProperty("creator_id") @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN, timezone = "GMT+8") private Date createAt;//创建时间 @JsonProperty("updater") private String updater;//更新人 @JsonProperty("updater_id") @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN, timezone = "GMT+8") private Date updateAt;//更新时间 }
/**工具类来了**/ public class MybatisDynamicOrderUtils { private static final String desc = " desc"; /** * 获取对象 JsonProperty 值列表 使用Column替换一下 * @param object * @return */ public static Set<String> getParamJsonPropertyValue(Class<?> object){ try { //获取filed数组 Set<String> resultList = new HashSet<>(); Field[] fields = object.getDeclaredFields(); for (Field field:fields){ //获取JsonProperty注解 if(field.getAnnotation(JsonProperty.class)!=null){ JsonProperty annotation = field.getAnnotation(JsonProperty.class); if (annotation != null) { //获取JsonProperty 的值 String jsonPropertyValue = annotation.value(); resultList.add(jsonPropertyValue); } } } return resultList; }catch (Exception e){ e.printStackTrace(); } return null; } /** * 判断动态order是否是合理 * @param order * @param object * @return */ public static Boolean isDynamicOrderValue(String order,Class<?> object){ //先获取JsonProperty 注解中的集合 Set<String> set = getParamJsonPropertyValue(object); //属于直属字段 直接返回 if(set.contains(order)){ return true; } //多了倒序,先去除倒序字段再判断 if(order.lastIndexOf(desc)>0){ String temp = order.substring(0,order.lastIndexOf(desc)); if(set.contains(temp)){ return true; } } return false; } }
//调用操作一下 //检验动态order是否合理,防止SQL注入 if(!MybatisDynamicOrderUtils.isDynamicOrderValue(sort,ContentEntity.class)){ log.error("dynamic order is error:{}",sort); return null; } //mapper.class @Select({"<script>select * from content order by ${sort}</script>"}) List<ContentEntity> getList(@Param("sort") String sort);
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
编程 | 2023-02-24 21:36
编程 | 2023-02-21 12:51
编程 | 2023-02-21 12:47
编程 | 2023-02-21 00:15
编程 | 2023-02-21 00:08
编程 | 2023-02-20 21:46
编程 | 2023-02-20 21:42
编程 | 2023-02-20 21:36
编程 | 2023-02-20 21:32
编程 | 2023-02-20 18:12
网友评论