web-dev-qa-db-ja.com

最大5つの同時ログインセッションを許可する

同時ログインチェックを実装できません。

このサイトでは、特定のユーザーに対して同時に5つ以上の同時セッションを禁止する必要があります。

例: User Mattは5つのアクティブセッションを持つことができます。

ユーザーMattが6番目のセッションでログインしようとすると、最初にログインしたセッションと4時間以上経過したアクティビティがないセッションが削除されます。過去4時間以内に5つのセッションすべてにアクティビティがあった場合、ログインは失敗し、サイト管理者に連絡するためのエラー/メッセージが表示されます。

私はWordpressがWP_Session_Tokensを持っていることを知っていますが、それは彼らが 'last_activity'なしで 'expiration'と 'login'だけを格納しているようです。 WordpressまたはPHPセッションを介して最後のアクティビティを確認する方法はありますか?

そうでない場合、私の次の質問は、「最後の」ログインを現在の時刻と比較し、4時間以上経過しているかどうかを確認する方法です。

これが私の現在のコードです:

// On login, check if there are already 5 sessions active for the user
function check_sessions($login) {

    global $user_ID;
    $user = get_user_by( 'slug', $login );

    //If there are less than 5 sessions, let user login normally
     if( count( wp_get_all_sessions() ) < 5 ) {
         return true;
     }

    $sessions = WP_Session_Tokens::get_instance(  $user->id );

    $all_sessions = $sessions->get_all();

    $first_login = $all_sessions[0]['login'];

    if( $first_login->diff(time()) > 4hrs ) {
        // log out first_login user & login new user
        WP_Session_Tokens::destroy( $all_sessions[0] );
        return true;
    }

    else {

       // display message to user
    }
}
add_action('wp_login','check_sessions');
5
Gil

この質問は私を本当に興味深くしました。完全な解決策を作成するために私の土曜日の約5時間かかりました:)

プラグイン制限ログインセッション

それはまだ設定ページを提供していないので、すべてのオプションは現在ハードコーディングされています。プラグインは以下を実装します(OPによる)。

  1. ユーザーは、さまざまなブラウザやデバイス間で最大5つのログインセッションを持つことができます。
  2. 最も古いアクティビティセッションが4時間以上経過していない限り、5つ以上のセッションが試行されるとエラーが表示されます。
  3. 最も古いアクティビティセッションが4時間以上経過している場合、そのセッションは閉じられ、現在のログイン試行は許可されます。

コメント付きのコードに説明を追加しようとしました。ほとんどのプラグインコードは一目瞭然です。その一部が明確でない場合は、コメントしてください。

GitHubリポジトリはここにあります 。フォークして改良してください:)もしWordPressプラグインレポジトリに追加するのが便利だと思う人がいたら、私に知らせてください。必要であればWordPress.orgにアップロードします。

<?php
/*
Plugin Name: Limit Login Sessions
Version: 1.0.0
Author: Sisir Kanti Adhikari
Author URI: https://sisir.me/
Description: Limits users login sessions.
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Limit Login Sessions is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
any later version.

Limit Login Sessions is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (http://www.gnu.org/licenses/gpl-2.0.html)
 for more details.

*/

add_filter('authenticate', 'lls_authenticate', 1000, 2);

function lls_authenticate($user, $username){

    if(!username_exists($username) || !$user = get_user_by('login', $username))
        return null; // will trigger WP default no username/password matched error

    // setup vars
    $max_sessions = 5;
    $max_oldest_allowed_session_hours = 4;
    $error_code = 'max_session_reached';
    $error_message = "Maximum $max_sessions login sessions are allowed. Please contact site administrator.";

    // 1. Get all active session for this user
    $manager = WP_Session_Tokens::get_instance( $user->ID );
    $sessions =  $manager->get_all();

    // 2. Count all active session
    $session_count = count($sessions);

    // 3. Return okay if active session less then $max_sessions
    if($session_count < $max_sessions)
        return $user;

    $oldest_activity_session = lls_get_oldest_activity_session($sessions);

    // 4. If active sessions is equal to 5 then check if a session has no activity last 4 hours
    // 5. if oldest session have activity return error
    if(
        ( $session_count >= $max_sessions && !$oldest_activity_session ) // if no oldest is found do not allow
        || ( $session_count >= $max_sessions && $oldest_activity_session['last_activity'] + $max_oldest_allowed_session_hours * HOUR_IN_SECONDS > time())
    ){
        return new WP_Error($error_code, $error_message);
    }

    // 5. Oldest activity session doesn't have activity is given recent hours
    // destroy oldest active session and authenticate the user

    $verifier = lls_get_verifier_by_session($oldest_activity_session, $user->ID);

    lls_destroy_session($verifier, $user->ID);

    return $user;

}

function lls_destroy_session($verifier, $user_id){

    $sessions = get_user_meta( $user_id, 'session_tokens', true );

    if(!isset($sessions[$verifier]))
        return true;

    unset($sessions[$verifier]);

    if(!empty($sessions)){
        update_user_meta( $user_id, 'session_tokens', $sessions );
        return true;
    }

    delete_user_meta( $user_id, 'session_tokens');
    return true;

}

function lls_get_verifier_by_session($session, $user_id = null){

    if(!$user_id)
        $user_id = get_current_user_id();

    $session_string = implode(',', $session);
    $sessions = get_user_meta( $user_id, 'session_tokens', true );

    if(empty($sessions))
        return false;

    foreach($sessions as $verifier => $sess){
        $sess_string = implode(',', $sess);

        if($session_string == $sess_string)
            return $verifier;

    }

    return false;
}


function lls_get_oldest_activity_session($sessions){
    $sess = false;

    foreach($sessions as $session){

        if(!isset($session['last_activity']))
            continue;

        if(!$sess){
            $sess = $session;
            continue;
        }

        if($sess['last_activity'] > $session['last_activity'])
            $sess = $session;

    }

    return $sess;
}

// add a new key to session token array

add_filter('attach_session_information', 'lls_attach_session_information');

function lls_attach_session_information($session){
    $session['last_activity'] = time();
    return $session;
}

add_action('template_redirect', 'lls_update_session_last_activity');

function lls_update_session_last_activity(){

    if(!is_user_logged_in())
        return;

    // get the login cookie from browser
    $logged_in_cookie = $_COOKIE[LOGGED_IN_COOKIE];

    // check for valid auth cookie
    if( !$cookie_element = wp_parse_auth_cookie($logged_in_cookie) )
        return;

    // get the current session
    $manager = WP_Session_Tokens::get_instance( get_current_user_id() );

    $current_session = $manager->get($cookie_element['token']);

    if(
        $current_session['expiration'] <= time() // only update if session is not expired
        || ( $current_session['last_activity'] + 5 * MINUTE_IN_SECONDS ) > time() // only update in every 5 min to reduce db load
    ){
        return;
    }

    $current_session['last_activity'] = time();
    $manager->update($cookie_element['token'], $current_session);

}

一部の機能については、データベースのuser_meta値と直接やり取りする必要がありました。このクラスはいくつかのメソッドを保護していたので、直接アクセスすることはできませんでした。

プラグインはWP v4.3.1でローカルにテストされます。

12
Sisir

Httpプロトコルには長期セッションがないため、質問されているため、質問に対する回答を得ることは不可能です。 httpのセッションは1つの要求と応答です。

私たちがインターネット上で知っているセッションは、ログインを提供し、すべてのページロードのために情報を渡す必要性を取り除くために設計された単なるハックです。

wordpress 4.1は「セッション」とエンドデバイスとの関連付けを改善するための小さな一歩を踏み出しましたが、これまで以上に優れたハックであり、100%信頼できるものではありません。 2つの別々のエンドデバイスが同じセッションに属していると考えている。 OTOHそれは同じマシン上の2つのブラウザが異なるデバイス上にあると思います。

あなたはDRMを機能させようとしています、そして過去20年間、DRMについて証明されたことが1つありました。それは、人々がコンテンツへの「違法」アクセスを防ぐことではありません。それは支払う顧客をいらいらさせる。

あなたの具体的な計画は、どのデバイスが次に使用されるのかを知ることにかかっています、そして、それが最も最近アクティブだったものであるというあなたの仮定は利点がありません。 OTOH 5のアクティブセッションは、おそらく私のブロック全体で十分なので、「コンテンツの盗用をブロックする」ことさえしないでください。

問題がまったくメリットがないわけではありません。私はセキュリティを強化するために使われていたようなものを見ることができますが、DRMは細部に非常に依存しています。 「アクティブセッション」を見つけることはあなたの計画を実装する上で最も重要な部分であるので、あなたが実際にあなたがシステムに何を期待しているかを知っていてください。

0
Mark Kaplun