Mejores prácticas para la autenticación basada en token REST con JAX-RS y Jersey

459

Estoy buscando una manera de habilitar la autenticación basada en tokens en Jersey. Estoy tratando de no usar ningún marco en particular. ¿Es eso posible?

Mi plan es: un usuario se registra en mi servicio web, mi servicio web genera un token, lo envía al cliente y el cliente lo retendrá. Luego, el cliente, para cada solicitud, enviará el token en lugar de nombre de usuario y contraseña.

Estaba pensando en usar un filtro personalizado para cada solicitud y @PreAuthorize("hasRole('ROLE')") solo pensé que esto causa muchas solicitudes a la base de datos para verificar si el token es válido.

¿O no crear filtro y en cada solicitud poner un token param? Para que cada API compruebe primero el token y luego ejecute algo para recuperar el recurso.

DevOps85
fuente

Respuestas:

1388

Cómo funciona la autenticación basada en token

En la autenticación basada en token, el cliente intercambia credenciales (como nombre de usuario y contraseña) por un dato llamado token . Para cada solicitud, en lugar de enviar las credenciales físicas, el cliente enviará el token al servidor para realizar la autenticación y luego la autorización.

En pocas palabras, un esquema de autenticación basado en tokens sigue estos pasos:

  1. El cliente envía sus credenciales (nombre de usuario y contraseña) al servidor.
  2. El servidor autentica las credenciales y, si son válidas, genera un token para el usuario.
  3. El servidor almacena el token generado previamente en algún almacenamiento junto con el identificador de usuario y una fecha de vencimiento.
  4. El servidor envía el token generado al cliente.
  5. El cliente envía el token al servidor en cada solicitud.
  6. El servidor, en cada solicitud, extrae el token de la solicitud entrante. Con el token, el servidor busca los detalles del usuario para realizar la autenticación.
    • Si el token es válido, el servidor acepta la solicitud.
    • Si el token no es válido, el servidor rechaza la solicitud.
  7. Una vez que se ha realizado la autenticación, el servidor realiza la autorización.
  8. El servidor puede proporcionar un punto final para actualizar los tokens.

Nota: El paso 3 no es obligatorio si el servidor ha emitido un token firmado (como JWT, que le permite realizar tareas sin estado autenticación ).

Qué puede hacer con JAX-RS 2.0 (Jersey, RESTEasy y Apache CXF)

Esta solución usa solo la API JAX-RS 2.0, evitando cualquier solución específica del proveedor . Por lo tanto, debería funcionar con implementaciones JAX-RS 2.0, como Jersey , RESTEasy y Apache CXF .

Vale la pena mencionar que si está utilizando autenticación basada en tokens, no está confiando en los mecanismos de seguridad de aplicaciones web Java EE estándar que ofrece el contenedor de servlets y que se puede configurar a través del web.xmldescriptor de la aplicación . Es una autenticación personalizada.

Autenticar a un usuario con su nombre de usuario y contraseña y emitir un token

Cree un método de recurso JAX-RS que reciba y valide las credenciales (nombre de usuario y contraseña) y emita un token para el usuario:

@Path("/authentication")
public class AuthenticationEndpoint {

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response authenticateUser(@FormParam("username") String username, 
                                     @FormParam("password") String password) {

        try {

            // Authenticate the user using the credentials provided
            authenticate(username, password);

            // Issue a token for the user
            String token = issueToken(username);

            // Return the token on the response
            return Response.ok(token).build();

        } catch (Exception e) {
            return Response.status(Response.Status.FORBIDDEN).build();
        }      
    }

    private void authenticate(String username, String password) throws Exception {
        // Authenticate against a database, LDAP, file or whatever
        // Throw an Exception if the credentials are invalid
    }

    private String issueToken(String username) {
        // Issue a token (can be a random String persisted to a database or a JWT token)
        // The issued token must be associated to a user
        // Return the issued token
    }
}

Si se lanzan excepciones al validar las credenciales, 403se devolverá una respuesta con el estado (Prohibido).

Si las credenciales se validan correctamente, 200se devolverá una respuesta con el estado (OK) y el token emitido se enviará al cliente en la carga útil de respuesta. El cliente debe enviar el token al servidor en cada solicitud.

Al consumir application/x-www-form-urlencoded, el cliente debe enviar las credenciales en el siguiente formato en la carga útil de la solicitud:

username=admin&password=123456

En lugar de parámetros de formulario, es posible ajustar el nombre de usuario y la contraseña en una clase:

public class Credentials implements Serializable {

    private String username;
    private String password;

    // Getters and setters omitted
}

Y luego consumirlo como JSON:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response authenticateUser(Credentials credentials) {

    String username = credentials.getUsername();
    String password = credentials.getPassword();

    // Authenticate the user, issue a token and return a response
}

Con este enfoque, el cliente debe enviar las credenciales en el siguiente formato en la carga útil de la solicitud:

{
  "username": "admin",
  "password": "123456"
}

Extraer el token de la solicitud y validarlo

El cliente debe enviar el token en el Authorizationencabezado HTTP estándar de la solicitud. Por ejemplo:

Authorization: Bearer <token-goes-here>

El nombre del encabezado HTTP estándar es lamentable porque contiene información de autenticación , no autorización . Sin embargo, es el encabezado HTTP estándar para enviar credenciales al servidor.

JAX-RS proporciona @NameBindinguna metaanotación utilizada para crear otras anotaciones para unir filtros e interceptores a clases y métodos de recursos. Defina una @Securedanotación de la siguiente manera:

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }

La anotación de enlace de nombre definida anteriormente se usará para decorar una clase de filtro, que se implementa ContainerRequestFilter, lo que le permite interceptar la solicitud antes de que sea manejada por un método de recurso. Se ContainerRequestContextpuede usar para acceder a los encabezados de solicitud HTTP y luego extraer el token:

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private static final String REALM = "example";
    private static final String AUTHENTICATION_SCHEME = "Bearer";

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the Authorization header from the request
        String authorizationHeader =
                requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Validate the Authorization header
        if (!isTokenBasedAuthentication(authorizationHeader)) {
            abortWithUnauthorized(requestContext);
            return;
        }

        // Extract the token from the Authorization header
        String token = authorizationHeader
                            .substring(AUTHENTICATION_SCHEME.length()).trim();

        try {

            // Validate the token
            validateToken(token);

        } catch (Exception e) {
            abortWithUnauthorized(requestContext);
        }
    }

    private boolean isTokenBasedAuthentication(String authorizationHeader) {

        // Check if the Authorization header is valid
        // It must not be null and must be prefixed with "Bearer" plus a whitespace
        // The authentication scheme comparison must be case-insensitive
        return authorizationHeader != null && authorizationHeader.toLowerCase()
                    .startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
    }

    private void abortWithUnauthorized(ContainerRequestContext requestContext) {

        // Abort the filter chain with a 401 status code response
        // The WWW-Authenticate header is sent along with the response
        requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED)
                        .header(HttpHeaders.WWW_AUTHENTICATE, 
                                AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
                        .build());
    }

    private void validateToken(String token) throws Exception {
        // Check if the token was issued by the server and if it's not expired
        // Throw an Exception if the token is invalid
    }
}

Si se produce algún problema durante la validación del token, 401se devolverá una respuesta con el estado (No autorizado). De lo contrario, la solicitud procederá a un método de recurso.

Asegurar sus puntos finales REST

Para vincular el filtro de autenticación a métodos de recursos o clases de recursos, anótelos con la @Securedanotación creada anteriormente. Para los métodos y / o clases que están anotados, se ejecutará el filtro. Significa que dichos puntos finales solo se alcanzarán si la solicitud se realiza con un token válido.

Si algunos métodos o clases no necesitan autenticación, simplemente no los anote:

@Path("/example")
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myUnsecuredMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // The authentication filter won't be executed before invoking this method
        ...
    }

    @DELETE
    @Secured
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response mySecuredMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured
        // The authentication filter will be executed before invoking this method
        // The HTTP request must be performed with a valid token
        ...
    }
}

En el ejemplo que se muestra arriba, el filtro se ejecutará solo para el mySecuredMethod(Long)método porque está anotado con @Secured.

Identificando al usuario actual

Es muy probable que necesite conocer al usuario que realiza la solicitud contra su API REST. Se pueden utilizar los siguientes enfoques para lograrlo:

Anular el contexto de seguridad de la solicitud actual

Dentro de su ContainerRequestFilter.filter(ContainerRequestContext)método, SecurityContextse puede establecer una nueva instancia para la solicitud actual. Luego anule el SecurityContext.getUserPrincipal(), devolviendo una Principalinstancia:

final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {

        @Override
        public Principal getUserPrincipal() {
            return () -> username;
        }

    @Override
    public boolean isUserInRole(String role) {
        return true;
    }

    @Override
    public boolean isSecure() {
        return currentSecurityContext.isSecure();
    }

    @Override
    public String getAuthenticationScheme() {
        return AUTHENTICATION_SCHEME;
    }
});

Use el token para buscar el identificador de usuario (nombre de usuario), que será el Principalnombre del usuario .

Inyecte el SecurityContexten cualquier clase de recurso JAX-RS:

@Context
SecurityContext securityContext;

Lo mismo se puede hacer en un método de recurso JAX-RS:

@GET
@Secured
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myMethod(@PathParam("id") Long id, 
                         @Context SecurityContext securityContext) {
    ...
}

Y luego obtener el Principal:

Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();

Uso de CDI (inyección de contexto y dependencia)

Si, por algún motivo, no desea anular SecurityContext, puede usar CDI (Inyección de contexto y dependencia), que proporciona características útiles como eventos y productores.

Cree un calificador CDI:

@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface AuthenticatedUser { }

En su AuthenticationFiltercreado anteriormente, inyecte un Eventanotado con @AuthenticatedUser:

@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;

Si la autenticación tiene éxito, active el evento pasando el nombre de usuario como parámetro (recuerde, el token se emite para un usuario y el token se usará para buscar el identificador de usuario):

userAuthenticatedEvent.fire(username);

Es muy probable que haya una clase que represente a un usuario en su aplicación. Llamemos a esta clase User.

Cree un bean CDI para manejar el evento de autenticación, busque una Userinstancia con el nombre de usuario correspondiente y asígnelo al authenticatedUsercampo productor:

@RequestScoped
public class AuthenticatedUserProducer {

    @Produces
    @RequestScoped
    @AuthenticatedUser
    private User authenticatedUser;

    public void handleAuthenticationEvent(@Observes @AuthenticatedUser String username) {
        this.authenticatedUser = findUser(username);
    }

    private User findUser(String username) {
        // Hit the the database or a service to find a user by its username and return it
        // Return the User instance
    }
}

El authenticatedUsercampo produce una Userinstancia que se puede inyectar en beans gestionados por contenedor, como servicios JAX-RS, beans CDI, servlets y EJB. Use el siguiente código para inyectar una Userinstancia (de hecho, es un proxy CDI):

@Inject
@AuthenticatedUser
User authenticatedUser;

Tenga en cuenta que la @Producesanotación CDI es diferente de la @Producesanotación JAX-RS :

Asegúrese de usar la @Producesanotación CDI en su AuthenticatedUserProducerbean.

La clave aquí es el bean anotado con @RequestScoped , lo que le permite compartir datos entre los filtros y sus beans. Si no desea utilizar eventos, puede modificar el filtro para almacenar el usuario autenticado en un bean de ámbito de solicitud y luego leerlo desde sus clases de recursos JAX-RS.

En comparación con el enfoque que anula el SecurityContext , el enfoque CDI le permite obtener el usuario autenticado de beans que no sean recursos y proveedores JAX-RS.

Apoyar la autorización basada en roles

Por favor, consulte mi otra respuesta para obtener detalles sobre cómo respaldar la autorización basada en roles.

Emitir tokens

Un token puede ser:

  • Opaco: no revela más detalles que el valor en sí (como una cadena aleatoria)
  • Autocontenido: contiene detalles sobre el token en sí (como JWT).

Vea los detalles abajo:

Cadena aleatoria como token

Se puede emitir un token generando una cadena aleatoria y persistiéndola en una base de datos junto con el identificador de usuario y una fecha de vencimiento. Aquí se puede ver un buen ejemplo de cómo generar una cadena aleatoria en Java . También puedes usar:

Random random = new SecureRandom();
String token = new BigInteger(130, random).toString(32);

JWT (JSON Web Token)

JWT (JSON Web Token) es un método estándar para representar reclamos de forma segura entre dos partes y está definido por el RFC 7519 .

Es un token autónomo y le permite almacenar detalles en reclamaciones . Estos reclamos se almacenan en la carga útil del token, que es un JSON codificado como Base64 . Aquí hay algunos reclamos registrados en el RFC 7519 y lo que significan (lea el RFC completo para más detalles):

  • iss: Principal que emitió el token.
  • sub: Principal que es el tema de la JWT.
  • exp: Fecha de caducidad del token.
  • nbf: Hora en que el token comenzará a ser aceptado para su procesamiento.
  • iat: Hora en que se emitió el token.
  • jti: Identificador único para el token.

Tenga en cuenta que no debe almacenar datos confidenciales, como contraseñas, en el token.

El cliente puede leer la carga útil y la integridad del token se puede verificar fácilmente verificando su firma en el servidor. La firma es lo que evita que el token sea alterado.

No necesitará persistir los tokens JWT si no necesita rastrearlos. Sin embargo, al persistir los tokens, tendrás la posibilidad de invalidar y revocar el acceso a ellos. Para realizar un seguimiento de los tokens JWT, en lugar de conservar todo el token en el servidor, puede conservar el identificador de token (jti reclamo) junto con algunos otros detalles, como el usuario para el que emitió el token, la fecha de vencimiento, etc.

Cuando persista los tokens, siempre considere eliminar los viejos para evitar que su base de datos crezca indefinidamente.

Usando JWT

Hay algunas bibliotecas de Java para emitir y validar tokens JWT como:

Para encontrar otros recursos excelentes para trabajar con JWT, eche un vistazo a http://jwt.io .

Manejo de revocación de token con JWT

Si desea revocar tokens, debe mantener el seguimiento de ellos. No necesita almacenar todo el token en el lado del servidor, almacenar solo el identificador del token (que debe ser único) y algunos metadatos si es necesario. Para el identificador de token, puede usar UUID .

El jtireclamo debe usarse para almacenar el identificador de token en el token. Al validar el token, asegúrese de que no se haya revocado comprobando el valor del jtireclamo con los identificadores de token que tiene en el lado del servidor.

Por razones de seguridad, revoque todos los tokens para un usuario cuando cambien su contraseña.

Información Adicional

  • No importa qué tipo de autenticación decida usar. Siempre hágalo en la parte superior de una conexión HTTPS para evitar el ataque del hombre en el medio .
  • Eche un vistazo a esta pregunta de Seguridad de la información para obtener más información sobre los tokens.
  • En este artículo encontrará información útil sobre la autenticación basada en tokens.
cassiomolin
fuente
The server stores the previously generated token in some storage along with the user identifier and an expiration date. The server sends the generated token to the client. ¿Cómo es esto RESTful?
scottysseus
3
@scottyseus La autenticación basada en token funciona según cómo el servidor recuerda el token que ha emitido. Puede usar tokens JWT para autenticación sin estado.
cassiomolin
¿Qué pasa con el envío de contraseña hash en lugar de simple (hash con nonce generado por el servidor)? ¿Aumenta el nivel de seguridad (por ejemplo, cuando no se usa https)? En el caso de un hombre en el medio, podrá secuestrar una sesión, pero al menos no obtendrá la contraseña
Denis Itskovich
15
No puedo creer que esto no esté en la documentación oficial.
Daniel M.
2
@grep En REST, no hay tal cosa como sesión en el lado del servidor. En consecuencia, el estado de la sesión se gestiona en el lado del cliente.
cassiomolin
98

Esta respuesta tiene que ver con la autorización y es un complemento de mi respuesta anterior sobre la autenticación.

¿Por qué otra respuesta? Intenté expandir mi respuesta anterior agregando detalles sobre cómo admitir las anotaciones JSR-250. Sin embargo, la respuesta original se hizo demasiado larga y excedió la longitud máxima de 30,000 caracteres . Así que moví todos los detalles de autorización a esta respuesta, manteniendo la otra respuesta enfocada en realizar la autenticación y emitir tokens.


Apoyar la autorización basada en roles con la @Securedanotación

Además del flujo de autenticación que se muestra en la otra respuesta , la autorización basada en roles se puede admitir en los puntos finales REST.

Cree una enumeración y defina los roles según sus necesidades:

public enum Role {
    ROLE_1,
    ROLE_2,
    ROLE_3
}

Cambie la @Securedanotación de enlace de nombre creada anteriormente para admitir roles:

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
    Role[] value() default {};
}

Y luego anote las clases de recursos y métodos @Securedpara realizar la autorización. Las anotaciones de método anularán las anotaciones de clase:

@Path("/example")
@Secured({Role.ROLE_1})
public class ExampleResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response myMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // But it's declared within a class annotated with @Secured({Role.ROLE_1})
        // So it only can be executed by the users who have the ROLE_1 role
        ...
    }

    @DELETE
    @Path("{id}")    
    @Produces(MediaType.APPLICATION_JSON)
    @Secured({Role.ROLE_1, Role.ROLE_2})
    public Response myOtherMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured({Role.ROLE_1, Role.ROLE_2})
        // The method annotation overrides the class annotation
        // So it only can be executed by the users who have the ROLE_1 or ROLE_2 roles
        ...
    }
}

Cree un filtro con la AUTHORIZATIONprioridad, que se ejecuta después del AUTHENTICATIONfiltro de prioridad definido anteriormente.

Se ResourceInfopuede usar para obtener el recurso Methody el recurso Classque manejará la solicitud y luego extraer las @Securedanotaciones de ellos:

@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the resource class which matches with the requested URL
        // Extract the roles declared by it
        Class<?> resourceClass = resourceInfo.getResourceClass();
        List<Role> classRoles = extractRoles(resourceClass);

        // Get the resource method which matches with the requested URL
        // Extract the roles declared by it
        Method resourceMethod = resourceInfo.getResourceMethod();
        List<Role> methodRoles = extractRoles(resourceMethod);

        try {

            // Check if the user is allowed to execute the method
            // The method annotations override the class annotations
            if (methodRoles.isEmpty()) {
                checkPermissions(classRoles);
            } else {
                checkPermissions(methodRoles);
            }

        } catch (Exception e) {
            requestContext.abortWith(
                Response.status(Response.Status.FORBIDDEN).build());
        }
    }

    // Extract the roles from the annotated element
    private List<Role> extractRoles(AnnotatedElement annotatedElement) {
        if (annotatedElement == null) {
            return new ArrayList<Role>();
        } else {
            Secured secured = annotatedElement.getAnnotation(Secured.class);
            if (secured == null) {
                return new ArrayList<Role>();
            } else {
                Role[] allowedRoles = secured.value();
                return Arrays.asList(allowedRoles);
            }
        }
    }

    private void checkPermissions(List<Role> allowedRoles) throws Exception {
        // Check if the user contains one of the allowed roles
        // Throw an Exception if the user has not permission to execute the method
    }
}

Si el usuario no tiene permiso para ejecutar la operación, la solicitud se cancela con un 403(Prohibido).

Para conocer al usuario que realiza la solicitud, consulte mi respuesta anterior . Puede obtenerlo de SecurityContext(que ya debería estar configurado en ContainerRequestContext) o inyectarlo usando CDI, dependiendo del enfoque que elija.

Si una @Securedanotación no tiene roles declarados, puede asumir que todos los usuarios autenticados pueden acceder a ese punto final, sin tener en cuenta los roles que tienen los usuarios.

Soporte de autorización basada en roles con anotaciones JSR-250

Como alternativa a la definición de los roles en la @Securedanotación como se muestra arriba, podría considerar las anotaciones JSR-250 como @RolesAllowed, @PermitAlly @DenyAll.

JAX-RS no admite tales anotaciones listas para usar, pero podría lograrse con un filtro. Aquí hay algunas consideraciones a tener en cuenta si desea admitirlas todas:

Por lo tanto, un filtro de autorización que verifique las anotaciones JSR-250 podría ser como:

@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        Method method = resourceInfo.getResourceMethod();

        // @DenyAll on the method takes precedence over @RolesAllowed and @PermitAll
        if (method.isAnnotationPresent(DenyAll.class)) {
            refuseRequest();
        }

        // @RolesAllowed on the method takes precedence over @PermitAll
        RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
            return;
        }

        // @PermitAll on the method takes precedence over @RolesAllowed on the class
        if (method.isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // @DenyAll can't be attached to classes

        // @RolesAllowed on the class takes precedence over @PermitAll on the class
        rolesAllowed = 
            resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
        if (rolesAllowed != null) {
            performAuthorization(rolesAllowed.value(), requestContext);
        }

        // @PermitAll on the class
        if (resourceInfo.getResourceClass().isAnnotationPresent(PermitAll.class)) {
            // Do nothing
            return;
        }

        // Authentication is required for non-annotated methods
        if (!isAuthenticated(requestContext)) {
            refuseRequest();
        }
    }

    /**
     * Perform authorization based on roles.
     *
     * @param rolesAllowed
     * @param requestContext
     */
    private void performAuthorization(String[] rolesAllowed, 
                                      ContainerRequestContext requestContext) {

        if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
            refuseRequest();
        }

        for (final String role : rolesAllowed) {
            if (requestContext.getSecurityContext().isUserInRole(role)) {
                return;
            }
        }

        refuseRequest();
    }

    /**
     * Check if the user is authenticated.
     *
     * @param requestContext
     * @return
     */
    private boolean isAuthenticated(final ContainerRequestContext requestContext) {
        // Return true if the user is authenticated or false otherwise
        // An implementation could be like:
        // return requestContext.getSecurityContext().getUserPrincipal() != null;
    }

    /**
     * Refuse the request.
     */
    private void refuseRequest() {
        throw new AccessDeniedException(
            "You don't have permissions to perform this action.");
    }
}

Nota: La implementación anterior se basa en Jersey RolesAllowedDynamicFeature. Si usa Jersey, no necesita escribir su propio filtro, solo use la implementación existente.

cassiomolin
fuente
¿Hay algún repositorio de github con esta elegante solución disponible?
Daniel Ferreira Castro
66
@DanielFerreiraCastro Por supuesto. Echa un vistazo aquí .
cassiomolin
¿Hay alguna buena manera de validar que una solicitud es de un usuario autorizado Y ese usuario PUEDE cambiar los datos porque él "posee" los datos (por ejemplo, un hacker no puede usar su token para cambiar el nombre de otro usuario)? Sé que puedo verificar en cada punto final si el user_id== token.userId, o algo así, pero esto es muy repetitivo.
mFeinstein
@mFeinstein Una respuesta para eso seguramente requeriría más caracteres de los que puedo escribir aquí en los comentarios. Solo para darle alguna dirección, puede buscar seguridad a nivel de fila .
cassiomolin
Puedo ver muchos temas en las bases de datos para cuando busco seguridad a nivel de fila, abriré esto como una nueva pregunta entonces
mFeinstein