Tengo una API que necesita ser asegurado de dos maneras diferentes:
1) El uso de JWT para todos de que no sea 1 URL de solicitud que debe ser asegurado con autenticación básica
2) Basic Auth en una única URL.
Tengo las configuraciones de seguridad de configuración para ambos JWT y autenticación básica. Mi problema es que cuando hago una petición a la URL autenticados Basic utilizando un nombre de usuario y una contraseña válidos, Me autentica y hace su trabajo de almacenar datos dentro de Cassandra con éxito.
entonces esperar a tener que generar un token para todas las demás URL de solicitud de VIA / api / login y añadirlo a la autorización: Portador {} simbólico de cabecera ..
Sin embargo, si ive sido autenticado mediante autenticación básica, puedo entonces acceder a la otra URL (protegido por JWT auth) sin ni siquiera tener una ficha en la solicitud.
Cuando tengo acceso a la dirección URL de JWT protegida sin necesidad de autenticación con autenticación básica, tengo que enviar el token en la cabecera y funciona como se esperaba ..
¿Debo esperar esto? Como creo que a pesar de que ive verifica a través de autenticación básica para un punto final, todavía tendría que enviar códigos en la solicitud de todos los demás criterios de valoración JWT protegidos ..
He encontrado esta respuesta: SpringBoot adaptador de autenticación múltiple
y también este artículo: https://docs.spring.io/spring-security/site/docs/4.2.x/reference/htmlsingle/#multiple-httpsecurity
y trató de implementar las soluciones, pero el problema como se explica todavía ocurre.
La clase de configuración de seguridad es el siguiente:
@Configuration
@EnableWebSecurity
public class SecurityHttpConfig extends WebSecurityConfigurerAdapter {
@Configuration
@Order(1)
public static class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${basic.auth.user}")
private String basicAuthUsername;
@Value("${basic.auth.password}")
private String basicAuthPassword;
@Value("${crashboxx.consume.endpoint}")
private String crashBoxxConsumeEndpoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/v1/crash/consumeCrashBoxxEvent").hasRole("ADMIN").and()
.httpBasic().authenticationEntryPoint(getBasicAuthEntryPoint()).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);// We don't need sessions to be created.
}
@Bean
public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint() {
return new CustomBasicAuthenticationEntryPoint();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication().withUser(basicAuthUsername).password(encoder.encode(basicAuthPassword))
.roles("ADMIN");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Configuration
@Order(2)
public static class JwtWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Autowired
private JwtAuthenticationProvider jwtAuthenticationProvider;
// Any endpoints that require no authorization should be added here..
@Value("${api.login.endpoint}")
private String loginEndpoint;
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(jwtAuthenticationProvider);
}
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() {
return new JwtAuthenticationTokenFilter();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/login").permitAll().anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.headers().cacheControl();
}
}
Con la clase BasicAuthEntryPoint:
public class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private static final Gson gson = new Gson();
@Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
// Authentication failed, send error response.
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println(gson.toJson("HTTP Status 401 : " + authException.getMessage()));
}
@Override
public void afterPropertiesSet() throws Exception {
setRealmName("Realm");
super.afterPropertiesSet();
}
También los impl JWT:
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.header}")
private String tokenHeader;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
final String requestHeader = request.getHeader(tokenHeader);
// Ensure Auth Header contains 'Bearer'
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
String authToken = requestHeader.substring(7);
JwtAuthentication authentication = new JwtAuthentication(authToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
Espero que esto haya tenido sentido .. Si hay más preguntas por favor hágamelo saber, pero parece que no puede conseguir alrededor de éste.
He añadido el 'caso especial' primera que es la dirección URL de autenticación básica, pero todavía no hace ninguna diferencia.
Gracias
Código que envió en la configuración de seguridad de @Order(1)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/v1/crash/consumeCrashBoxxEvent").hasRole("ADMIN").and()
.httpBasic().authenticationEntryPoint(getBasicAuthEntryPoint()).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
Si este es el código exacto que esté utilizando, entonces su @Order(2)
configuración no será consultado. Será configuración muertos.
¡Dejame explicar!
http.authorizeRequests()
== http.antMatcher("/**").authorizeRequests()
En su primera configuración está utilizando un comodín y el resultado de su configuración se
/v1/crash/consumeCrashBoxxEvent
accesible si se ha autenticado el usuario y tiene función de administradorRest of URL's
accesible si se autentica usuario
Déjame adivinar lo que está sucediendo!
1. usted está golpeando URL /v1/crash/consumeCrashBoxxEvent
o any URL
se le pedirá para la autenticación básica.
2. Después de una autenticación exitosa se puede acceder a cualquier URL, porque usted es un usuario autenticado.
Sin embargo, si ive sido autenticado mediante autenticación básica, puedo entonces acceder a la otra URL (protegido por JWT auth) sin ni siquiera tener una ficha en la solicitud.
Porque como he dicho se puede acceder a cualquier URL porque eres un usuario autenticado
Cuando tengo acceso a la dirección URL de JWT protegida sin necesidad de autenticación con autenticación básica, tengo que enviar el token en la cabecera y funciona como se esperaba
Compruebe sin señal se puede acceder o no. Porque una vez que si inicia la sesión mediante la autenticación básica no hay cierre de sesión del lado del servidor (incluso si reinicia el servidor). Se puede lograr de cierre de sesión sólo si se cierra el navegador. Así lo prueba mediante el cierre y el inicio del navegador de nuevo. Y la prueba de que al no enviar token de JWT.
Garantizar también su solicitud alcanzar JwtAuthenticationTokenFilter, registros de depuración puesto a verificar.
Al igual que en su pregunta hay gran cantidad de abstracciones, es muy difícil predecir exactamente lo que está sucediendo a menos de poner un código completo.
Déjame saber en los comentarios si mis predicciones se desviaron de real.