Este grupo de redactores ha estado horas buscando para darle respuesta a tus búsquedas, te dejamos la respuesta así que nuestro deseo es resultarte de mucha apoyo.
Solución:
Después de mucho buscar y probar, creo que he encontrado la solución:
Necesitas un frijol de SecurityWebFilterChain
que contiene toda la configuración.
Esto es mío:
@Configuration
public class SecurityConfiguration
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private SecurityContextRepository securityContextRepository;
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
// Disable default security.
http.httpBasic().disable();
http.formLogin().disable();
http.csrf().disable();
http.logout().disable();
// Add custom security.
http.authenticationManager(this.authenticationManager);
http.securityContextRepository(this.securityContextRepository);
// Disable authentication for `/auth/**` routes.
http.authorizeExchange().pathMatchers("/auth/**").permitAll();
http.authorizeExchange().anyExchange().authenticated();
return http.build();
He desactivado httpBasic, formLogin, csrf y logout para poder realizar mi autenticación personalizada.
Al establecer el AuthenticationManager
y SecurityContextRepository
Anulé la configuración de seguridad de primavera predeterminada para verificar si un usuario está autenticado / autorizado para una solicitud.
El administrador de autenticación:
@Component
public class AuthenticationManager implements ReactiveAuthenticationManager
@Override
public Mono authenticate(Authentication authentication)
// JwtAuthenticationToken is my custom token.
if (authentication instanceof JwtAuthenticationToken)
authentication.setAuthenticated(true);
return Mono.just(authentication);
No estoy completamente seguro de dónde está el administrador de autenticación, pero creo que para hacer la autenticación final, así que establezco authentication.setAuthenticated(true);
cuando todo va bien.
SecurityContextRepository:
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository
@Override
public Mono save(ServerWebExchange serverWebExchange, SecurityContext securityContext)
// Don't know yet where this is for.
return null;
@Override
public Mono load(ServerWebExchange serverWebExchange)
// JwtAuthenticationToken and GuestAuthenticationToken are custom Authentication tokens.
Authentication authentication = (/* check if authenticated based on headers in serverWebExchange */) ?
new JwtAuthenticationToken(...) :
new GuestAuthenticationToken();
return new SecurityContextImpl(authentication);
En la carga, lo verificaré en función de los encabezados en el serverWebExchange
si el usuario está autenticado. Yo uso https://github.com/jwtk/jjwt. Devuelvo un tipo diferente de token de autenticación si el usuario está autenticado o no.
Para aquellos que tienen el mismo problema (Webflux + Custom Authentication + JWT
) Lo resolví usando AuthenticationWebFilter
, personalizado ServerAuthenticationConverter
y ReactiveAuthenticationManager
, siguiendo el código, espero que pueda ayudar a alguien en el futuro. Probado con la última versión (spring-boot 2.2.4.RELEASE
).
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SpringSecurityConfiguration
@Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http)
return http
.csrf()
.disable()
.headers()
.frameOptions().disable()
.cache().disable()
.and()
.authorizeExchange()
.pathMatchers(AUTH_WHITELIST).permitAll()
.anyExchange().authenticated()
.and()
.addFilterAt(authenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.httpBasic().disable()
.formLogin().disable()
.logout().disable()
.build();
@Autowired private lateinit var userDetailsService: ReactiveUserDetailsService
class CustomReactiveAuthenticationManager(userDetailsService: ReactiveUserDetailsService?) : UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService)
override fun authenticate(authentication: Authentication): Mono
return if (authentication.isAuthenticated)
Mono.just(authentication)
else super.authenticate(authentication)
private fun responseError() : ServerAuthenticationFailureHandler
return ServerAuthenticationFailureHandler webFilterExchange: WebFilterExchange, _: AuthenticationException ->
webFilterExchange.exchange.response.statusCode = HttpStatus.UNAUTHORIZED
webFilterExchange.exchange.response.headers.addIfAbsent(HttpHeaders.LOCATION,"/")
webFilterExchange.exchange.response.setComplete();
private AuthenticationWebFilter authenticationWebFilter()
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(reactiveAuthenticationManager());
authenticationWebFilter.setServerAuthenticationConverter(new JwtAuthenticationConverter(tokenProvider));
NegatedServerWebExchangeMatcher negateWhiteList = new NegatedServerWebExchangeMatcher(ServerWebExchangeMatchers.pathMatchers(AUTH_WHITELIST));
authenticationWebFilter.setRequiresAuthenticationMatcher(negateWhiteList);
authenticationWebFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository());
authenticationWebFilter.setAuthenticationFailureHandler(responseError());
return authenticationWebFilter;
public class JwtAuthenticationConverter implements ServerAuthenticationConverter
private final TokenProvider tokenProvider;
public JwtAuthenticationConverter(TokenProvider tokenProvider)
this.tokenProvider = tokenProvider;
private Mono resolveToken(ServerWebExchange exchange)
log.debug("servletPath: ", exchange.getRequest().getPath());
return Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION))
.filter(t -> t.startsWith("Bearer "))
.map(t -> t.substring(7));
@Override
public Mono convert(ServerWebExchange exchange)
return resolveToken(exchange)
.filter(tokenProvider::validateToken)
.map(tokenProvider::getAuthentication);
public class CustomReactiveAuthenticationManager extends UserDetailsRepositoryReactiveAuthenticationManager
public CustomReactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService)
super(userDetailsService);
@Override
public Mono authenticate(Authentication authentication)
if (authentication.isAuthenticated())
return Mono.just(authentication);
return super.authenticate(authentication);
PD: la clase TokenProvider que encuentra en https://github.com/jhipster/jhipster-registry/blob/master/src/main/java/io/github/jhipster/registry/security/jwt/TokenProvider.java
En mi antiguo proyecto utilicé esta configuración:
@Configuration
@EnableWebSecurity
@Import(WebMvcConfig.class)
@PropertySource(value = "classpath:config.properties" , encoding = "UTF-8", ignoreResourceNotFound = false)
public class WebSecWebSecurityCfg extends WebSecurityConfigurerAdapter
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("objectMapper")
private ObjectMapper mapper;
@Autowired
@Qualifier("passwordEncoder")
private PasswordEncoder passwordEncoder;
@Autowired
private Environment env;
public WebSecWebSecurityCfg(UserDetailsService userDetailsService)
this.userDetailsService = userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception
JWTAuthorizationFilter authFilter = new JWTAuthorizationFilter
( authenticationManager(),//Auth mgr
env.getProperty("config.secret.symmetric.key"), //Chiave simmetrica
env.getProperty("config.jwt.header.string"), //nome header
env.getProperty("config.jwt.token.prefix") //Prefisso token
);
JWTAuthenticationFilter authenticationFilter = new JWTAuthenticationFilter
(
authenticationManager(), //Authentication Manager
env.getProperty("config.secret.symmetric.key"), //Chiave simmetrica
Long.valueOf(env.getProperty("config.jwt.token.duration")),//Durata del token in millisecondi
env.getProperty("config.jwt.header.string"), //nome header
env.getProperty("config.jwt.token.prefix"), //Prefisso token
mapper
);
http
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.addFilter(authenticationFilter)
.addFilter(authFilter)
// Disabilitiamo la creazione di sessione in spring
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
@Bean
CorsConfigurationSource corsConfigurationSource()
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
Dónde JWTAuthorizationFilter
es:
public class JWTAuthorizationFilter extends BasicAuthenticationFilter
{
private static final Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class.getName());
private String secretKey;
private String headerString;
private String tokenPrefix;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint, String secretKey, String headerString, String tokenPrefix)
super(authenticationManager, authenticationEntryPoint);
this.secretKey = secretKey;
this.headerString = headerString;
this.tokenPrefix = tokenPrefix;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, String secretKey, String headerString, String tokenPrefix)
super(authenticationManager);
this.secretKey = secretKey;
this.headerString = headerString;
this.tokenPrefix = tokenPrefix;
@Override
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException
AuthenticationErrorEnum customErrorCode = null;
StringBuilder builder = new StringBuilder();
if( failed.getCause() instanceof MissingJwtTokenException )
customErrorCode = AuthenticationErrorEnum.TOKEN_JWT_MANCANTE;
else if( failed.getCause() instanceof ExpiredJwtException )
customErrorCode = AuthenticationErrorEnum.TOKEN_JWT_SCADUTO;
else if( failed.getCause() instanceof MalformedJwtException )
customErrorCode = AuthenticationErrorEnum.TOKEN_JWT_NON_CORRETTO;
else if( failed.getCause() instanceof MissingUserSubjectException )
customErrorCode = AuthenticationErrorEnum.TOKEN_JWT_NESSUN_UTENTE_TROVATO;
else if( ( failed.getCause() instanceof GenericJwtAuthorizationException )
Y JWTAuthenticationFilter
es
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter
private AuthenticationManager authenticationManager;
private String secretKey;
private long tokenDurationMillis;
private String headerString;
private String tokenPrefix;
private ObjectMapper mapper;
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException
AuthenticationErrorEnum customErrorCode = null;
StringBuilder builder = new StringBuilder();
if( failed instanceof BadCredentialsException )
customErrorCode = AuthenticationErrorEnum.CREDENZIALI_SERVIZIO_ERRATE;
else
//Teoricamente nella fase di autenticazione all'errore generico non dovrebbe mai arrivare
customErrorCode = AuthenticationErrorEnum.ERRORE_GENERICO;
builder.append("Errore durante l'autenticazione del servizio. ");
builder.append(failed.getMessage());
JwtAuthApiError apiError = new JwtAuthApiError(HttpStatus.UNAUTHORIZED, failed.getMessage(), Arrays.asList(builder.toString()), customErrorCode);
String errore = mapper.writeValueAsString(apiError);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.sendError(HttpStatus.UNAUTHORIZED.value(), errore);
request.setAttribute(IRsConstants.API_ERROR_REQUEST_ATTR_NAME, apiError);
public JWTAuthenticationFilter(AuthenticationManager authenticationManager, String secretKey, long tokenDurationMillis, String headerString, String tokenPrefix, ObjectMapper mapper)
super();
this.authenticationManager = authenticationManager;
this.secretKey = secretKey;
this.tokenDurationMillis = tokenDurationMillis;
this.headerString = headerString;
this.tokenPrefix = tokenPrefix;
this.mapper = mapper;
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException
try
ServiceLoginDto creds = new ObjectMapper().readValue(req.getInputStream(), ServiceLoginDto.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(creds.getCodiceServizio(), creds.getPasswordServizio(), new ArrayList<>()));
catch (IOException e)
throw new RuntimeException(e);
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException
DateTime dt = new DateTime();
Date expirationTime = dt.plus(getTokenDurationMillis()).toDate();
String token = Jwts
.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(expirationTime)
.signWith(SignatureAlgorithm.HS512, getSecretKey().getBytes())
.compact();
res.addHeader(getHeaderString(), getTokenPrefix() + token);
res.addHeader("jwtExpirationDate", expirationTime.toString());
res.addHeader("jwtTokenDuration", String.valueOf(TimeUnit.MILLISECONDS.toMinutes(getTokenDurationMillis()))+" minuti");
public String getSecretKey()
return secretKey;
public void setSecretKey(String secretKey)
this.secretKey = secretKey;
public long getTokenDurationMillis()
return tokenDurationMillis;
public void setTokenDurationMillis(long tokenDurationMillis)
this.tokenDurationMillis = tokenDurationMillis;
public String getHeaderString()
return headerString;
public void setHeaderString(String headerString)
this.headerString = headerString;
public String getTokenPrefix()
return tokenPrefix;
public void setTokenPrefix(String tokenPrefix)
this.tokenPrefix = tokenPrefix;
El detalle del usuario es un detalle clásico del servicio al usuario.
@Service
public class UserDetailsServiceImpl implements UserDetailsService
@Autowired
private IServizioService service;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
Service svc;
try
svc = service.findBySvcCode(username);
catch (DbException e)
throw new UsernameNotFoundException("Errore durante il processo di autenticazione; "+e.getMessage(), e);
if (svc == null)
throw new UsernameNotFoundException("Nessun servizio trovato per il codice servizio "+username);
else if( !svc.getAbilitato().booleanValue() )
throw new UsernameNotFoundException("Servizio "+username+" non abilitato");
return new User(svc.getCodiceServizio(), svc.getPasswordServizio(), Collections.emptyList());
Tenga en cuenta que no utilicé Spring webflux
Espero que sea de utilidad
Angelo
Te mostramos las reseñas y valoraciones de los usuarios
Si te ha sido de utilidad nuestro artículo, sería de mucha ayuda si lo compartieras con el resto seniors de esta manera nos ayudas a extender este contenido.