¿Alguna vez ha necesitado crear un mecanismo de autenticación de inicio de sesión para una aplicación? Las probabilidades son, y probablemente más de una vez, con cada nueva implementación cercana, pero no idéntica, a la anterior. Por ejemplo, una implementación podría usar una base de datos Oracle, otra podría usar una autenticación NT y otra, un directorio LDAP (protocolo de directorio de acceso ligero). ¿No sería bueno admitir todos estos mecanismos de seguridad sin cambiar ningún código a nivel de aplicación?
Ahora, en el mundo de Java, puede hacerlo con el Servicio de autenticación y autorización de Java (JAAS). Esta API relativamente nueva era una extensión en J2SE (Java 2 Platform, Standard Edition) 1.3, es una API central en J2SE 1.4 y también es parte de la especificación J2EE (Java 2 Platform, Enterprise Edition) 1.3. En este artículo, le enseñaremos los conceptos básicos de JAAS y le mostraremos cómo aplicar JAAS de manera efectiva a aplicaciones del mundo real. Basamos la aplicación de este artículo en nuestras propias experiencias al integrar JAAS en un sistema Java existente basado en la Web que usaba un RDBMS (sistema de administración de bases de datos relacionales) para almacenar la información de inicio de sesión del usuario. Con JAAS, diseñamos mecanismos de autenticación e inicio de sesión más sólidos, flexibles y consistentes.
Puede descargar un conjunto completo de ejemplos de trabajo de los recursos a continuación (incluye fuentes Java, JSP (JavaServer Pages), configuración JAAS, con base de datos y scripts de compilación). Probamos estos ejemplos usando el servidor Resin con JDBC (Java Database Connectivity) y la base de datos MySQL.
Autenticación y autorización de Java: el panorama general
Antes de JAAS, el modelo de seguridad de Java estaba formado principalmente por su origen como un lenguaje independiente de la plataforma para aplicaciones distribuidas en red. En sus inicios, Java aparecía a menudo como código móvil, como los subprogramas basados en navegador y, por lo tanto, el modelo de seguridad inicial se centró en proteger a los usuarios en función de dónde se originó el código y quién lo creó. Los primeros mecanismos de seguridad de Java, como SecurityManager
s, el concepto de espacio aislado, la firma de código y los archivos de políticas, estaban destinados a proteger a los usuarios del sistema.
La invención de JAAS refleja la evolución de Java hacia un lenguaje de programación de propósito general, utilizado para implementar aplicaciones tradicionales de cliente y servidor que requieren inicio de sesión y control de acceso. JAAS protege el sistema de los usuarios al permitir o denegar el acceso según quién o qué ejecuta el programa. Si bien JAAS puede realizar tanto la autenticación como la autorización, en este artículo nos centramos principalmente en la autenticación.
JAAS puede simplificar su desarrollo de seguridad Java al colocar una capa de abstracción entre su aplicación y los distintos mecanismos de autenticación y autorización subyacentes. Esta independencia de plataformas y algoritmos le permite utilizar diferentes mecanismos de seguridad sin modificar su código a nivel de aplicación. Como ocurre con la mayoría de las API de seguridad de Java, JAAS logra esta independencia de implementación a través de un marco extensible de interfaces de proveedores de servicios conectables (SPI): un conjunto de clases e interfaces abstractas para las que se desarrollan implementaciones específicas.
La Figura 1 a continuación ofrece una descripción general de alto nivel de cómo JAAS logra esta capacidad de conexión. El código de la capa de aplicación se ocupa principalmente de un LoginContext
. Debajo LoginContext
hay un conjunto de uno o más mensajes de correo electrónico configurados dinámicamente LoginModule
, que manejan la autenticación real utilizando la infraestructura de seguridad adecuada.
JAAS proporciona algunas LoginModule
implementaciones de referencia , como JndiLoginModule
; también puede desarrollar el suyo propio, como haremos aquí con el RdbmsLoginModule
. También mostraremos cómo puede configurar rápidamente una aplicación con una variedad de implementaciones utilizando un archivo de configuración simple.
Además de ser conectable, JAAS es apilable: en el contexto de un único inicio de sesión, un conjunto de módulos de seguridad se pueden apilar uno encima del otro, cada uno llamado en orden e interactuando con una infraestructura de seguridad diferente.
Los aspectos de JAAS se basan en algunos patrones arquitectónicos de seguridad familiares y marcos existentes. La función apilable, por ejemplo, se parece deliberadamente al marco del módulo de autenticación conectable de Unix (PAM). Desde un punto de vista transaccional, JAAS adopta comportamientos similares a los protocolos de confirmación de dos fases (2PC). Los conceptos de configuración de seguridad de JAAS, incluidos los Policy
archivos y Permissions
, provienen de los paquetes de seguridad J2SE 1.2. JAAS también toma prestadas ideas de otros marcos de seguridad establecidos, como los certificados X.509, de los cuales Subject
se deriva el nombre (aprenderá más sobre esto más Subject
adelante).
Nota: JAAS es solo una de varias API de seguridad Java nuevas. Para obtener más información sobre la seguridad de Java, consulte la barra lateral "El rompecabezas de seguridad de Java" y los recursos a continuación.
JAAS del lado del cliente y del servidor
Puede aplicar JAAS tanto en el cliente como en el servidor. Usarlo en el lado del cliente es sencillo, como demostraremos en breve. En el lado del servidor, las cosas se vuelven un poco más complejas. Actualmente, JAAS en el mercado de servidores de aplicaciones es un poco inconsistente; Los servidores de aplicaciones J2EE usan JAAS de manera ligeramente diferente, dependiendo de cuál use. Por ejemplo, JBossSX, utilizando su propia arquitectura, integra muy bien JAAS en su marco de seguridad general (que se detalla en el excelente artículo JavaWorld de Scott Stark "Integrate Security Infrastructures with JBossSX" (agosto de 2001)). Sin embargo, aunque WebLogic 6.x admite JAAS, los detalles difieren.
Para que pueda comprender JAAS desde la perspectiva del lado del servidor y del cliente, mostraremos ejemplos de ambos en este artículo. Y por motivos de simplicidad en el servidor, usaremos el servidor de aplicaciones Resin para que podamos comenzar con una pizarra más limpia (Resin tiene un esquema de autenticación conectable propio, pero no es estándar, por lo que usar JAAS nos brinda más portabilidad opciones más tarde).
JAAS principal
Para comenzar con JAAS, primero debe asegurarse de que esté instalado. J2SE 1.4 ya incluye JAAS; J2SE 1.3 no lo hace. Si desea seguir utilizando J2SE 1.3, descargue JAAS de Sun Microsystems. Una vez que descargue e instale JAAS en un directorio determinado, verá un subdirectorio llamado lib
, que contiene un archivo llamado jaas.jar
. Deberá agregar este archivo a su classpath o copiarlo a su directorio de extensiones JRE (Java Runtime Environment) (en \lib\ext
, donde está la ubicación de su JRE). Entonces estará listo para JAAS. Nota: Si utiliza un servidor de aplicaciones, es posible que ya incluya JAAS. Consulte la documentación de su servidor para obtener más detalles.
Con cualquiera de estos enfoques, tenga en cuenta que puede cambiar algunos de los valores de propiedades del sistema relacionados con JAAS (así como muchos otros valores de seguridad de Java) en el archivo de propiedades de seguridad de Java. Este archivo, java.security
se encuentra en el /lib/security
directorio y está escrito en el formato de archivo de propiedades estándar de Java.
El uso de la autenticación JAAS desde su aplicación generalmente implica los siguientes pasos:
- Crear un
LoginContext
- Opcionalmente, pase un
CallbackHandler
alLoginContext
para recopilar o procesar datos de autenticación - Realice la autenticación llamando al método
LoginContext
'slogin()
- Realice acciones privilegiadas utilizando lo devuelto
Subject
(suponiendo que el inicio de sesión se realice correctamente)
Aquí tienes un ejemplo mínimo:
LoginContext lc = new LoginContext ("MiEjemplo"); intente {lc.login (); } catch (LoginException) {// Error de autenticación. } // Autenticación exitosa, ahora podemos continuar. // Podemos usar el Asunto devuelto si queremos. Asunto sub = lc.getSubject (); Subject.doAs (sub, nueva MyPrivilegedAction ());
Debajo de las sábanas, ocurren algunas otras cosas:
- Durante la inicialización,
LoginContext
busca la entrada de configuración"MyExample"
en un archivo de configuración JAAS (que usted configuró) para determinar quéLoginModule
s cargar (ver Figura 2) - Durante la conexión, la
LoginContext
llama a cada unoLoginModule
'slogin()
método - Cada
login()
método realiza la autenticación o alista unCallbackHandler
- Los
CallbackHandler
usos de uno o másCallback
s para interactuar con el usuario y se reúnen de entrada - A new
Subject
instance is populated with authentication details such asPrincipal
s and credentials
We'll explain further details below, but to begin, let's look at the key JAAS classes and interfaces involved in the process. These are typically divided into the following three groups:
Table 1. JAAS classes and interfaces
Common | Subject , Principal , credential (credential is not any specific class, but can be any object) |
Authentication | LoginContext , LoginModule , CallbackHandler , Callback |
Authorization | Policy , AuthPermission , PrivateCredentialPermission |
Most of these classes and interfaces are in the javax.security.auth
package's subpackages, with some prebuilt implementations in the com.sun.security.auth
package, included only in J2SE 1.4.
Note: Because we focus on authentication in this article, we don't delve into the authorization classes.
Common: Subjects, Principals, and Credentials
The Subject
class represents an authenticated entity: an end-user or administrator, or a Web service, device, or another process. The class contains three sets of security information types:
- Identities: In the form of one or more
Principal
s - Public credentials: Such as name or public keys
- Private credentials: Like passwords or private keys
Principal
s represent Subject
identities. They implement the java.security.Principal
interface (which predates JAAS) and java.io.Serializable
. A Subject
's most important method is getName()
, which returns an identity's string name. Since a Subject
instance contains an array of Principal
s, it can thus have multiple names. Because a social security number, login ID, email address, and so on, can all represent one user, multiple identities prove common in the real world.
The last element here, credential, is not a class or an interface, but can be any object. Credentials can include any authentication artifact, such as a ticket, key, or password, that specific security systems might require. The Subject
class maintains unique Set
s of private and public credentials, which can be retrieved with methods such as getPrivateCredentials()
and getPublicCrendentials()
. These methods are more often used by security subsystems than at the application layer.
Authentication: LoginContext
Your application layer uses LoginContext
as its primary class for authenticating Subject
s. LoginContext
also represents where JAAS's dynamic pluggability comes into play, because when you construct a LoginContext
, you specify a named configuration to load. The LoginContext
typically loads the configuration information from a text file, which in turn tells the LoginContext
which LoginModule
s to use during login.
The three commonly used methods in LoginContext
are:
Table 2. LoginContext methods
login() |
Performs login, a relatively complex step that invokes all LoginModule s specified for this configuration. If it succeeds, it creates an authenticated Subject . If it fails, it throws a LoginException . |
getSubject() |
Returns the authenticated Subject . |
logout() |
Logs out the authenticated Subject and removes its Principal s and credentials. |
We will show how to use these methods later.
Authentication: LoginModule
LoginModule
is the interface to specific authentication mechanisms. J2SE 1.4 ships with a set of ready-to-use LoginModules
, including:
Table 3. LoginModules in J2SE 1.4
JndiLoginModule |
Verifies against a directory service configured under JNDI (Java Naming and Directory Interface) |
Krb5LoginModule |
Authenticates using Kerberos protocols |
NTLoginModule |
Uses the current user's NT security information to authenticate |
UnixLoginModule |
Uses the current user's Unix security information to authenticate |
Along with these modules comes a set of corresponding concrete Principal
implementations in the com.sun.security.auth
package, such as NTDomainPrincipal
and UnixPrincipal
.
The LoginModule
interface has five methods:
Table 4. LoginModule methods
initialize() |
Called after the LoginModule is constructed. |
|
Performs the authentication. |
commit() |
Called by the LoginContext after it has accepted the results from all LoginModule s defined for this application. We assign Principal s and credentials to the Subject here. |
abort() |
Called when any LoginModule for this application fails (even though earlier ones in sequence may have succeeded—thus akin to a 2PC model). No Principal s or credentials are assigned to the Subject . |
logout() |
Removes the Principal s and credentials associated with the Subject . |
The application layer calls none of these methods directly—the LoginContext
invokes them as needed. Our example below will elaborate on these methods' implementations.
Authentication: CallbackHandlers and Callbacks
CallbackHandler
sys Callback
permiten LoginModule
recopilar la información de autenticación necesaria de un usuario o sistema, sin dejar de ser independiente del mecanismo de interacción real. Aprovecharemos esa capacidad en nuestro diseño; RdbmsLoginModule
no depende de cómo se obtengan las credenciales de usuario (nombre de usuario / contraseña) y, por lo tanto, se pueden usar en los diferentes entornos de aplicación que ilustraremos (ya sea desde la línea de comandos o desde una JSP) .