Tenemos la mejor solución que hemos encontrado por todo internet. Nosotros esperamos que te sea de mucha utilidad y si deseas compartir cualquier detalle que nos pueda ayudar a crecer hazlo con total libertad.
Solución:
Cree un filtro que tome los encabezados que esté utilizando para la autenticación.
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter
private String principalRequestHeader;
public APIKeyAuthFilter(String principalRequestHeader)
this.principalRequestHeader = principalRequestHeader;
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request)
return request.getHeader(principalRequestHeader);
@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request)
return "N/A";
Configure el filtro en su configuración de seguridad web.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@Configuration
@EnableWebSecurity
@Order(1)
public class APISecurityConfig extends WebSecurityConfigurerAdapter
@Value("$yourapp.http.auth-token-header-name")
private String principalRequestHeader;
@Value("$yourapp.http.auth-token")
private String principalRequestValue;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
filter.setAuthenticationManager(new AuthenticationManager()
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
String principal = (String) authentication.getPrincipal();
if (!principalRequestValue.equals(principal))
throw new BadCredentialsException("The API key was not found or not the expected value.");
authentication.setAuthenticated(true);
return authentication;
);
httpSecurity.
antMatcher("/api/**").
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().addFilter(filter).authorizeRequests().anyRequest().authenticated();
Me doy cuenta de que llegué un poco tarde al juego en este caso, pero también logré obtener API keys trabajar con Spring Boot junto con la autenticación de nombre de usuario/contraseña. No estaba loco por la idea de usar AbstractPreAuthenticatedProcessingFilter
porque al leer el JavaDoc, parecía un mal uso de esa clase en particular.
Terminé creando una nueva ApiKeyAuthenticationToken
class junto con un filtro de servlet sin procesar bastante simple para lograr esto:
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.Transient;
@Transient
public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken
private String apiKey;
public ApiKeyAuthenticationToken(String apiKey, Collection extends GrantedAuthority> authorities)
super(authorities);
this.apiKey = apiKey;
setAuthenticated(true);
@Override
public Object getCredentials()
return null;
@Override
public Object getPrincipal()
return apiKey;
y el filtro
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
public class ApiKeyAuthenticationFilter implements Filter
static final private String AUTH_METHOD = "api-key";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
if(request instanceof HttpServletRequest && response instanceof HttpServletResponse)
String apiKey = getApiKey((HttpServletRequest) request);
if(apiKey != null)
if(apiKey.equals("my-valid-api-key"))
ApiKeyAuthenticationToken apiToken = new ApiKeyAuthenticationToken(apiKey, AuthorityUtils.NO_AUTHORITIES);
SecurityContextHolder.getContext().setAuthentication(apiToken);
else
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(401);
httpResponse.getWriter().write("Invalid API Key");
return;
chain.doFilter(request, response);
private String getApiKey(HttpServletRequest httpRequest)
String apiKey = null;
String authHeader = httpRequest.getHeader("Authorization");
if(authHeader != null)
authHeader = authHeader.trim();
if(authHeader.toLowerCase().startsWith(AUTH_METHOD + " "))
apiKey = authHeader.substring(AUTH_METHOD.length()).trim();
return apiKey;
Todo lo que queda en este punto es inyectar el filtro en la ubicación adecuada de la cadena. En mi caso, quería API key la autenticación se evaluará antes de cualquier autenticación de nombre de usuario/contraseña para que pueda autenticar la solicitud antes de que la aplicación intente redirigir a una página de inicio de sesión:
@Override
protected void configure(HttpSecurity http) throws Exception
http
.csrf()
.disable()
.addFilterBefore(new ApiKeyAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin();
Otra cosa que diré que debe tener en cuenta es que su API key las solicitudes autenticadas no crean y abandonan un montón de HttpSession
s en su servidor.
Sección de Reseñas y Valoraciones
Finalizando este artículo puedes encontrar las notas de otros usuarios, tú igualmente tienes el poder dejar el tuyo si lo deseas.