如何允许 Laravel 和 WordPress 共享登录名?

Posted

技术标签:

【中文标题】如何允许 Laravel 和 WordPress 共享登录名?【英文标题】:How to allow Laravel and WordPress to share logins? 【发布时间】:2016-06-09 05:01:32 【问题描述】:

我是一名 Laravel 开发人员。我用 Laravel 开发了一个电子商务插件,我只想把 WordPress 和 Laravel 结合起来。所以我需要在 Laravel 和 WordPress 之间共享或建立共同的登录会话。

我该如何实现呢?是否有可用的特殊插件?或者我可以使用 laravel-Auth 吗?

【问题讨论】:

据我了解,这是不可能的,因为 laravel 和 wordpress 有自己不同的目录和编码结构。如果您在 Laravel 中创建 API 并且您通过 wordpress 插件或代码进行调用,这可能是可能的。即使您这样做,您也必须在两端保持不同的身份验证, 但是如果我使用相同的数据库意味着......?任何其他解决方案...? 这意味着你的 laravel 和 wordpress 两个代码都将访问同一个数据库,但我不认为这是可行的,需要更多的研究。 i 使用 Corcel 插件将 wordpress 数据库用作 laravel 数据库。但我不知道如何在 wordpress 和 laravel 之间启用会话共享。 你可以创建一个单独的 API 并尝试从 Laravel 端登录,如果登录成功则设置你的 Laravel 会话。类似于 OAuth 的工作原理。但我不认为会话共享会起作用。 【参考方案1】:

在 WordPress 中启用单点登录花了我 18 多个小时的努力,但可能只需要几分钟:

我尝试了各种各样的东西:Laravel Passport (OAuth2)、OpenID Connect 等。

但是我可以开始工作的唯一解决方案是让 WordPress 登录页面重定向到受身份验证保护的 Laravel 路由,该路由生成 JWT(JSON Web 令牌)并重定向回 WordPress 上的特殊回调 URL,该 URL 要么创建一个新用户或登录现有用户。

效果很好。

class JwtController extends Controller 

    /**
     * Inspired by https://github.com/WebDevStudios/aad-first-party-sso-wordpress/tree/master/lib/php-jwt
     * 
     * @param Request $request
     * @return ResponseInterface
     */
    public function redirectWithToken(Request $request) 
        $key = config('jwt.key');
        $wpJwtUrl = $request->input('callback');
        $redirectUrlAfterLogin = $request->input('redirect_to'); //Get the original intended destination and append as URL param to /jwt. 
        $tokenArray = $this->getToken(auth()->user(), $redirectUrlAfterLogin);
        $jwt = \Firebase\JWT\JWT::encode($tokenArray, $key);
        $wpJwtUrlWithTokenAsParam = $wpJwtUrl . '?token=' . $jwt;
        return redirect()->away($wpJwtUrlWithTokenAsParam);
    

    /**
     * 
     * @param \App\User $user
     * @param string $redirectUrlAfterLogin
     * @return array
     */
    public function getToken($user, $redirectUrlAfterLogin) 
        $now = \Carbon\Carbon::now();
        $aud = config('jwt.audience'); //root URL of the WordPress site
        $firstName = StrT::getFirstNameFromFullName($user->name);
        $expirationMins = config('jwt.expirationMins');
        $token = [
            "iss" => url("/"),
            "aud" => $aud, //"audience" https://tools.ietf.org/html/rfc7519#section-4.1.3
            "iat" => $now->timestamp, //"issued at" https://tools.ietf.org/html/rfc7519#section-4.1.6
            "exp" => $now->addMinutes($expirationMins)->timestamp, //"expiration" https://tools.ietf.org/html/rfc7519#section-4.1.4
            "attributes" => [
                'emailAddress' => $user->email,
                'firstName' => $firstName,
                'lastName' => StrT::getLastNameFromFullName($user->name),
                'nickname' => $firstName,
                'displayName' => $user->name,
                'redirectUrlAfterLogin' => $redirectUrlAfterLogin//In plugin: use redirectUrlAfterLogin from attributes after login.
            ]
        ];
        return $token;
    

安装此 WordPress 插件,但在完成其他所有操作之前不要激活它:https://wordpress.org/plugins/wp-force-login/ 安装这个 WordPress 插件:https://as.wordpress.org/plugins/jwt-authenticator/

然后将其 auth.php 编辑为:

// register the callback
add_action('rest_api_init', function () 
    register_rest_route('jwt-auth/v1', 'callback', [
        'methods' => 'GET',
        'callback' => 'ja_login'
            ], true);
);

require_once('JWT.php');

function ja_login() 
    //get all attributes
    $options = get_option('ja_settings');
    $token_name = $options['token_name'];
    $secret_key = $options['secret_key'];
    $iss = $options['iss'];
    $aud = $options['aud'];

    // decode the token
    $token = $_GET[$token_name];
    $key = $secret_key;
    $JWT = new JWT;
    $json = $JWT->decode($token, $key);
    $jwt = json_decode($json, true);

    // use unix time for comparision
    $exp = is_int($jwt['exp']) ? $jwt['exp'] : strtotime($jwt['exp']);
    $nbf = $jwt['nbf'] ?? null;
    $now = strtotime("now");

    // if authentication successful
    if (($jwt['iss'] == $iss) && ($jwt['aud'] == $aud) && ($exp > $now) && ($now > $nbf)) 
        return getUserFromValidToken($options, $jwt);
     else 
        return 'Login failed. Please let us know exactly what happened, and we will help you out right away.';
    


/**
 * 
 * @param array $options
 * @param array $jwt
 * @return string
 */
function getUserFromValidToken($options, $jwt) 
    $attributesKey = $options['attributes'];
    $mail = $options['mail'];
    $givenname = $options['first_name'];
    $surname = $options['last_name'];
    $nickname = $options['nickname'];
    $displayname = $options['displayname'];
    $default_role = $options['default_role'];    
    $attributes = $jwt[$attributesKey];
    $redirectUrlAfterLogin = $attributes['redirectUrlAfterLogin'] ?? get_site_url();
    $_SESSION['attributes'] = $attributes;
    $_SESSION['jwt'] = $jwt;

    // find or create user
    $user = ja_find_or_create_user($attributes[$mail], $attributes[$mail], $attributes[$givenname], $attributes[$surname], $attributes[$nickname], $attributes[$displayname], $default_role);
    // login user
    if ($user) 
        wp_clear_auth_cookie();
        wp_set_current_user($user->ID, $user->user_login);
        wp_set_auth_cookie($user->ID);
        do_action('wp_login', $user->user_login);        
        wp_safe_redirect($redirectUrlAfterLogin);
        exit();
     else 
        return 'getUserFromValidToken failed!';
    


/**
 * 
 * @param string $username
 * @param string $emailAddress
 * @param string $firstName
 * @param string $lastName
 * @param string $nickname
 * @param string $displayName
 * @param string $defaultRole
 * @return mixed 
 */
function ja_find_or_create_user($username, $emailAddress, $firstName, $lastName, $nickname, $displayName, $defaultRole) 
    // if user exists, return user
    if (username_exists($username)) 
        return get_user_by('login', $username);
     elseif (email_exists($emailAddress)) 
        return get_user_by('email', $emailAddress);
     else //  create user
        $length = 16;
        $include_standard_special_chars = false;
        $random_password = wp_generate_password($length, $include_standard_special_chars);
        // create user
        $user_id = wp_create_user($username, $random_password, $emailAddress);
        // update user metadata and return user id
        $userData = [
            'ID' => $user_id,
            'first_name' => $firstName,
            'last_name' => $lastName,
            'nickname' => $nickname,
            'display_name' => $displayName,
            'role' => $defaultRole
        ];
        return wp_update_user($userData);//(If successful, returns the user_id, otherwise returns a WP_Error object.)
    


/**
 * Get login message link HTML for adding to the login form
 * @return string
 */
function getLoginMessage() 
    $options = get_option('ja_settings');
    $redirect_to = $_GET['redirect_to'] ?? null;
    $login_url = $options['login_url'] . '?callback=' . urlencode(site_url('/wp-json/jwt-auth/v1/callback'));
    if($redirect_to)
        $login_url .= '&redirect_to=' . urlencode($redirect_to);
    
    $login_message = $options['login_message'];
    return "<a id='jwt_link' href='$login_url'>$login_message</a>";


add_filter('login_message', 'getLoginMessage');
add_action( 'load-profile.php', function() //https://wordpress.stackexchange.com/a/195370/51462 Redirect from profile.php to the dashboard since there is no reason for WordPress users to see or manage their profile since their main account is on the other site.
    if( ! current_user_can( 'manage_options' ) )
        $redirectUrl = get_site_url();//admin_url()
        exit( wp_safe_redirect( $redirectUrl ) );
    
 );
function show_admin_bar_conditionally()//remove the WordPress admin toolbar https://premium.wpmudev.org/blog/remove-the-wordpress-admin-toolbar/
    return current_user_can( 'manage_options' );

add_filter('show_admin_bar', 'show_admin_bar_conditionally');//can use 'show_admin_bar_conditionally' or '__return_false' for never.
//------------------------------------------------------------------
//for https://wordpress.org/support/topic/rest-api-26/#post-9915078 
//and https://github.com/kevinvess/wp-force-login/issues/35 
//and https://wordpress.org/support/topic/rest-api-26/page/2/#post-10000740
//and https://wordpress.org/support/topic/jwt-authentication/#post-10698307
add_filter( 'rest_authentication_errors', '__return_true' );

这属于你在 WordPress 中的主题的 functions.php:

// https://codex.wordpress.org/Customizing_the_Login_Form
function my_custom_login_page()  ?>
    <style type="text/css">
        #loginform, #login #navdisplay: none;
        #jwt_linkfont-weight: bold; font-size: 20px;
    </style>
    <script>
        document.addEventListener("DOMContentLoaded", function(event)  
            document.getElementById('jwt_link').click();//immediately upon load of login page, click the JWT link automatically
        );
    </script>
<?php 
add_action( 'login_enqueue_scripts', 'my_custom_login_page' );

【讨论】:

谢谢你举个例子。我正在尝试按照您的方法来实现这个确切的用例。我注意到在 wordpress 中创建用户后,设置身份验证 cookie 并将用户登录并重定向到 URL 的代码不起作用,cookie 从未设置。这是我指的代码if ($user) wp_clear_auth_cookie(); wp_set_current_user($user-&gt;ID, $user-&gt;user_login); wp_set_auth_cookie($user-&gt;ID); do_action('wp_login', $user-&gt;user_login); wp_safe_redirect($redirectUrlAfterLogin); exit(); 【参考方案2】:

正确的做法是使用 Laravel(或 Wordpress)作为 Auth 服务器 并像 SSO 插件一样创建。 我对 Laravel 的 NodeBB 论坛登录做了同样的事情。

我建议的步骤:

    看这个包Laravel OAuth Server 为 wordpress 创建或查找任何 SSO 插件

所以你在 laravel 中拥有所有用户(注册等) 如果他们想登录到 Wordpress,他们会登录到 Laravel App 并授予登录到 wordpress 的权限。 想一想就像你添加Facebook Login to your site 阅读更多wordpress SSO

但要使用会话和 cookie,可能会出现安全问题。 希望有所帮助。

【讨论】:

以上是关于如何允许 Laravel 和 WordPress 共享登录名?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 和 WordPress 前端

从Laravel到Wordpress数据库的Auth用户

如何允许用户从 WordPress 网站下载内容

laravel 和 wordpress 在同一个域上(子文件夹中的 laravel)

Laravel 和 Wordpress 在同一服务器/域上

集成wordpress和laravel登录