Io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature - spring

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.</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
If you need more information let me know.
I've looked everywhere on the web and found nothing.
Thanks ind advance guys.

Related

Invalid CSRF Token returned spring-security 6 + WebFluxSecurity

Description:
Disabled the csrf token on my API with while configuring with spring security below versions
spring-boot-parent: 3.0.1
spring-security: 6.0.1
But while accessing services it throws "Invalid CSRF Token". Unable to fix it along with permitted URI's.
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
#AllArgsConstructor
#AutoConfiguration
public ApiConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.headers().contentSecurityPolicy("script-src 'self'");
http
.exceptionHandling()
.authenticationEntryPoint((swe, e) ->
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED))
).accessDeniedHandler((swe, e) ->
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN))
).and()
.httpBasic().disable()
.formLogin().disable()
.authenticationManager((ReactiveAuthenticationManager) authenticationManager)
.securityContextRepository((ServerSecurityContextRepository) securityContextRepository)
.authorizeExchange()
.pathMatchers("/", "/**",
"/api/**",
"/api/swagger-ui.html",
"/webjars/**",
"/api/webjars/**",
"/actuator/health",
).permitAll()
.and().redirectToHttps(withDefaults()).authorizeExchange()
.anyExchange().authenticated().build();
}
}
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!--<version>2.6.6</version>-->
<version>3.0.1</version>
<relativePath/>
</parent>
<java.version>17</java.version>
<spring-cloud.version>2022.0.0</spring-cloud.version>
<spring-security.version>6.0.1</spring-security.version>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
**On other side I have tried using below **
Below code works and allow me to access the /test service without any issue:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
</parent>
<java.version>17</java.version>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
#AllArgsConstructor
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class WebSecurityConfig {
private AuthenticationManager authenticationManager;
private SecurityContextRepository securityContextRepository;
#Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
return http
.exceptionHandling()
.authenticationEntryPoint((swe, e) ->
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED))
).accessDeniedHandler((swe, e) ->
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN))
).and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers("/test").permitAll()
.anyExchange().authenticated()
.and().build();
}
}

spring boot 2 authorization server - all endpoints return 404

I have an application with an authorization server (OAuth2) which I migrated from Spring Boot 1.X to 2.X. Whenever I try to access any of the endpoint functions (e.g. /oauth/token or /oauth/token_key) it results in HTTP-404. The service itself is up which I can see in /actuator/health. It was working without problems with the old Spring Boot.
I already tried to enable all services but without success. I'm using Spring Boot 2, Resilience4J, and OAuth2.
This is my code:
AuthApplication
#SpringBootApplication
#EnableFeignClients
#EnableCircuitBreaker
#EnableAutoConfiguration
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
AuthServerConfig
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailsService userDetailsService;
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
#Bean
public OAuth2AccessDeniedHandler oauthAccessDeniedHandler() {
return new OAuth2AccessDeniedHandler();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authorizationCodeServices(authorizationCodeServices())
.authenticationManager(this.authenticationManager)
.tokenStore(this.tokenStore())
.userDetailsService(this.userDetailsService)
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme").secret("acme")
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.scopes("openid", "request", "write", "read");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()");
}
}
SecurityConfiguration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous()
.and()
.authorizeRequests()
.anyRequest().permitAll()
.and().logout().permitAll().logoutSuccessUrl("/")
.and().httpBasic().disable().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(this.passwordEncoder());
}
protected PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
I also added this to the bootstrap.yml in the hope it would help, but it didn't:
management:
endpoints:
web:
exposure:
include: "*"
Why are non of the endpoints of the authorization server available?
EDIT 1
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.3.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.domain</groupId>
<artifactId>auth</artifactId>
<version>0.0.2-SNAPSHOT</version>
<name>auth</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
<docker.image.prefix>app</docker.image.prefix>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.3.6</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>

FindByIndexNameSessionRepository “RedisConnectionFactory is required” configuration error

Implementing the logic of limiting active sessions for each user (number of possible sessions stored in the database, sessions in Redis), I came across the problem of configuring FindByIndexNameSessionRepository.
Due to the fact that I am injecting FindByIndexNameSessionRepository spring starts to configure the bean in the RedisHttpSessionConfiguration class, and the fields are not injected into the class via setters.
As a result, I get the error:
Caused by: java.lang.IllegalStateException: RedisConnectionFactory is required
at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.data.redis.core.RedisAccessor.afterPropertiesSet(RedisAccessor.java:38) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.data.redis.core.RedisTemplate.afterPropertiesSet(RedisTemplate.java:127) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration.createRedisTemplate(RedisHttpSessionConfiguration.java:291) ~[spring-session-data-redis-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration.sessionRepository(RedisHttpSessionConfiguration.java:120) ~[spring-session-data-redis-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
Config code:
#EnableRedisHttpSession
#EnableWebSecurity
#Configuration
#RequiredArgsConstructor
public class CustomConfiguration extends WebSecurityConfigurerAdapter {
private final FindByIndexNameSessionRepository sessionRepository;
// ....
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.formLogin()
.and()
// ....
.sessionManagement()
.sessionAuthenticationStrategy(new CustomAuthenticationStrategy(sessionRegistry(), sessionsLimitRepository));
}
#Bean
public SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry(sessionRepository);
}
#Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("server", 6379));
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<!-- .... -->
</dependencies>
If you remove\comment out the places where FindByIndexNameSessionRepository is used, then the RedisHttpSessionConfiguration setters are called by the "guts" of the spring when the springSessionRepositoryFilter bean is created.
If I understood correctly, then I accidentally broke the order of configuring the beans. In attempts to fix it, I tried #DependsOn, #Lazy and various frauds with moving bins to different classes of configs, changing the order of loading configs, and so on.
UPD: Add bean httpSessionEventPublisher and pom.xml
The main problem was that spring-boot had already configured most of the bins for me.
To resolve this problem, you must remove #EnableRedisHttpSession (#EnableWebSecurity can also be deleted), beans redisConnectionFactory and httpSessionEventPublisher, and optionally you can remove the unnecessary dependency spring-session-core
Actual config code:
#Configuration
#RequiredArgsConstructor
public class CustomConfiguration extends WebSecurityConfigurerAdapter {
private final FindByIndexNameSessionRepository<? extends Session> sessionRepository;
// ....
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.formLogin()
.and()
// ....
.sessionManagement()
.sessionAuthenticationStrategy(new CustomAuthenticationStrategy(sessionRegistry(), sessionsLimitRepository));
}
#Bean
public SessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry<>(sessionRepository);
}
}
Actual pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- .... -->
</dependencies>

Spring Security Keycloak Adapter cannot handle single sign-out when logout from another tab in browser

I have implemented a spring boot web app using spring security and Keycloak to authenticate users based on this tutorial. I used maven, spring boot 2.2.2 and Keycloak 8.0.1. All things works correctly except a problem in single sign-out. When I open the secured path of spring boot app in a tab of my browser (http://localhost:8080/books) and Keycloak account page (http://localhost:8180/auth/realms/{realm_name}/account) in another tab and login with one of the users in one of them, the other tab will aware of the login and after reload the page, that page will also be authenticated. But the problem is where, when both tabs are logged in and first I log out from account page, and reload spring boot app, the user remains active and the app does not aware of the log out action in other tabs. How can I handle this problem?
my project resource tree is like image below:
Project resource tree
This is my 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.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sso</groupId>
<artifactId>demoapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demoapp</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>13</java.version>
<keycloak.version>8.0.1</keycloak.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- Keycloak Adapter -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-test-helper</artifactId>
<version>${keycloak.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
This is SecurityConfig.java :
#KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
grantedAuthorityMapper.setPrefix("ROLE_");
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
auth.authenticationProvider(keycloakAuthenticationProvider);
}
/**
* Defines the session authentication strategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
/**
* Define an HttpSessionManager bean only if missing.
*/
#Bean
#Override
#ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
/**
* Define security constraints for the application resources.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/books").authenticated()
.antMatchers("/manager").hasRole("admin")
.anyRequest().permitAll();
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
}
This is LibraryController.java :
#Controller
public class LibraryController {
private final HttpServletRequest request;
private final BookRepository bookRepository;
#Autowired
public LibraryController(HttpServletRequest request, BookRepository bookRepository) {
this.request = request;
this.bookRepository = bookRepository;
}
#GetMapping(value = "/")
public String getHome() {
return "index";
}
#GetMapping(value = "/books")
public String getBooks(Model model) {
configCommonAttributes(model);
model.addAttribute("books", bookRepository.readAll());
return "books";
}
#GetMapping(value = "/manager")
public String getManager(Model model) {
configCommonAttributes(model);
model.addAttribute("books", bookRepository.readAll());
return "manager";
}
#GetMapping(value = "/logout")
public String logout() throws ServletException {
request.logout();
return "redirect:/";
}
private void configCommonAttributes(Model model) {
model.addAttribute("firstname", getKeycloakSecurityContext().getIdToken().getGivenName());
model.addAttribute("lastname", getKeycloakSecurityContext().getIdToken().getFamilyName());
model.addAttribute("email", getKeycloakSecurityContext().getIdToken().getEmail());
}
/**
* The KeycloakSecurityContext provides access to several pieces of information
* contained in the security token, such as user profile information.
*/
private KeycloakSecurityContext getKeycloakSecurityContext() {
return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
}
}
and below is my DemoappApplication.java :
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoappApplication {
public static void main(String[] args) {
SpringApplication.run(DemoappApplication.class, args);
}
}
The problem was just solved by setting the base URL of my spring boot application as Admin-URL in the corresponding client configuration page of Keycloak server (admin console). Now, the spring boot app is aware of user log out from other apps.

Spring boot h2-console not working

Hello I am not able to open h2-console with spring boot
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.0.RC1</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
SpringSecurity Cfg:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("admin").roles("ADMIN").and()
.withUser("user").password("user").roles("USER");
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/Access_Denied")
.and()
.csrf();
}
}
#SpringBootApplication
public class SpringBootWebSecureApplication {
#Bean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootWebSecureApplication.class);
}
}
I am starting boot with:
-Dserver.port=8090
-Dspring.h2.console.enabled=true
-Dspring.datasource.url=jdbc:h2:mem:testdb
-Dspring.datasource.username=sa
-Dspring.datasource.driverClassName=org.h2.Driver
Logs:
2015-11-13 17:37:47 [restartedMain] DEBUG c.m.a.SpringBootWebSecureApplication - Running with Spring Boot v1.3.0.RC1, Spring v4.2.2.RELEASE
2015-11-13 17:37:49 [restartedMain] INFO o.s.b.c.e.t.TomcatEmbeddedServletContainer - Tomcat initialized with port(s): 8090 (http)
2015-11-13 17:37:50 [localhost-startStop-1] INFO o.s.b.c.e.ServletRegistrationBean - Mapping servlet: 'webServlet' to [/h2-console/*]
2015-11-13 17:37:51 [localhost-startStop-1] INFO o.s.s.web.DefaultSecurityFilterChain - Creating filter chain: Ant [pattern='/h2-console/**'], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#11ffd6f0, org.springframework.security.web.context.SecurityContextPersistenceFilter#615c57c5, org.springframework.security.web.header.HeaderWriterFilter#6b2cbcbf, org.springframework.security.web.authentication.logout.LogoutFilter#af7bece, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#17d03ab2, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#613197b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#8d48442, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#7fa13240, org.springframework.security.web.session.SessionManagementFilter#9f16a1c, org.springframework.security.web.access.ExceptionTranslationFilter#1f3f02ef, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#115de1f6]
But when I open browser with my app
console not appearing
http://localhost:8090/h2-console/
Any hints?
Thank you
Same here, I think it's the Java8TimeDialect Bean, in your case. In my case, I have a DandelionDialect Bean and when I remove it the h2-console works again.... Try removing the Java8TimeDialect to see if the console works.
Something related with the spring boot DispatcherServletAutoConfiguration?, the order in which the servlets are created or mapped? Not really sure...
http.headers().frameOptions().disable();
You can try following configuration class:
import org.h2.server.web.WebServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import javax.servlet.DispatcherType;
import java.util.EnumSet;
#Configuration
public class WebConfiguration {
private static final String mapping = "/console/*";
#Bean
public ServletRegistrationBean h2servletRegistration(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean( new WebServlet());
registrationBean.addUrlMappings(mapping);
return registrationBean;
}
#Bean
public FilterRegistrationBean shallowEtagHeaderFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new ShallowEtagHeaderFilter());
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
registration.addUrlPatterns(mapping);
return registration;
}
}

Resources