其他
原创 | Mybatis常用注解中的SQL注入
MyBatis3提供了新的基于注解的配置。主要在MapperAnnotationBuilder中,定义了相关的注解:
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
...
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
......
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
@Select @Insert @Update @Delete @SelectProvider @InsertProvider @UpdateProvider @DeleteProvider
@Mapper
public interface UserMapper {
@Select("select * from t_user")
List<User> list();
}
以@SelectProvider 为例,查看具体的实现,主要包含两个注解属性,其中type表示工具类,method 表示工具类的某个方法,用于返回具体的SQL:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface InsertProvider {
// 用于指定获取 sql 语句的指定类
Class<?> type();
// 指定类中要执行获取 sql 语句的方法
String method();
}
List<Integer> getContentByProjectIds(List<Integer> projectIds);
在Mybatis中,#的作用主要是替换预编译语句(PrepareStatement)中的占位符?,$是直接的SQL拼接。以like模糊查询 为例子:
例如如下例子:
跟xml配置一样,like模糊查询直接使用#预编译的方式进行注解的话会触发异常,所以很多时候直接使用$进行注解:
@Select("SELECT id, name, age, email FROM user where name like '${name}'")List<User> queryUserByName(@Param("name") String name);
@Select("SELECT id, name, age, email FROM user where name like '%'||#{name}||'%'")List<User> queryUserByName(@Param("name") String name);
此时已使用预编译进行SQL查询:2.2 动态sql
2.2.1 使用< script>
要在带普通注解的映射器接口类中使用动态 SQL,可以使用script 元素。跟xml类似,主要是如下的元素:if choose (when, otherwise) trim (where, set) foreach
2.2.2 使用Provider注解
可以通过使用Provider注解指定某个工具类的方法来动态编写SQL。以@SelectProvider为例:首先在mapper中使用@SelectProvider定义相关的方法,其中type表示工具类,method 表示工具类的某个方法,用于返回具体的SQL。例如下面的例子:通过传递userIds以及name,查询相关的用户信息,在UserInfoSql类的getUserInfoByids方法定义了具体的SQL内容:/**
* @param userIds 必填
* @param name 可选
* @return
*/
@SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids")
List<User> getUserInfoByids(List<Long> userIds, String name);
class UserInfoSql {
public String getUserInfoByids(List<Long> userIds, String name) {
SQL sql = new SQL();
sql.SELECT("id, name, age, email");
sql.FROM("user");
sql.WHERE("id in(" + Joiner.on(',').join(userIds) + ")");
if(StringUtil.isNotBlank(name)){
sql.WHERE("name like '%" + name + "%'");
}
sql.ORDER_BY("id desc");
return sql.toString();
}
}
@RequestMapping(value = "/getUserInfoByids")
public List<User> getUserInfoByids(String name,@RequestParam List<Long> userIds){
List<User> userList = userMapper.getUserInfoByids(userIds,name);
return userList;
}
@SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids")
List<User> getUserInfoByids(@Param("userIds")List<Long> userIds,@Param("name")String name);
class UserInfoSql {
public String getUserInfoByids(@Param("userIds")List<Long> userIds, @Param("name")String name) {
StringBuilder sql = new StringBuilder(128);
sql.append("< script>SELECT id, name, age, email FROM user WHERE (id in");
sql.append("<foreach item='item' collection='userIds' open='(' separator=',' close=')'>#{item}</foreach>");
if(StringUtil.isNotBlank(name)){
sql.append("and name like '%'||#{name}||'%')");
}
sql.append("ORDER BY id desc</script>");
return sql.toString();
}
}
结语
mybatis3提供了新的基于注解的配置。实际上在sql注入的防护以及框架规范使用上跟xml还是类似的,在实际开发或者审计时可以关注对应注解的实现,避免sql注入风险。
原创 |【胖哈勃的七月公开赛】NewSql
原创 | 记一次完成的钓鱼实战
原创 | AMSI 浅析及绕过