tutorial servlet services restful example java rest jersey

servlet - web service rest java eclipse



Cómo autenticar usuarios en Jersey (3)

Estoy escribiendo una aplicación RESTful en Java usando Jersey, y necesito autenticar usuarios. Sé que puedo especificar los roles en el recurso utilizando las anotaciones @RolesAllowed, pero no puedo entender cómo un usuario está asociado a un rol específico. El cliente envía nombre de usuario y contraseña de esta manera

HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic(user, password); Client client = ClientBuilder.newClient(); client.register(feature); WebTarget target = client.target(baseUrl).path(urlString); Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON); Response response = invocationBuilder.get();

Suponiendo que algunos usuarios solo puedan usar algunos métodos, ¿cómo puedo distinguirlos cuando el cliente envía el nombre de usuario y la contraseña?


La clase HttpAuthenticationFeature proporciona capacidades de autenticación de cliente HttpBasic y Digest. La función funciona en uno de 4 modos;

BÁSICO: es una forma de autenticación preventiva, es decir, la información se envía siempre con cada solicitud HTTP. Este modo se debe combinar con el uso de SSL / TLS ya que la contraseña solo se envía con codificación BASE64.

BÁSICO NO PREVENTIVO: es una forma de autenticación no preventiva, es decir, la información de autenticación se agrega solo cuando el servidor rechaza la solicitud con el código de estado 401 y luego la solicitud se repite con la información de autenticación.

DIGEST: Http digest authentication. No requiere el uso de SSL / TLS.

UNIVERSAL: Combinación de autenticación básica y digestiva en modo no preventivo, es decir, en el caso de la respuesta 401, se utiliza una autenticación adecuada en función de la autenticación solicitada tal como se define en WWW-Authenticate HTTP header.

Para usar HttpAuthenticationFeature, construya una instancia y regístrese con el cliente. Por ejemplo;

1) Modo de autenticación básica

HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("username", "password"); final Client client = ClientBuilder.newClient(); client.register(feature);

2) Autenticación básica: modo no premptivo

HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder() .nonPreemptive() .credentials("username", "password") .build(); final Client client = ClientBuilder.newClient(); client.register(feature);

3) modo universal

//Universal builder having different credentials for different schemes HttpAuthenticationFeature feature = HttpAuthenticationFeature.universalBuilder() .credentialsForBasic("username1", "password1") .credentials("username2", "password2").build(); final Client client = ClientBuilder.newClient(); client.register(feature);


Hay dos cosas que debemos abordar

  1. Autenticación: verificando si el usuario es realmente el que dice ser
  2. Autorización: si el usuario autenticado tiene el privilegio de acceder al método proporcionado

Para hacer autenticación y autorización, necesitamos un almacén de datos que almacene la siguiente asignación:

  1. Mapeo entre el usuario y su contraseña
  2. Mapeo entre roles y usuarios
  3. Mapeo entre roles y permisos

Aquí se requiere la primera asignación para la autenticación y las otras dos asignaciones se usan para la autorización.

Además, tenga en cuenta que necesitamos hacer autenticación y autorización para cada llamada API. Entonces haremos muchas operaciones de lectura.

Por lo tanto, generalmente se usa un servidor de directorio o servidor Ldap como Apache DS para almacenar estas asignaciones porque un servidor de directorio es un almacén de datos optimizado para lectura.

En una aplicación RESTful, generalmente se usa un filtro para extraer el nombre de usuario y la contraseña del encabezado de la solicitud, y hacer la autenticación con el servidor Ldap. Si la autenticación es exitosa, el siguiente paso es extraer los permisos del usuario del servidor Ldap al consultar las asignaciones de rol de usuario y permiso de rol. Si el usuario está autorizado, solo en ese caso el control fluye a la lógica de negocio API real.

Consulte esta respuesta para más detalles.


Sé que puedo especificar los roles en el recurso usando las anotaciones @RolesAllowed, pero no puedo entender cómo un usuario está asociado a un rol específico

La información de rol se almacena en la base de datos. Supongamos que tiene un User que modela la tabla USUARIO y ROLES en la BD

class User { String username; List<String> roles; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public List<String> getRoles() { return roles; } public void setRoles(List<String> roles) { this.roles = roles; } }

Tendría al User dentro de un filtro Jersey. Aquí también es donde se autenticaría.

@Provider @Priority(Priorities.AUTHENTICATION) // needs to happen before authorization class AuthenticationFilter implements ContainerRequestFilter { @Inject private UserService userService; // this is your own service @Override public void filter(ContainerRequestFilter filter) { // note, this is a lazy implementation of Basic auth. // it doesn''t do ant error checking. Please see // link at bottom for better imlementation String authzHeader = filter.getHeaderString(HttpHeaders.AUTHORIZATION); // (1) String decoded = Base64.decodeAsString(authzHeader); String[] split = decoded.split(":"); User user = userService.getUser(split[0]); // (2) if (user == null || !user.getPassword().equals(someHash(split[1])) { // (3) throw new UnauthorizedException(); } SecurityContext oldContext = filter.getSecurityContext(); // (4) filter.setSecurityContext(new BasicSecurityConext(user, oldContext.isSecure())); } }

Lo que estás haciendo aquí es:

  1. Análisis del encabezado Autorización básica de autenticación
  2. Obtener el User con el nombre de usuario
  3. Haciendo tu autenticación
  4. Establecer un nuevo SecurityContext .

El BasicSecurityContext se muestra a continuación. Aquí es donde asociarás roles con el usuario.

static class BasicSecurityContext implements SecurityContext { private final User user; private final boolean secure; public BasicSecurityContext(User user, boolean secure) { this.user = user; this.secure = secure; } @Override public Principal getUserPrincipal() { return new Principal() { @Override public String getName() { return user.getUsername(); } }; } @Override public String getAuthenticationScheme() { return SecurityContext.BASIC_AUTH; } @Override public boolean isSecure() { return secure; } @Override public boolean isUserInRole(String role) { return user.getRoles().contains(role); } }

Si miras en la parte inferior en isUserInRole . Lo que sucederá es que Jersey agarrará la anotación @RolesAllowed del método o clase de recurso, tomará los valores y luego los pasará a isUserInRole . Si devuelve true , entonces el usuario está autorizado. En pseudo-código

@RolesAllowed({"USER", "SUPER_USER"}) public Response get() {} ... RolesAllowed annotation = resourceMethod.getAnnotation(RolesAllowed.class); String roles = annotation.value(); SecurityContext context = getSecurityContext(); for (String role: roles) { if (context.isUserInRole(role)) { return; } } throw new ForbiddenException();

Esto es solo un pseudocódigo, pero muestra cómo maneja Jersey la autorización, usando @RolesAllowed , SecurityContext y cómo implementa isUserInRole .

Esta función de autorización no se activa automáticamente. Tienes que encenderlo tú mismo. Para hacerlo, simplemente registre RolesAllowedDynamicFeature

public JerseyConfig extends ResourceConfig { public JerseyConfig() { register(RolesAllowedDynamicFeature.class); } }

Una cosa a tener en cuenta aquí es que en todo lo anterior, estamos implementando nuestra autenticación básica y configuración del contexto de seguridad. No hay nada realmente malo con esto. Pero si está utilizando el mecanismo de autenticación del contenedor de servlets, Jersey tomará realmente la información de autenticación de HttpServletRequest . El HttpServletRequest tiene un método getUserPrincipal() y un método isUserInRole . Jersey los usará para delegar en SecurityContext . Entonces, si usted es usuario de la autenticación del contenedor, entonces realmente no necesita implementar nada. Solo necesita registrar RolesAllowedDynamicFeature

Si desea utilizar el mecanismo de autenticación de su contenedor, debe consultar la documentación de su servidor. Después de haber configurado un reino con su servidor, necesitará configurar el web.xml con la información de seguridad. Hay un ejemplo en el siguiente enlace. También debe encontrar esta información en los documentos de Java EE en la sección de seguridad web.

Ver también: