Prática recomendada para autenticação baseada em token REST com JAX-RS e Jersey

459

Estou procurando uma maneira de habilitar a autenticação baseada em token em Jersey. Estou tentando não usar nenhuma estrutura específica. Isso é possível?

Meu plano é: um usuário se inscreve no meu serviço da Web, meu serviço da Web gera um token, envia-o ao cliente e o cliente o retém. Em seguida, o cliente, para cada solicitação, enviará o token em vez de nome de usuário e senha.

Eu estava pensando em usar um filtro personalizado para cada solicitação, @PreAuthorize("hasRole('ROLE')") mas achei que isso faz com que muitas solicitações ao banco de dados verifiquem se o token é válido.

Ou não criar filtro e em cada solicitação colocar um token de parâmetro? Para que cada API verifique primeiro o token e depois execute algo para recuperar o recurso.

DevOps85
fonte

Respostas:

1388

Como a autenticação baseada em token funciona

Na autenticação baseada em token, o cliente troca credenciais concretas (como nome de usuário e senha) por um dado chamado token . Para cada solicitação, em vez de enviar as credenciais físicas, o cliente enviará o token ao servidor para executar autenticação e autorização.

Em poucas palavras, um esquema de autenticação baseado em tokens segue estas etapas:

  1. O cliente envia suas credenciais (nome de usuário e senha) para o servidor.
  2. O servidor autentica as credenciais e, se forem válidas, gera um token para o usuário.
  3. O servidor armazena o token gerado anteriormente em algum armazenamento, juntamente com o identificador do usuário e uma data de validade.
  4. O servidor envia o token gerado para o cliente.
  5. O cliente envia o token para o servidor em cada solicitação.
  6. O servidor, em cada solicitação, extrai o token da solicitação recebida. Com o token, o servidor consulta os detalhes do usuário para executar a autenticação.
    • Se o token for válido, o servidor aceitará a solicitação.
    • Se o token for inválido, o servidor recusará a solicitação.
  7. Depois que a autenticação é realizada, o servidor executa a autorização.
  8. O servidor pode fornecer um terminal para atualizar os tokens.

Nota: A etapa 3 não será necessária se o servidor tiver emitido um token assinado (como JWT, que permite executar autenticação sem estado ).

O que você pode fazer com o JAX-RS 2.0 (Jersey, RESTEasy e Apache CXF)

Esta solução usa apenas a API JAX-RS 2.0, evitando qualquer solução específica do fornecedor . Portanto, ele deve funcionar com implementações JAX-RS 2.0, como Jersey , RESTEasy e Apache CXF .

Vale ressaltar que, se você estiver usando autenticação baseada em token, não estará confiando nos mecanismos de segurança padrão de aplicativos da web Java EE oferecidos pelo contêiner de servlet e configuráveis ​​por meio do web.xmldescritor do aplicativo . É uma autenticação personalizada.

Autenticando um usuário com seu nome de usuário e senha e emitindo um token

Crie um método de recurso JAX-RS que receba e valide as credenciais (nome de usuário e senha) e emita um token para o usuário:

@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
    }
}

Se alguma exceção for lançada ao validar as credenciais, uma resposta com o status 403(Proibido) será retornada.

Se as credenciais forem validadas com êxito, uma resposta com o status 200(OK) será retornada e o token emitido será enviado ao cliente na carga útil da resposta. O cliente deve enviar o token para o servidor em todas as solicitações.

Ao consumir application/x-www-form-urlencoded, o cliente deve enviar as credenciais no seguinte formato na carga útil da solicitação:

username=admin&password=123456

Em vez de parâmetros de formulário, é possível agrupar o nome de usuário e a senha em uma classe:

public class Credentials implements Serializable {

    private String username;
    private String password;

    // Getters and setters omitted
}

E consuma-o 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
}

Usando essa abordagem, o cliente deve enviar as credenciais no seguinte formato na carga útil da solicitação:

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

Extraindo o token da solicitação e validando-o

O cliente deve enviar o token no Authorizationcabeçalho HTTP padrão da solicitação. Por exemplo:

Authorization: Bearer <token-goes-here>

O nome do cabeçalho HTTP padrão é lamentável, pois carrega informações de autenticação , não autorização . No entanto, é o cabeçalho HTTP padrão para o envio de credenciais para o servidor.

O JAX-RS fornece @NameBindinguma meta-anotação usada para criar outras anotações para vincular filtros e interceptores a classes e métodos de recursos. Defina uma @Securedanotação da seguinte maneira:

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

A anotação de ligação de nome definida acima será usada para decorar uma classe de filtro, que é implementada ContainerRequestFilter, permitindo que você intercepte a solicitação antes de ser tratada por um método de recurso. O ContainerRequestContextpode ser usado para acessar os cabeçalhos de solicitação HTTP e, em seguida, extrair o 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
    }
}

Se ocorrer algum problema durante a validação do token, uma resposta com o status 401(Não autorizado) será retornada. Caso contrário, a solicitação prosseguirá para um método de recurso.

Protegendo seus terminais REST

Para vincular o filtro de autenticação a métodos ou classes de recursos, anote-os com a @Securedanotação criada acima. Para os métodos e / ou classes anotadas, o filtro será executado. Isso significa que esses pontos de extremidade serão alcançados apenas se a solicitação for executada com um token válido.

Se alguns métodos ou classes não precisarem de autenticação, simplesmente não os 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
        ...
    }
}

No exemplo mostrado acima, o filtro será executado apenas para o mySecuredMethod(Long)método porque está anotado com @Secured.

Identificando o usuário atual

É muito provável que você precise conhecer o usuário que está executando a solicitação novamente em sua API REST. As seguintes abordagens podem ser usadas para alcançá-lo:

Substituindo o contexto de segurança da solicitação atual

Dentro do seu ContainerRequestFilter.filter(ContainerRequestContext)método, uma nova SecurityContextinstância pode ser definida para a solicitação atual. Em seguida, substitua o SecurityContext.getUserPrincipal(), retornando uma Principalinstância:

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 o token para procurar o identificador de usuário (nome de usuário), que será o Principalnome do usuário .

Injete SecurityContextem qualquer classe de recurso JAX-RS:

@Context
SecurityContext securityContext;

O mesmo pode ser feito em um método de recurso JAX-RS:

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

E então obtenha o Principal:

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

Usando CDI (injeção de contexto e dependência)

Se, por algum motivo, você não quiser substituir o SecurityContext, poderá usar o CDI (Injeção de Contexto e Dependência), que fornece recursos úteis, como eventos e produtores.

Crie um qualificador CDI:

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

No AuthenticationFiltercriado acima, injete uma Eventanotação com @AuthenticatedUser:

@Inject
@AuthenticatedUser
Event<String> userAuthenticatedEvent;

Se a autenticação for bem-sucedida, ative o evento que passa o nome de usuário como parâmetro (lembre-se, o token é emitido para um usuário e o token será usado para procurar o identificador do usuário):

userAuthenticatedEvent.fire(username);

É muito provável que exista uma classe que represente um usuário no seu aplicativo. Vamos chamar essa classe User.

Crie um bean CDI para manipular o evento de autenticação, encontre uma Userinstância com o nome de usuário correspondente e atribua-o ao authenticatedUsercampo produtor:

@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
    }
}

O authenticatedUsercampo produz uma Userinstância que pode ser injetada nos beans gerenciados por contêiner, como serviços JAX-RS, beans CDI, servlets e EJBs. Use o seguinte trecho de código para injetar uma Userinstância (de fato, é um proxy CDI):

@Inject
@AuthenticatedUser
User authenticatedUser;

Observe que a @Producesanotação CDI é diferente da @Producesanotação JAX-RS :

Certifique-se de usar a @Producesanotação CDI no seu AuthenticatedUserProducerbean.

A chave aqui é o bean anotado @RequestScoped, permitindo que você compartilhe dados entre filtros e seus beans. Se você não quiser usar eventos, poderá modificar o filtro para armazenar o usuário autenticado em um bean com escopo de solicitação e, em seguida, lê-lo em suas classes de recurso JAX-RS.

Comparada à abordagem que substitui a SecurityContext, a abordagem CDI permite obter o usuário autenticado de beans que não sejam os recursos e provedores JAX-RS.

Suporte à autorização baseada em função

Consulte minha outra resposta para obter detalhes sobre como dar suporte à autorização baseada em função.

Emitindo tokens

Um token pode ser:

  • Opaco: não revela detalhes além do próprio valor (como uma sequência aleatória)
  • Independente: contém detalhes sobre o próprio token (como JWT).

Veja os detalhes abaixo:

Sequência aleatória como token

Um token pode ser emitido gerando uma sequência aleatória e persistindo em um banco de dados, juntamente com o identificador do usuário e uma data de validade. Um bom exemplo de como gerar uma sequência aleatória em Java pode ser visto aqui . Você também pode usar:

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

JWT (Token da Web JSON)

O JWT (JSON Web Token) é um método padrão para representar reivindicações de forma segura entre duas partes e é definido pelo RFC 7519 .

É um token independente e permite armazenar detalhes nas reivindicações . Essas declarações são armazenadas na carga útil do token, que é um JSON codificado como Base64 . Aqui estão algumas reivindicações registradas na RFC 7519 e o que elas significam (leia a RFC completa para obter mais detalhes):

  • iss: Principal que emitiu o token.
  • sub: Principal que é o assunto da JWT.
  • exp: Data de validade do token.
  • nbf: Hora em que o token começará a ser aceito para processamento.
  • iat: Hora em que o token foi emitido.
  • jti: Identificador exclusivo para o token.

Esteja ciente de que você não deve armazenar dados confidenciais, como senhas, no token.

A carga útil pode ser lida pelo cliente e a integridade do token pode ser facilmente verificada, verificando sua assinatura no servidor. A assinatura é o que impede que o token seja adulterado.

Você não precisará persistir tokens JWT se não precisar rastreá-los. No entanto, ao persistir os tokens, você terá a possibilidade de invalidar e revogar o acesso a eles. Para manter o controle dos tokens JWT, em vez de persistir em todo o token no servidor, você pode persistir no identificador de token ( jtireivindicação) junto com alguns outros detalhes, como o usuário para o qual emitiu o token, a data de validade etc.

Ao persistir tokens, sempre considere remover os antigos para impedir que seu banco de dados cresça indefinidamente.

Usando JWT

Existem algumas bibliotecas Java para emitir e validar tokens JWT, como:

Para encontrar outros ótimos recursos para trabalhar com o JWT, consulte http://jwt.io .

Manipulando a revogação de token com JWT

Se você deseja revogar os tokens, deve acompanhá-los. Você não precisa armazenar o token inteiro no lado do servidor, apenas o identificador do token (que deve ser exclusivo) e alguns metadados, se necessário. Para o identificador de token, você pode usar o UUID .

A jtideclaração deve ser usada para armazenar o identificador de token no token. Ao validar o token, verifique se ele não foi revogado, verificando o valor da jtideclaração nos identificadores de token existentes no lado do servidor.

Por motivos de segurança, revogue todos os tokens para um usuário quando ele alterar sua senha.

Informação adicional

  • Não importa que tipo de autenticação você decida usar. Sempre faça isso na parte superior de uma conexão HTTPS para impedir o ataque man-in-the-middle .
  • Dê uma olhada nesta pergunta da Segurança da informação para obter mais informações sobre tokens.
  • Neste artigo, você encontrará algumas informações úteis sobre autenticação baseada em token.
cassiomolin
fonte
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. Como isso é RESTful?
scottysseus 8/0318
3
@scottyseus A autenticação baseada em token funciona de acordo com a forma como o servidor se lembra do token emitido. Você pode usar tokens JWT para autenticação sem estado.
Cassiomolin # 9/18
Que tal enviar uma senha com hash em vez de simples (hash com nonce gerado pelo servidor)? Aumenta o nível de segurança (por exemplo, quando não está usando https)? No caso do homem no meio - ele será capaz de sequestrar a uma sessão, mas pelo menos ele não terá a senha
Denis Itskovich
15
Não acredito que isso não esteja na documentação oficial.
Daniel M.
2
@grep No REST, não existe sessão no lado do servidor. Conseqüentemente, o estado da sessão é gerenciado no lado do cliente.
Cassiomolin # 28/19
98

Esta resposta é sobre autorização e é um complemento da minha resposta anterior sobre autenticação

Por que outra resposta? Tentei expandir minha resposta anterior adicionando detalhes sobre como suportar as anotações do JSR-250. No entanto, a resposta original ficou muito longa e excedeu o tamanho máximo de 30.000 caracteres . Então, mudei todos os detalhes da autorização para essa resposta, mantendo a outra resposta focada na autenticação e emissão de tokens.


Suporte à autorização baseada em função com a @Securedanotação

Além do fluxo de autenticação mostrado na outra resposta , a autorização baseada em função pode ser suportada nos terminais REST.

Crie uma enumeração e defina as funções de acordo com suas necessidades:

public enum Role {
    ROLE_1,
    ROLE_2,
    ROLE_3
}

Altere a @Securedanotação de ligação de nome criada antes para suportar funções:

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

E anote as classes e métodos de recurso @Securedpara executar a autorização. As anotações do método substituirão as anotações da classe:

@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
        ...
    }
}

Crie um filtro com a AUTHORIZATIONprioridade, que é executado após o AUTHENTICATIONfiltro de prioridade definido anteriormente.

O ResourceInfopode ser usado para obter o recurso Methode recurso Classque irá lidar com o pedido e, em seguida, extrair os @Securedanotações a partir deles:

@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
    }
}

Se o usuário não tiver permissão para executar a operação, a solicitação será abortada com um 403(Proibido).

Para conhecer o usuário que está executando a solicitação, consulte minha resposta anterior . Você pode obtê-lo no SecurityContext(que já deve estar definido no ContainerRequestContext) ou injetá-lo usando o CDI, dependendo da abordagem a ser adotada.

Se uma @Securedanotação não possui funções declaradas, você pode assumir que todos os usuários autenticados podem acessar esse terminal, desconsiderando as funções que os usuários têm.

Suporte à autorização baseada em função com anotações JSR-250

Como alternativa, para definir as funções na @Securedanotação, como mostrado acima, você pode considerar anotações no JSR-250, como@RolesAllowed , @PermitAlle @DenyAll.

O JAX-RS não suporta essas anotações prontas para uso, mas pode ser alcançado com um filtro. Aqui estão algumas considerações a serem lembradas se você deseja dar suporte a todas elas:

Portanto, um filtro de autorização que verifica as anotações do JSR-250 pode 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: A implementação acima é baseada em Jersey RolesAllowedDynamicFeature. Se você usa Jersey, não precisa escrever seu próprio filtro, basta usar a implementação existente.

cassiomolin
fonte
Existe algum repositório github com esta solução elegante disponível?
Daniel Ferreira Castro
6
@DanielFerreiraCastro Claro. Dê uma olhada aqui .
Cassiomolin
Existe alguma maneira de validar que uma solicitação é de um usuário autorizado E que o usuário PODE alterar os dados porque ele "possui" os dados (por exemplo, para que um hacker não possa usar seu token para alterar o nome de outro usuário)? Eu sei que posso verificar em todos os pontos de extremidade se o user_id== token.userIdou algo parecido, mas isso é muito repetitivo.
MFeinstein 19/03/19
@mFeinstein Uma resposta para isso certamente exigiria mais caracteres do que posso digitar aqui nos comentários. Só para dar uma orientação, você pode procurar por segurança no nível da linha .
31419 Cassiomolin
Eu posso ver um monte de tópicos sobre bancos de dados para quando eu procurar segurança em nível de linha, eu vou abrir isso como uma nova questão então
mFeinstein