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