kingFisherでは、ユーザーIDとパスワードを用いてログイン認証を行いますが、招かざるお客様によるパスワードアタックに対処する必要がありますので、サンプル実装してみました。
この実装概念としては良いですが2つの問題を含んでいます。まず1つ目はJava.Util.Mapクラスはスレッドセーフで無いので、クラス変数として使用は注意が必要です。もう1つは、拡張For文の中で要素数を削除してはいけないです。
(そのままコピペで使用しないでくださいね)こちらにスレッドセーフ版を掲載しました。
仕様は、「連続3回以上ログインに失敗した場合、10分間はアクセスが制限される」
まずはログイン失敗情報を記録するクラスを用意
/**
* 連続ログイン失敗制限用クラス
* @author naka
*/
public class LoginGuard {
//最後にアクセスした日時を数値で保持
private long lastAccess ;
//アクセス回数
private int accessCnt;
public long getLastAccess() {
return lastAccess;
}
public void setLastAccess(long lastAccess) {
this.lastAccess = lastAccess;
}
public int getAccessCnt() {
return accessCnt;
}
public void setAccessCnt(int accessCnt) {
this.accessCnt = accessCnt;
}
}
複数スレッドから共有されるようにservletのクラス変数にMap型を用意し、上記クラスを格納できるようにします
@WebServlet("/S02")
public class Servlet02 extends HttpServlet {
/*
* Login連続失敗検証用変数
*/
public static Map<String,LoginGuard> loginGuardMap = new HashMap<String,LoginGuard>();
loginGuardCheckメソッドを作成します。
このメソッドは自分を含め誰かかログインすると指定時間(今回は10分)経過しているログイン失敗情報をクリア。続けてログイン試行回数が指定回数(今回は3回)以内かチェックします。
このメソッドをログイン認証の前に呼び出し、falseが返った場合、「アクセスが制限されています」のエラーメッセージを出してログインを拒否します。
/**
* Login連続失敗検証
* @param loginid
* @return true:ログイン検証してよい false:制限回数オーバー
*/
private boolean loginGuardCheck(String loginid) {
boolean blnReturn = false;
String strKey = loginid; //ログインしようとしているユーザーID
//Mapのクリア準備
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("JST"),new Locale("ja","JP"));
long nowDateTime=0;
nowDateTime = (cal.get(Calendar.YEAR));
nowDateTime = nowDateTime *100000000;
nowDateTime = nowDateTime + ((cal.get(Calendar.MONTH)+1)*1000000);
nowDateTime = nowDateTime + (cal.get(Calendar.DATE)*10000);
nowDateTime = nowDateTime + (cal.get(Calendar.HOUR_OF_DAY)*100);
nowDateTime = nowDateTime + (cal.get(Calendar.MINUTE));
long chkDateTime=0;
cal.add(Calendar.MINUTE, -10); //10分前を算出
chkDateTime = (cal.get(Calendar.YEAR));
chkDateTime = chkDateTime *100000000;
chkDateTime = chkDateTime + ((cal.get(Calendar.MONTH)+1)*1000000);
chkDateTime = chkDateTime + (cal.get(Calendar.DATE)*10000);
chkDateTime = chkDateTime + (cal.get(Calendar.HOUR_OF_DAY)*100);
chkDateTime = chkDateTime + (cal.get(Calendar.MINUTE));
//10分前のログ審失敗情報をMapからクリア
for (String key : loginGuardMap.keySet()) {
LoginGuard objGuard = loginGuardMap.get(key);
if (objGuard.getLastAccess() << chkDateTime) {
loginGuardMap.remove(key);
}
}
//過去制限時間内のアクセスチェック
LoginGuard objGuard = loginGuardMap.get(strKey);
if(objGuard == null) {
//初回アクセスと判断
blnReturn = true;
objGuard = new LoginGuard();
objGuard.setAccessCnt(1);
objGuard.setLastAccess(nowDateTime);
loginGuardMap.put(strKey, objGuard);
}else {
//2回目以降アクセスなので、3回以下のアクセスか確認
int accessCnt = objGuard.getAccessCnt();
if(accessCnt < 4) {
//制限内回数アクセス
blnReturn = true;
objGuard.setAccessCnt(++accessCnt);
objGuard.setLastAccess(nowDateTime);
loginGuardMap.replace(strKey, objGuard);
}
}
return blnReturn;
}
最後にログインが成功したら、Mapからそのユーザー情報を削除するメソッドloginGuardResetを用意し、ログイン認証が成功したら、このメソッドを呼び出します
/**
* Login連続失敗検証用Mapクリア
* @param loginid
* @return
*/
private void loginGuardReset(String orgid,String loginid) {
String strKey = loginid; //ログインしようとしているユーザーID
loginGuardMap.remove(strKey);
}
サンプル実装の為、指定時間、指定回数を直接書きましたが、コンスタント定義又はパラメタとして外出ししたほうが良いでしょう。
今回はちょっとコードの量が多くなりましたが以上です。
0 件のコメント:
コメントを投稿