Spring Security para uma API REST

Spring Security para uma API REST

1. Visão geral

Neste tutorial, aprendemos comoSecure a REST API using Spring and Spring Security 5.

Definiremos a segurança usando a configuração Java e usaremos uma abordagem de Login e Cookie para autenticação.

Leitura adicional:

Tutorial de autenticação do Spring Security

Como criar um processo de registro em nível de produção para novos usuários e fluxo de logon para usuários existentes.

Read more

Autoridade concedida versus função na segurança da primavera

Um guia rápido para a diferença entre uma autoridade concedida e uma função no Spring Security.

Read more

Autenticação básica do Spring Security

Configure a autenticação básica no Spring - a configuração XML, as mensagens de erro e exemplo de consumo de URLs protegidos com curl.

Read more

2. Ativar Spring Security

The architecture of Spring Security is based entirely on Servlet Filters. Portanto, isso ocorre antes do Spring MVC no que diz respeito ao processamento de solicitações HTTP.

A opção mais simples para registrar o filtro de segurança Spring é anotar nossa classe de configuração com@EnableWebSecurity:

@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

    // ...
}

Para um aplicativo não Spring Boot, podemos estenderAbstractSecurityWebApplicationInitializere passar nossa classe de configuração em seu construtor:

public class SecurityWebApplicationInitializer
  extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(SecurityJavaConfig.class);
    }
}

Ou então podemos declará-lo emweb.xml do aplicativo:


   springSecurityFilterChain
   org.springframework.web.filter.DelegatingFilterProxy


   springSecurityFilterChain
   /*

Devemos nomear o filtro‘springSecurityFilterChain' para corresponder ao bean padrão criado pelo Spring Security no contêiner.

Observe que o filtro definido não é a classe real que implementa a lógica de segurança. Em vez disso, é umDelegatingFilterProxy que delega os métodos do filtro para um bean interno. Isso é feito para que o bean de destino ainda possa se beneficiar do ciclo de vida e flexibilidade do contexto Spring.

O padrão de URL usado para configurar o filtro é/*, embora todo o serviço da web seja mapeado para/api/*. Isso fornece à configuração de segurança uma opção para proteger outros possíveis mapeamentos, se necessário.

3. Configuração de Spring Security Java

Podemos fazer configurações de segurança inteiramente em uma classe Java criando uma classe de configuração que estendeWebSecurityConfigurerAdaptere anotando-a com@EnableWebSecurity:

@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

    // ...
}

Agora, vamos criar usuários com funções diferentes emSecurityJavaConfig que usaremos para autenticar nossos endpoints de API:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
        .and()
        .withUser("user").password(encoder().encode("userPass")).roles("USER");
}

@Bean
public PasswordEncoder  encoder() {
    return new BCryptPasswordEncoder();
}

A seguir, vamos configurar a segurança para nossos endpoints de API:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    .csrf().disable()
    .exceptionHandling()
    .authenticationEntryPoint(restAuthenticationEntryPoint)
    .and()
    .authorizeRequests()
    .antMatchers("/api/foos").authenticated()
    .antMatchers("/api/admin/**").hasRole("ADMIN")
    .and()
    .formLogin()
    .successHandler(mySuccessHandler)
    .failureHandler(myFailureHandler)
    .and()
    .logout();
}

3.1. O elementohttp

O elementohttp é o elemento inicial para a configuração de segurança e nos fornece métodos fluentes e flexíveis para configurar a segurança.

Em nossa implementação, estamos criando mapeamentos protegidos/api/foos and/api/admin/** usingantMatchers. 

O padrão/api/foos pode ser acessado por qualquer usuário autenticado. Por outro lado,/api/admin/** will só estará acessível aADMIN usuários principais.

3.2. O ponto de entrada

Em um aplicativo Web padrão, o processo de autenticação pode ser acionado automaticamente quando um cliente não autenticado tenta acessar um recurso protegido. Esse processo geralmente redireciona para uma página de login para que o usuário possa inserir credenciais.

No entanto, para um serviço da Web REST, esse comportamento não faz muito sentido. We should be able to authenticate only by a request to the correct URI and if the user is not authenticated all requests should simply fail with a 401 UNAUTHORIZED status code.

Spring Security handles this automatic triggering of the authentication process with the concept of an Entry Point - esta é uma parte necessária da configuração e pode ser injetada por meio do métodoauthenticationEntryPoint .

Tendo em mente que essa funcionalidade não faz sentido no contexto do serviço REST, definimos o novo ponto de entrada personalizado para simplesmente retornar 401 quando acionado:

@Component
public final class RestAuthenticationEntryPoint
  implements AuthenticationEntryPoint {

    @Override
    public void commence(
        HttpServletRequest request,
        HttpServletResponse response,
        AuthenticationException authException) throws IOException {

        response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
          "Unauthorized");
    }
}

Uma observação rápida aqui é que o 401 é enviado sem o cabeçalhoWWW-Authenticate, conforme exigido pela especificação HTTP. É claro que podemos definir o valor manualmente, se precisarmos.

3.3. O formulário de login para REST

Existem várias maneiras de fazer a autenticação para uma API REST. Um dos padrões que o Spring Security fornece é o Form Login - que usa um filtro de processamento de autenticação -org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.

The formLogin element will create this filter and also provides additional methods successHandler and failureHandler para definir nossos gerenciadores de sucesso e falha de autenticação, respectivamente.

Observe que, para um aplicativo da web padrão, a anotação@EnableWebSecurity configura várias configurações padrão.

3.4. A autenticação deve retornar 200 em vez de 301

Por padrão, o login do formulário responderá a uma solicitação de autenticação bem-sucedida com um código de status301 MOVED PERMANENTLY; isso faz sentido no contexto de um formulário de login real que precisa ser redirecionado após o login.

No entanto,for a RESTful web service, the desired response for a successful authentication should be 200 OK.

We do this by injecting a custom authentication success handler no filtro de login do formulário, para substituir o padrão. O novo manipulador implementa exatamente o mesmo login que oorg.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler padrão com uma diferença notável - ele remove a lógica de redirecionamento:

public class MySavedRequestAwareAuthenticationSuccessHandler
  extends SimpleUrlAuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(
      HttpServletRequest request,
      HttpServletResponse response,
      Authentication authentication)
      throws ServletException, IOException {

        SavedRequest savedRequest
          = requestCache.getRequest(request, response);

        if (savedRequest == null) {
            clearAuthenticationAttributes(request);
            return;
        }
        String targetUrlParam = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl()
          || (targetUrlParam != null
          && StringUtils.hasText(request.getParameter(targetUrlParam)))) {
            requestCache.removeRequest(request, response);
            clearAuthenticationAttributes(request);
            return;
        }

        clearAuthenticationAttributes(request);
    }

    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
    }
}

3.5. A autenticação com falha deve retornar 401 em vez de 302

Da mesma forma - configuramos o manipulador de falhas de autenticação - da mesma maneira que fizemos com o manipulador de sucesso.

Felizmente - neste caso, não precisamos realmente definir uma nova classe para este manipulador - a implementação padrão -SimpleUrlAuthenticationFailureHandler - funciona bem.

3.6. O gerenciador e provedor de autenticação

O processo de autenticação usa umin-memory provider para realizar a autenticação. Isso visa simplificar a configuração, pois uma implementação de produção desses artefatos está fora do escopo deste artigo.

Criamos dois usuários, a saberuser função de barbearUSER areiaadmin com funçãoADMIN.

3.7. Finalmente - autenticação no serviço REST em execução

Agora vamos ver como podemos autenticar na API REST para diferentes usuários.

A URL para login é/login - e um comandocurl simples executando o login para o usuário de nomeusere senhauserPass seria:

curl -i -X POST -d username=user -d password=userPass
http://localhost:8080/spring-security-rest/login

Esta solicitação retornará o Cookie que podemos usar para qualquer solicitação subsequente contra o Serviço REST.

Podemos usarcurl para autenticação estore the cookie it receives in a file:

curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login

Em seguida,we can use the cookie from the file para fazer outras solicitações autenticadas:

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/foos

Como a varredurauser acessa o ponto de envio/api/foos/ , esta solicitação autenticada resultará corretamente * em um 200 OK:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 24 Jul 2013 20:31:13 GMT

[{"id":0,"name":"JbidXc"}]

Da mesma forma, paraadmin user, podemos usarcurl para autenticação:

curl -i -X POST -d username=admin -d password=adminPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login

e, em seguida, cookies atualizados para acessar pontos de extremidade de administrador/api/admin/*:

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/admin/x

Uma vez queadmin user pode acessar o endpoint/api/admin/* , isso resultará em uma resposta de sucesso:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 5
Date: Mon, 15 Oct 2018 17:16:39 GMT

Hello

4. A configuração de segurança XML

Também podemos fazer toda a configuração de segurança acima usando XML em vez de Java:


    

    

    






    
        
            
            
        
    

A maior parte da configuração é feita usando o namespace de segurança. Para habilitar isso, precisamos definir os locais do esquema.

O espaço para nome foi projetado para expressar os casos de uso comuns do Spring Security e ainda fornecer ganchos para beans brutos para acomodar cenários mais avançados.

5. Conclusão

Neste tutorial, abordamos a configuração básica de segurança e implementação de um serviço RESTful usando o Spring Security 5.

Aprendemos como fazer a configuração de segurança para nossa API REST inteiramente via configuração Java e também vimos sua alternativa de configuraçãoweb.xml.

Em seguida, discutimos como criar usuários e funções para nosso aplicativo seguro e também como mapear esses usuários para pontos de extremidade específicos de nosso aplicativo.

Por fim, também analisamos a criação de ponto de entrada de autenticação personalizado e um manipulador de sucesso personalizado que dava ao nosso aplicativo uma melhor flexibilidade em termos de controle de segurança.

A implementação completa está disponívelover on Github.