使用 WordPress REST API 通过 AJAX 更改密码

Posted

技术标签:

【中文标题】使用 WordPress REST API 通过 AJAX 更改密码【英文标题】:Changing Password via AJAX with the WordPress REST API 【发布时间】:2018-01-07 01:22:35 【问题描述】:

我正在使用 WordPress REST API 构建密码更改表单。用户输入一个新密码,然后通过 AJAX 将其提交到执行此操作的自定义端点:

$userID = get_current_user_id();
wp_set_password($password, $userID);

//Log user in and update auth cookie
wp_set_current_user($userID);
wp_set_auth_cookie($userID);
//Set the cookie immediately
$_COOKIE[AUTH_COOKIE] = wp_generate_auth_cookie($userID, 2 * DAY_IN_SECONDS);

//Return fresh nonce
return new WP_Rest_Response(array(
    'nonce' => wp_create_nonce('wp_rest')
));

端点应该自动让用户登录并返回一个新的随机数,这样他们就不必使用新密码再次登录。

问题是返回的nonce完全一样,是无效的。我只能在页面刷新后获得新的随机数。 WordPress 用来生成 nonce 的一些 $_COOKIE$_SESSION 变量似乎在页面刷新之前不会更新。

感谢您的帮助。

【问题讨论】:

查看此解决方案***.com/questions/42179869/… 【参考方案1】:

WordPress 似乎有一些 $_COOKIE 或 $_SESSION 变量 依赖于生成随机数直到页面才被更新 刷新。

是的,它是LOGGED_IN_COOKIE,默认为:

'wordpress_logged_in_' . COOKIEHASH

看看:

$token = wp_get_session_token(); in wp_create_nonce()

$cookie = wp_parse_auth_cookie( '', 'logged_in' ); in wp_get_session_token()

$cookie_name = LOGGED_IN_COOKIE; in wp_parse_auth_cookie()

因此您将替换此代码:(我不会为此使用 wp_generate_auth_cookie();而是使用已通过 wp_set_auth_cookie() 生成的代码)

//Set the cookie immediately
$_COOKIE[AUTH_COOKIE] = wp_generate_auth_cookie($userID, 2 * DAY_IN_SECONDS);

..用这个:

$_set_cookies = true; // for the closures

// Set the (secure) auth cookie immediately. We need only the first and last
// arguments; hence I renamed the other three, namely `$a`, `$b`, and `$c`.
add_action( 'set_auth_cookie', function( $auth_cookie, $a, $b, $c, $scheme ) use( $_set_cookies )
    if ( $_set_cookies ) 
        $_COOKIE[ 'secure_auth' === $scheme ? SECURE_AUTH_COOKIE : AUTH_COOKIE ] = $auth_cookie;
    
, 10, 5 );

// Set the logged-in cookie immediately. `wp_create_nonce()` relies upon this
// cookie; hence, we must also set it.
add_action( 'set_logged_in_cookie', function( $logged_in_cookie ) use( $_set_cookies )
    if ( $_set_cookies ) 
        $_COOKIE[ LOGGED_IN_COOKIE ] = $logged_in_cookie;
    
 );

// Set cookies.
wp_set_auth_cookie($userID);
$_set_cookies = false;

工作示例(在 WordPress 4.9.5 上测试)

PHP/WP REST API

function myplugin__change_password( $password, $userID ) 
    //$userID = get_current_user_id();
    wp_set_password($password, $userID);

    // Log user in.
    wp_set_current_user($userID);
    $_set_cookies = true; // for the closures

    // Set the (secure) auth cookie immediately. We need only the first and last
    // arguments; hence I renamed the other three, namely `$a`, `$b`, and `$c`.
    add_action( 'set_auth_cookie', function( $auth_cookie, $a, $b, $c, $scheme ) use( $_set_cookies )
        if ( $_set_cookies ) 
            $_COOKIE[ 'secure_auth' === $scheme ? SECURE_AUTH_COOKIE : AUTH_COOKIE ] = $auth_cookie;
        
    , 10, 5 );

    // Set the logged-in cookie immediately. `wp_create_nonce()` relies upon this
    // cookie; hence, we must also set it.
    add_action( 'set_logged_in_cookie', function( $logged_in_cookie ) use( $_set_cookies )
        if ( $_set_cookies ) 
            $_COOKIE[ LOGGED_IN_COOKIE ] = $logged_in_cookie;
        
     );

    // Set cookies.
    wp_set_auth_cookie($userID);
    $_set_cookies = false;

    //Return fresh nonce
    return new WP_Rest_Response(array(
        'nonce'  => wp_create_nonce('wp_rest'),
        'status' => 'password_changed',
    ));


function myplugin_change_password( WP_REST_Request $request ) 
    $old_pwd = $request->get_param( 'old_pwd' );
    $new_pwd = $request->get_param( 'new_pwd' );

    $user = wp_get_current_user();
    if ( ! wp_check_password( $old_pwd, $user->user_pass, $user->ID ) ) 
        return new WP_Error( 'wrong_password', 'Old password incorrect' );
    

    if ( $old_pwd !== $new_pwd ) 
        return myplugin__change_password( $new_pwd, $user->ID );
    

    return new WP_Rest_Response( [
        'nonce'  => wp_create_nonce( 'wp_rest' ),
        'status' => 'passwords_equal',
    ], 200 );


add_action( 'rest_api_init', function()
    register_rest_route( 'myplugin/v1', '/change-password', [
        'methods'             => 'POST',
        'callback'            => 'myplugin_change_password',
        'args'                => [
            'old_pwd' => [
                'type'              => 'string',
                'validate_callback' => function( $param ) 
                    return ! empty( $param );
                
            ],
            'new_pwd' => [
                'type'              => 'string',
                'validate_callback' => function( $param ) 
                    return ! empty( $param );
                
            ],
        ],
        'permission_callback' => function()
            // Only logged-in users.
            return current_user_can( 'read' );
        ,
    ] );
 );

HTML / 表单

<fieldset>
    <legend>Change Password</legend>

    <p>
        To change your password, please enter your old or current password.
    </p>

    <label>
        Old Password:
        <input id="old_passwd">
    </label>
    <label>
        New Password:
        <input id="new_passwd">
    </label>

    <label>
        Old nonce: (read-only)
        <input id="old_nonce" value="<?= wp_create_nonce( 'wp_rest' ) ?>"
        readonly disabled>
    </label>
    <label>
        New nonce: (read-only)
        <input id="new_nonce" readonly disabled>
    </label>

    <button id="go" onclick="change_passwd()">Change</button>
</fieldset>
<div id="rest-res"><!-- AJAX response goes here --></div>

jQuery / AJAX

function change_passwd() 
    var apiurl = '/wp-json/myplugin/v1/change-password',
        $ = jQuery;

    $.post( apiurl, 
        old_pwd: $( '#old_passwd' ).val(),
        new_pwd: $( '#new_passwd' ).val(),
        _wpnonce: $( '#new_nonce' ).val() || $( '#old_nonce' ).val()
    , function( res )
        $( '#new_nonce' ).val( res.nonce );

        // Update the global nonce for scripts using the `wp-api` script.
        if ( 'object' === typeof wpApiSettings ) 
            wpApiSettings.nonce = res.nonce;
        

        $( '#rest-res' ).html( '<b>Password changed successfully.</b>' );
    , 'json' ).fail( function( xhr )
        try 
            var res = JSON.parse( xhr.responseText );
         catch ( err ) 
            return;
        

        if ( res.code ) 
            $( '#rest-res' ).html( '<b>[ERROR]</b> ' + res.message );
        
    );

【讨论】:

【参考方案2】:

您需要将随机数通过 X-WP-Nonce 标头与您的 AJAX 请求一起传递。比如:

beforeSend: function ( xhr ) 
    xhr.setRequestHeader( 'X-WP-Nonce', localizedScript.nonce );

localizedScript 是您的 nonce 本地化的脚本。有关本地化的更多信息,请参阅法典。 注意localizedScript 方法不是必需的。

如果没有localizedSript,您的 AJAX 可能类似于以下内容:

var _nonce = "<?php echo wp_create_nonce( 'wp_rest' ); ?>";
$.ajax(
    type: 'POST',
    url: url_path,
    data: ,
    dataType: 'json',
    beforeSend: function ( xhr ) 
        xhr.setRequestHeader( 'X-WP-Nonce', _nonce );
    
);

显然复制上面的那个确切的脚本不会为你完成它,但这基本上是你成功传递随机数的方式。

【讨论】:

以上是关于使用 WordPress REST API 通过 AJAX 更改密码的主要内容,如果未能解决你的问题,请参考以下文章

wordpress rest api 授权

如何通过wordpress rest api重置用户密码

使用 Wordpress Rest API JS 填充 ACF 字段

通过 Axios 和有效 JWT 发布时,WordPress REST API 返回 401 Unauthorized

WordPress REST API 未检索所有数据

如何通过 Wordpress REST API 对 LearnDash 中受保护数据的远程访问进行身份验证?