違い1:
@Parm( "xx")がパラメータ名を示す場合、数値データの場合、#と$の間に実質的な違いはありません。文字データの場合、$によって取得される値には独自の引用符がありません ''。
数値実験:
1.DAOにメソッドを追加します
User queryUserById(@Param("id") Integer id);
2.Mapper.xmlを作成します
A.値には#を使用してください
<sql id="user_field">
select id,username,password,gender,regist_time as registerTime
from t_user
</sql>
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = #{id}
</select>
B. $の値を取る
<sql id="user_field">
select id,username,password,gender,regist_time as registerTime
from t_user
</sql>
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = #{id}
</select>
[注意!!!]:コンソールから出力されたSQLステートメントから、次のこともわかります。
これは#outputSQLです。
これは$によるSQL出力です。
$直接文字列スプライシングを行いますが、#は明らかにそうではありません。プレースホルダーを使用します。文字タイプであるかどうかに応じて、 ''を追加するかどうかを選択します。
キャラクター実験:
1.DAOにメソッドを追加します
User queryUserByUsername(@Param("username") String username);
2.Mapper.xmlを作成します
A.値には#を使用してください
<select id="queryUserByUsername" resultType="User">
<include refid="user_field"></include>
where username = #{username}
</select>
B. $の値を取る
<select id="queryUserByUsername" resultType="User">
<include refid="user_field"></include>
where username = ${username}
</select>
正しい書き方:
<select id="queryUserByUsername" resultType="User">
<include refid="user_field"></include>
where username = '${username}'
</select>
違い2:
@Paramアノテーションが使用されていない場合、#は分散パラメーターのarg0、arg1、またはparam1、param2をサポートしますが、$はサポートしません。@Paramを使用する場合、#と$の両方がparamをサポートします。
A. @Param Note +#{param1}
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = #{param1}
</select>
B. @ Paramコメント+ $ {param}
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = ${param1}
</select>
違いは依然としてSQLステートメントの違いです。読者がまだ印象を持っている場合、PreparedStatementの主な機能はSQLインジェクションを防ぐことです。これについては後で説明します。
注:パラメーターが@Paramアノテーションを使用する場合、#はparamのみをサポートし、argはサポートしません。
C. @ Paramアノテーションを削除する
1)#{arg0}または#{param1}
User queryUserById(Integer id);
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = #{arg0}
</select>
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = #{param1}
</select>
2)$ {arg0}または$ {param1}
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = ${arg0}
</select>
エラーメッセージから、@ Paramアノテーションを削除した後、$ {arg0}は実際にはarg0をクラスパラメータIntegeridのarg0という名前の属性として扱い、getメソッドを介して値を取得しようとしていることがわかります。
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = ${param1}
</select>
エラーメッセージから、@ Paramアノテーションを削除した後、$ {param1}は実際にはparam1をクラスパラメータ整数IDのparam1という名前の属性として扱い、getメソッドを介して値を取得しようとしていることがわかります。
注目に値する:Mybatisでは、DAOインターフェイスで設定したメソッドのパラメーターが基本データ型であっても、Mybatisが生成されると、パラメーターは次のような基本データ型のパッケージングクラスに変更されます。
User queryUserById(int id);
<select id="queryUserById" resultType="User">
<include refid="user_field"></include>
where id = ${id}
</select>
[概要]:$を使用する必要がある場合は、散在するパラメーターに注釈を付ける必要があります。文字または文字列の場合は、手動で引用符を追加する必要があります。
違い3 [重要]:
SQLインジェクションは、SQLステートメントを使用して、SQLステートメントのみを使用するセキュリティチェックを回避できることを意味します。たとえば、システムにログインする場合、通常はユーザー名またはパスワードを確認します。
実行する通常のSQLステートメント: "select * from t_user where username = '" + username + "' and password = '" + password + "'"。
ただし、パスワードを「123456または1 = 1」に割り当てると、実行されるSQLステートメントは次のようになります。
select * from t_user where username = '"+ username +"' and 123456 or 1 = 1 '"、1 = 1が条件であるため、このステートメントはテーブル内のすべてのデータを見つけることができ、セキュリティを完全にバイパスします。 SQLで確認してください。
例を見てみましょう:
List<User> Login(@Param("username") String id,@Param("password") String password);
<select id="Login" resultType="User">
select * from t_user
where username = '${username}' and password = '${password}'
</select>
@Test
public void testLogin(){
List<User> zzt = mapper.Login("zzt", "111' or '1' = '1");
for (User user : zzt) {
System.out.println(zzt);
}
}
注意深い読者は、メソッド全体の戻り値がリストでなければならないという前提があることを発見したかもしれません。それがエンティティクラスタイプの場合、カプセル化するオブジェクトが多すぎるためにエラーが発生します。
ただし、これはSQLインジェクションについて心配する必要がないという意味ではありません。システム全体のログインロジックが複数ある場合は、特に一部のアカウントでは、システムデータを解放できます。繰り返しが許可されているパスワード。つまり、他の値をプライマリキーとして使用します。
さらに、より可能性のある状況について話しましょう。多数のデータベースクエリ操作にはかなりの時間がかかることがわかっています。SQLインジェクションを通じて、すべてのデータをクエリできます。その後、クライアントがサーバーにSQLをインジェクションし続ける限り、 、サーバークエリが多数あると、キャッシュが使い果たされたり、システムの応答に影響したり、クラッシュしたりする可能性があります。
では、なぜ#useプレースホルダーでSQLインジェクションの問題を回避できるのでしょうか。SQLステートメントが一目でわかります。
実際、翻訳は次のとおりです。select* from t_user where username = 'zzt' and password = "111'or '1' = '1"、つまり、111'または '1' = '1がパスワードとして使用されます。自然に注射を防ぎます
だが!だが!だが!プレースホルダーを使用できない場合もあります。たとえば、動的ルールに従ってデータを順番に検索および出力できます。select* from t_user order by id desc;
List<User> queryByDesc(@Param("order") String order);
<select id="queryByDesc" resultType="User">
select * from t_user order by id #{order}
</select>
@Test
public void testQueryByDesc(){
List<User> users = mapper.queryByDesc("desc");
for (User user : users) {
System.out.println(user);
}
}
明らかに:select * from t_user order by id'desc ';は構文エラーです。
スプライシングを使用する:
<select id="queryByDesc" resultType="User">
select * from t_user order by id ${order}
</select>
安全性を確保し、注入を防ぐために、ユーザーがルールを直接入力することは許可されていません。ルールに対応するパラメーターのみが入力されます。
@Test
public void testQueryByDesc(){
int signal = 0;
List<User> users;
if( signal == 0 ){
users = mapper.queryByDesc("desc");
}else{
users = mapper.queryByDesc("asc");
}
for (User user : users) {
System.out.println(user);
}
}
[概要]:入力するデータが列属性に関連している場合、プレースホルダーを使用すると、SQLインジェクションのリスクを回避できます。他の状況(SQL自体に関連)で使用すると、SQL構文エラーが発生する可能性が非常に高くなります。SQLスプライシングにはインジェクションのリスクがありますが、構文が正しい限り、制限はありません(ここでの制限は、インジェクションによって引き起こされるセキュリティ問題の防止ではなく、使用を指します)。ほとんどの場合、#を使用することをお勧めします。