Hi guys, I need help.
I′m learning Spring WebFlux and jwt authentication, so I'm doing a REST service to practice, but this error won't let me continue, I don't know how to fix it.
Error:
io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:411) ~[jjwt-impl-0.11.1.jar:0.11.1]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ? org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ? org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ? HTTP GET "/api/v1/character/users" [ExceptionHandlingWebHandler]
Stack trace:
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:411) ~[jjwt-impl-0.11.1.jar:0.11.1]
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:541) ~[jjwt-impl-0.11.1.jar:0.11.1]
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:601) ~[jjwt-impl-0.11.1.jar:0.11.1]
at com.rostenross.webflux.configuration.JWTUtil.getClaimsFromToken(JWTUtil.java:53) ~[classes/:na]
at com.rostenross.webflux.configuration.JWTUtil.getUsernameFromToken(JWTUtil.java:58) ~[classes/:na]
at com.rostenross.webflux.configuration.AuthenticationManager.authenticate(AuthenticationManager.java:32) ~[classes/:na]
at com.rostenross.webflux.configuration.SecurityContextRepository.lambda$3(SecurityContextRepository.java:55) ~[classes/:na]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1784) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2346) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:169) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:110) ~[reactor-core-3.4.1.jar:3.4.1]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[reactor-core-3.4.1.jar:3.4.1]
at
Now, this is my code:
**JWTUtil: **
@Component
public class JWTUtil {
private Logger log = LoggerFactory.getLogger(JWTUtil.class);
private String secrete = "RostenRoss35689682_Nestor_Matias_Costantini_16/04/1991_programador-universitario_profesor-de-quimica";
private String expireTimeInMilliSec="30000";
public String generateToken(User user) {
Date now =new Date();
Map<String, Object> claim = new HashMap<>();
claim.put("alg", "HS256");
claim.put("typ", "JWT");
return Jwts.builder()
.signWith(SignatureAlgorithm.HS256,Base64.getEncoder().encode(secrete.getBytes()))
.setClaims(claim)
.setSubject(user.getUsername())
.setExpiration(new Date(System.currentTimeMillis() + 864000000))
.claim("rol", user.getAuthorities().stream().collect(Collectors.toList()))
.compact();
}
public Claims getClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(Base64.getEncoder().encodeToString(secrete.getBytes()))
.parseClaimsJws(token.replace("Bearer ", ""))
.getBody();
}
public String getUsernameFromToken(String token) {
return getClaimsFromToken(token).getSubject();
}
public Date getExpirationDate(String token) {
return getClaimsFromToken(token).getExpiration();
}
public Boolean isTokenExpired(String token) {
Date expirationDate = getExpirationDate(token);
log.info("isTokenExpired: "+expirationDate.before(new Date()));
return expirationDate.before(new Date());
}
public Boolean isTokenValidated(String token) {
return !isTokenExpired(token);
}
}
**SecurityContextRepository: **
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
private Logger log = LoggerFactory.getLogger(SecurityContextRepository.class);
@Autowired
private AuthenticationManager authenticationManager;
@Override
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
// TODO Auto-generated method stub
return Mono.empty();
}
@Override
public Mono<SecurityContext> load(ServerWebExchange exchange ) {
String bearer = "Bearer ";
return Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION))
.filter(b -> b.startsWith(bearer))
.map(subs -> {
log.info("Substring: "+subs.substring(7));
return subs.substring(7);
}
)
.flatMap(token ->
Mono.just(
new UsernamePasswordAuthenticationToken(token,
token
,Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
)
)
)
.flatMap(auth -> authenticationManager.authenticate(auth).map(SecurityContextImpl::new));
}
}
**AuthenticationManager: **
@Component
public class AuthenticationManager implements ReactiveAuthenticationManager{
private final Logger log = LoggerFactory.getLogger(AuthenticationManager.class);
@Autowired
private JWTUtil jwtUtil;
@Autowired
private UserRepository userRepository;
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
// TODO Auto-generated method stub
log.info("autenticaion inicio");
String token =authentication.getCredentials().toString();
String userName= jwtUtil.getUsernameFromToken(token);
return userRepository.findByUsername(userName)
.flatMap(userDetails -> {
log.info("role: "+userDetails.getAuthorities().toString());
if(userName.equals(userDetails.getUsername()) && jwtUtil.isTokenValidated(token)) {
return Mono.just(authentication);
}else {
log.info("userDetails: "+userDetails.toString());
return Mono.just(authentication);
}
});
}
}
SecurityConfig
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
private final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
@Autowired
private UserRepository repo;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private SecurityContextRepository securityContextRepository;
@Bean
ReactiveUserDetailsService userDdetailService() {
return (name) -> repo.findByUsername(name);
}
@Bean
public SecurityWebFilterChain securityWebFilterChain (ServerHttpSecurity http) {
return http.authorizeExchange(
authorizedExchangeSpec -> authorizedExchangeSpec
.pathMatchers("/api/v1/character/singin","/api/v1/character/login", "/api/v1/character/about","/api/v1/character/all","/api/v1/character/id=**","/api/v1/character/name=**")
.permitAll()
.anyExchange().authenticated()
)
.exceptionHandling()
.authenticationEntryPoint((response, error) -> Mono.fromRunnable(()->{
response.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
})).accessDeniedHandler((response,error) -> Mono.fromRunnable(()->{
response.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
})).and()
.httpBasic().disable()
.formLogin().disable()
.csrf().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.requestCache().requestCache(NoOpServerRequestCache.getInstance())
.and()
.build();
}
}
**pom.xml: **
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.rostenross.webflux</groupId>
<artifactId>CharactersAPI</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>CharactersAPI</name>
<description>TV and comics charactes reactive API.</
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…