A cadeia de filtros de segurança Spring é um mecanismo muito complexo e flexível.
Os principais filtros da cadeia são (na ordem)
- SecurityContextPersistenceFilter (restaura a autenticação de JSESSIONID)
- UsernamePasswordAuthenticationFilter (executa autenticação)
- ExceptionTranslationFilter (capturar exceções de segurança de FilterSecurityInterceptor)
- FilterSecurityInterceptor (pode gerar exceções de autenticação e autorização)
Examinando a documentação atual do release estável 4.2.1 , seção 13.3 Ordenação de filtros, você pode ver toda a organização de filtros da cadeia de filtros:
13.3 Solicitação de filtro
A ordem em que os filtros são definidos na cadeia é muito importante. Independentemente de quais filtros você está realmente usando, o pedido deve ser o seguinte:
ChannelProcessingFilter , porque pode ser necessário redirecionar para um protocolo diferente
SecurityContextPersistenceFilter , para que um SecurityContext possa ser configurado no SecurityContextHolder no início de uma solicitação da Web, e quaisquer alterações no SecurityContext podem ser copiadas no HttpSession quando a solicitação da Web terminar (pronta para uso com a próxima solicitação da Web)
ConcurrentSessionFilter , porque usa a funcionalidade SecurityContextHolder e precisa atualizar o SessionRegistry para refletir solicitações em andamento do principal
Mecanismos de processamento de autenticação -
UsernamePasswordAuthenticationFilter , CasAuthenticationFilter ,
BasicAuthenticationFilter etc - para que o SecurityContextHolder possa ser modificado para conter um token de solicitação de autenticação válido
O SecurityContextHolderAwareRequestFilter , se você estiver usando-o para instalar um HttpServletRequestWrapper compatível com o Spring Security no contêiner do servlet
O JaasApiIntegrationFilter , se um JaasAuthenticationToken estiver no SecurityContextHolder, isso processará o FilterChain como o Assunto no JaasAuthenticationToken
RememberMeAuthenticationFilter , para que, se nenhum mecanismo de processamento de autenticação anterior atualize o SecurityContextHolder, e a solicitação apresente um cookie que permita a realização de serviços de lembrança de mim, um objeto de autenticação lembrado adequado será colocado lá
AnonymousAuthenticationFilter , para que, se nenhum mecanismo de processamento de autenticação anterior atualizasse o SecurityContextHolder, um objeto de autenticação anônimo será colocado lá
ExceptionTranslationFilter , para capturar qualquer exceção do Spring Security, para que uma resposta de erro HTTP possa ser retornada ou um AuthenticationEntryPoint apropriado possa ser iniciado
FilterSecurityInterceptor , para proteger URIs da web e gerar exceções quando o acesso é negado
Agora, tentarei continuar suas perguntas uma a uma:
Estou confuso como esses filtros são usados. Será que para o formulário fornecido na primavera, o UsernamePasswordAuthenticationFilter é usado apenas para / login e os filtros posteriores não? O elemento do espaço para nome do formulário-login configura automaticamente esses filtros? Todas as solicitações (autenticadas ou não) chegam ao FilterSecurityInterceptor para obter um URL sem logon?
Depois de configurar um <security-http>
seção, para cada uma você deve fornecer pelo menos um mecanismo de autenticação. Esse deve ser um dos filtros que correspondem ao grupo 4 na seção 13.3 Ordenação de filtros da documentação do Spring Security que acabei de referenciar.
Este é o elemento http mínimo de segurança válido: que pode ser configurado:
<security:http authentication-manager-ref="mainAuthenticationManager"
entry-point-ref="serviceAccessDeniedHandler">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
</security:http>
Ao fazer isso, esses filtros são configurados no proxy da cadeia de filtros:
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"6": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"7": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"8": "org.springframework.security.web.session.SessionManagementFilter",
"9": "org.springframework.security.web.access.ExceptionTranslationFilter",
"10": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
Nota: Eu os obtenho criando um RestController simples que @Autowires the FilterChainProxy e retorna seu conteúdo:
@Autowired
private FilterChainProxy filterChainProxy;
@Override
@RequestMapping("/filterChain")
public @ResponseBody Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
return this.getSecurityFilterChainProxy();
}
public Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
Map<Integer, Map<Integer, String>> filterChains= new HashMap<Integer, Map<Integer, String>>();
int i = 1;
for(SecurityFilterChain secfc : this.filterChainProxy.getFilterChains()){
//filters.put(i++, secfc.getClass().getName());
Map<Integer, String> filters = new HashMap<Integer, String>();
int j = 1;
for(Filter filter : secfc.getFilters()){
filters.put(j++, filter.getClass().getName());
}
filterChains.put(i++, filters);
}
return filterChains;
}
Aqui pudemos ver que apenas declarando o <security:http>
elemento com uma configuração mínima, todos os filtros padrão estão incluídos, mas nenhum deles é do tipo Autenticação (quarto grupo na seção 13.3 Ordenação de filtros). Portanto, na verdade, significa que apenas declarando o security:http
elemento, o SecurityContextPersistenceFilter, o ExceptionTranslationFilter e o FilterSecurityInterceptor são configurados automaticamente.
De fato, um mecanismo de processamento de autenticação deve ser configurado e até os beans de namespace de segurança processam reivindicações para isso, gerando um erro durante a inicialização, mas isso pode ser ignorado pela adição de um atributo de ponto de entrada-ref em <http:security>
Se eu adicionar um básico <form-login>
à configuração, desta maneira:
<security:http authentication-manager-ref="mainAuthenticationManager">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
<security:form-login />
</security:http>
Agora, o filterChain ficará assim:
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter",
"6": "org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter",
"7": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"8": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"9": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"10": "org.springframework.security.web.session.SessionManagementFilter",
"11": "org.springframework.security.web.access.ExceptionTranslationFilter",
"12": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
Agora, esses dois filtros org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter e org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter são criados e configurados no FilterChainProxy.
Então, agora, as perguntas:
Será que para o formulário fornecido na primavera, o UsernamePasswordAuthenticationFilter é usado apenas para / login e os filtros posteriores não?
Sim, é usado para tentar concluir um mecanismo de processamento de login, caso a solicitação corresponda ao URL UsernamePasswordAuthenticationFilter. Esse URL pode ser configurado ou até alterado seu comportamento para corresponder a todas as solicitações.
Você também pode ter mais de um mecanismo de processamento de autenticação configurado no mesmo FilterchainProxy (como HttpBasic, CAS, etc.).
O elemento do espaço para nome do formulário-login configura automaticamente esses filtros?
Não, o elemento form-login configura o UsernamePasswordAUthenticationFilter e, caso você não forneça um URL da página de login, ele também configura o org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter, que termina em um login simples gerado automaticamente página.
Os outros filtros são configurados automaticamente por padrão apenas criando um <security:http>
elemento sem security:"none"
atributo.
Todas as solicitações (autenticadas ou não) chegam ao FilterSecurityInterceptor para obter um URL sem logon?
Cada solicitação deve alcançá-lo, pois é o elemento que cuida se a solicitação tem os direitos de atingir o URL solicitado. Mas alguns dos filtros processados anteriormente podem parar o processamento da cadeia de filtros, simplesmente não sendo chamados FilterChain.doFilter(request, response);
. Por exemplo, um filtro CSRF pode parar o processamento da cadeia de filtros se a solicitação não tiver o parâmetro csrf.
E se eu quiser proteger minha API REST com o token JWT, que é recuperado do login? Preciso configurar duas tags http de configuração de namespace, direitos? Outro para / login com UsernamePasswordAuthenticationFilter
e outro para URLs REST, com customização JwtAuthenticationFilter
.
Não, você não é forçado a fazer isso. Você pode declarar ambos UsernamePasswordAuthenticationFilter
e o JwtAuthenticationFilter
mesmo elemento http, mas isso depende do comportamento concreto de cada um desses filtros. Ambas as abordagens são possíveis e a escolha final depende das próprias preferências.
A configuração de dois elementos http cria dois springSecurityFitlerChains?
Sim, é verdade
O UsernamePasswordAuthenticationFilter está desativado por padrão, até que eu declare o login do formulário?
Sim, você pode vê-lo nos filtros criados em cada uma das configurações que publiquei
Como substituo o SecurityContextPersistenceFilter por um que obtém a autenticação do token JWT existente em vez do JSESSIONID?
Você pode evitar o SecurityContextPersistenceFilter, apenas configurando a estratégia de sessão no <http:element>
. Basta configurar assim:
<security:http create-session="stateless" >
Ou, nesse caso, você pode substituí-lo por outro filtro, desta forma dentro do <security:http>
elemento:
<security:http ...>
<security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>
</security:http>
<beans:bean id="myCustomFilter" class="com.xyz.myFilter" />
EDITAR:
Uma pergunta sobre "Você também pode ter mais de um mecanismo de processamento de autenticação configurado no mesmo FilterchainProxy". O último substituirá a autenticação executada pelo primeiro, se declarar vários filtros de autenticação (implementação Spring)? Como isso está relacionado a ter vários provedores de autenticação?
Finalmente, isso depende da implementação de cada filtro em si, mas é verdade que os últimos filtros de autenticação são capazes de sobrescrever qualquer autenticação anterior eventualmente feita pelos filtros anteriores.
Mas isso não acontece necessariamente. Eu tenho alguns casos de produção em serviços REST protegidos, onde eu uso um tipo de token de autorização que pode ser fornecido como um cabeçalho Http ou dentro do corpo da solicitação. Portanto, configurei dois filtros que recuperam esse token, em um caso do cabeçalho Http e o outro no corpo da solicitação da própria solicitação de descanso. É verdade que, se uma solicitação http fornecer esse token de autenticação como cabeçalho Http e dentro do corpo da solicitação, ambos os filtros tentarão executar o mecanismo de autenticação que o delegará ao gerente, mas isso poderá ser facilmente evitado simplesmente verificando se a solicitação está correta. já autenticado apenas no início do doFilter()
método de cada filtro.
Ter mais de um filtro de autenticação está relacionado a ter mais de um provedor de autenticação, mas não o force. No caso exposto anteriormente, tenho dois filtros de autenticação, mas apenas um provedor de autenticação, pois os dois filtros criam o mesmo tipo de objeto de autenticação. Nos dois casos, o gerenciador de autenticação o delega ao mesmo provedor.
E, ao contrário disso, também tenho um cenário em que publico apenas um UsernamePasswordAuthenticationFilter, mas as credenciais do usuário podem estar contidas no DB ou LDAP, por isso tenho dois provedores de suporte de UsernamePasswordAuthenticationToken e o AuthenticationManager delega qualquer tentativa de autenticação do filtro nos provedores para validar as credenciais.
Portanto, acho claro que nem a quantidade de filtros de autenticação determina a quantidade de provedores de autenticação nem a quantidade de provedor determina a quantidade de filtros.
Além disso, a documentação afirma que SecurityContextPersistenceFilter é responsável por limpar o SecurityContext, que é importante devido ao pool de encadeamentos. Se eu omitir ou fornecer implementação personalizada, tenho que implementar a limpeza manualmente, certo? Existem truques mais semelhantes ao personalizar a cadeia?
Eu não examinei cuidadosamente esse filtro antes, mas após sua última pergunta, estive verificando sua implementação e, como normalmente no Spring, quase tudo poderia ser configurado, estendido ou substituído.
O SecurityContextPersistenceFilter delega em uma implementação SecurityContextRepository a procura pelo SecurityContext. Por padrão, um HttpSessionSecurityContextRepository é usado, mas isso pode ser alterado usando um dos construtores do filtro. Portanto, pode ser melhor escrever um SecurityContextRepository que atenda às suas necessidades e apenas configurá-lo no SecurityContextPersistenceFilter, confiando no seu comportamento comprovado, em vez de começar a fazer tudo do zero.