Cómo validar un usuario desde fuera de WordPress/PHP
Estoy trabajando en una aplicación AJAX que estará embebida en una página de WordPress. La aplicación AJAX intercambia datos con servlets ejecutándose en Tomcat. Ahora los servlets necesitan una forma de determinar si una petición viene de un usuario que está logueado en WordPress. Y si el usuario está logueado, los servlets también deben poder determinar el ID del usuario para poder consultar la base de datos. Si el usuario no está logueado, la petición será denegada.
En otras palabras, necesito que un servlet ejecute una petición solo si el usuario que la generó está logueado en WordPress (versión 3.3.x). Tanto el servlet (Tomcat) como WordPress (Apache2) corren en la misma máquina física y comparten la misma base de datos.
En teoría esto podría resolverse fácilmente haciendo lo siguiente:
- Durante el login de WordPress, algún token de usuario se almacena en una variable JavaScript.
- La aplicación AJAX envía el token de usuario a los servlets en cada llamada.
- Los servlets usan el token para consultar a WordPress si es válido (es decir, si el usuario está logueado) y ejecutan o deniegan la petición.
La pregunta es ¿cómo puede implementarse esto en el lado de WordPress?
Porque, lo que hace complicada la teoría es el hecho de que aún no he hecho programación en PHP.
Primero pensé en transmitir la cookie wordpress_logged_in (auth) al servlet y hacer que el servlet consulte a WordPress si la cookie auth sigue siendo válida. Pero al parecer, esto no puede hacerse, ya que wp_validate_auth_cookie() siempre falla, incluso si se pasan datos de cookie de un usuario logueado. Otra solución podría ser desarrollar un plugin que almacene el sessionid y userid en una tabla, que podría ser consultada fácilmente por los servlets. O quizás haya otra solución...

WordPress ya tiene una API incorporada a través de un servidor XMLRPC. Esto significa que puedes hacer una solicitud XMLRPC desde tu aplicación Java y verificar un nombre de usuario/contraseña. Desafortunadamente, no hay manera de autenticarse directamente a través de ella tal como está.
Dicho esto, es muy fácil crear la tuya propia. Solo debes engancharte al filtro xmlrpc_methods
y agregar el tuyo. La clave del array que agregues será el método XMLRPC que llamarás desde tu aplicación, y el valor será la función que será llamada por el servidor XMLRPC de WordPress.
<?php
add_filter('xmlrpc_methods', 'wpse39662_add_login_method' );
/**
* Filtra los métodos XMLRPC para permitir solo verificar el usuario/contraseña
* de un usuario dado
*/
function wpse39662_add_login_method( $methods )
{
$methods['wpse39662.login'] = 'wpse39662_check_login';
return $methods;
}
Y la función de callback, wpse39662_check_login
, recibiría un argumento, el array de cosas enviadas al servidor XMLRPC.
<?php
function wpse39662_check_login( $args )
{
$username = $args[0];
$password = $args[1];
$user = wp_authenticate( $username, $password );
if( is_wp_error( $user ) )
{
return false;
}
return true;
}
Aquí está todo eso como un plugin. Con eso instalado y XMLRPC habilitado en tu sitio de WP, deberías poder hacer solicitudes con algún cliente XMLRPC (estoy seguro de que Java tiene uno).
Aquí está el código que usé para probar lo anterior (cliente XMLRPC de Python).
>>> import xmlrpclib as xmlrpc
>>> s = xmlrpc.ServerProxy('http://wordpress.dev/xmlrpc.php')
>>> s.wpse39662.login('admin', 'password')
True

¡Gracias! ¡Esto me acerca un gran paso! ¿Se puede lograr lo mismo usando la cookie de autenticación del usuario? Así no tengo que almacenar y enviar el nombre de usuario/contraseña a través de la red. Mi proyecto consiste en una aplicación AJAX que está incrustada en una página de WordPress. La aplicación AJAX llama a un servlet y el servlet pregunta a WordPress si el usuario está autenticado. Podría pasar el usuario/contraseña a la aplicación AJAX y transferirlo al servlet, pero me preocupa que no sea muy seguro. Así que intenté pasar el contenido de la cookie de autenticación a wp_validate_auth_cookie() pero siempre falla.

WordPress (actualmente) verifica si el usuario sigue conectado comprobando una de las cookies que emite al iniciar sesión. Construye el contenido de esta cookie mediante operaciones de hashing. Los detalles están en la función "wp_generate_auth_cookie" en /wp-includes/pluggable.php:
function wp_generate_auth_cookie($user_id, $expiration, $scheme = 'auth') {
$user = get_userdata($user_id);
$pass_frag = substr($user->user_pass, 8, 4);
$key = wp_hash($user->user_login . $pass_frag . '|' . $expiration, $scheme);
$hash = hash_hmac('md5', $user->user_login . '|' . $expiration, $key);
$cookie = $user->user_login . '|' . $expiration . '|' . $hash;
return apply_filters('auth_cookie', $cookie, $user_id, $expiration, $scheme);
}
Puedes recrear este algoritmo (usando esta y otras funciones de auth_cookie) en tu código Java para realizar las mismas verificaciones. Se podría usar JS para asegurar que la cookie se envía a tu servlet.
Alternativamente, XMLRPC podría ser una buena opción. Podrías escribir un nuevo método (como se explica en otra solución aquí) para validar la cookie de autenticación (en lugar de validar usuario y contraseña como se hace normalmente).

Obtén el plugin Exec-PHP, y luego crea una página en WordPress (no una entrada) con un permalink adecuado (http://misitio/user_id/
) y el código de la referencia de la API get_current_user_id()
:
<?php
$user_id = get_current_user_id();
if ($user_id == 0) {
echo 'Actualmente no has iniciado sesión.';
} else {
echo 'Has iniciado sesión como usuario '.$user_id.'.';
}
?>
Luego puedes extraer las cookies que el cliente te envía y colocarlas en una solicitud GET
para http://127.0.0.1/user_id/
. Así sabrás si el usuario ha iniciado sesión y cuál es su ID de usuario.

Gracias por la respuesta, el problema es que los servlets están escritos en Java, por lo que no se puede ejecutar código PHP. Lo que estoy buscando es algún tipo de interfaz externa que permita a un servlet/Java comunicarse con WordPress/PHP. Seguro que existe algún tipo de interfaz disponible, simplemente no he podido encontrarla...

Ah, ya veo. Quizás usar algo como Quercus http://www.caucho.com/resin-3.0/quercus/ podría darte lo mejor de ambos mundos?

Gracias, pero Quercus no es la solución correcta, ya que ya tengo una instalación funcional de WordPress/PHP/Apache y una instalación funcional de servlet/Java/Tomcat. Lo único que necesito ahora es una interfaz entre estos dos que permita al servlet verificar si un usuario ha iniciado sesión en WordPress (algún tipo de interfaz/protocolo/IPC/lo que sea).

Este es un plugin de WordPress de un solo archivo que hace el trabajo:
function yournamespace_validateAuthCookie($cookie, $scheme = 'logged_in') {
return wp_validate_auth_cookie($cookie, $scheme);
}
function yournamespace_new_xmlrpc_methods($methods) {
$methods['yournamespace.validateAuthCookie'] = 'yournamespace_validateAuthCookie';
return $methods;
}
add_filter('xmlrpc_methods', 'yournamespace_new_xmlrpc_methods');
Básicamente, expone un nuevo método XML-RPC con el que puedes pedirle a WordPress que valide la cookie wordpress_logged_in_...
.
Luego necesitas escribir algún código para consultar este método y pasarle el valor de la cookie wordpress_logged_in_...
.
Este método devolverá false
(si la cookie no es válida) o el ID de usuario si la validación es exitosa.
