允许最多5个并发登录会话

时间:2015-03-13 作者:Gil

我在执行并发登录检查时遇到问题。

该站点需要防止任何特定用户同时进行5次以上的并发会话。

Example:用户Matt可以有5个活动会话。

如果用户Matt尝试使用第6个会话登录,它将删除第一个登录的会话(&a);没有超过4小时的活动。如果所有5个会话在过去4小时内都有活动,则登录失败,并向用户显示一条错误/消息以联系站点管理员。

我知道Wordpress有WP\\u Session\\u令牌,但它们似乎只存储“expiration”和“login”,没有“last\\u activity”。有没有办法通过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\');

2 个回复
SO网友:Sisir

这个问题让我很感兴趣。周六花了大约5个小时创建完整解决方案:)

插件限制登录会话,它还没有提供设置页面,所以所有选项目前都是硬编码的。插件实现以下功能(根据OP):

用户最多可以在各种浏览器和设备上拥有5个登录会话如果尝试的会话超过5个,则将显示错误,除非最旧的活动会话超过4小时我试图在代码中添加注释解释。大多数插件代码应该是不言自明的。如果部分内容不清楚,请随时发表评论。

The GitHub repository can be found here. 如果有人认为这对WordPress插件库是一个有用的补充,请告诉我,我会上传到WordPress。组织(如果需要)。

<?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.

SO网友:Mark Kaplun

由于http协议没有任何长期会话,所以在被问及这个问题时不可能得到答案。http上的会话是一个请求和响应。

正如我们所知,互联网上的会话只是为了消除每次加载页面时提供登录和传递信息的需要而设计的黑客行为。

wordpress 4.1在更好地将“会话”与终端设备关联方面迈出了一小步,但它只是一种比以前更好的黑客攻击,而且它不是百分之百可靠,因为您仍然可以复制cookie和/或使用代理使其误认为两个独立的终端设备属于同一会话。它会认为同一台机器上的两个浏览器在不同的设备上。

您只是想让DRM发挥作用,在过去20年中,DRM有一件事得到了证明,1。它并不能阻止人们“非法”访问内容,只是需要更长的时间。它惹恼了付费客户。

您的具体方案取决于您是否知道下一步将使用哪个设备,而您认为它是最近激活的设备是没有价值的。OTOH 5活动会话可能足够我的整个区块,所以你甚至不会“阻止内容窃取”

这并不是说这个问题毫无价值。我可以看到类似的东西被用来增强安全性,但DRM在很大程度上取决于小细节,一旦你使用了一个流行但关键的术语,如“活动会话”,而没有首先定义它的细节,这意味着对我来说,你真的不知道你实际期望从系统中得到什么,因为找出“活动会话”是实施方案的最关键部分。

结束

相关推荐

WP_LOGIN_URL不能正常工作

我正在进行一个项目,需要向“管理”菜单添加一个自定义链接,并使用以下代码:$login_url = wp_login_url(); add_options_page(\'Login\', \'Login Page\', \'manage_options\', \'loop.php?url=\'.esc_url($login_url).\'\'); 有件事。上述代码正在将以下URL添加到菜单:http://example.com/wp-admin/loop.php?url=http:/ex