Search code examples
javaspring-bootspring-securityoauth-2.0jwt

Why am I getting an HTTP response 401 (access denied) when trying to make an HTTP GET request with Springboot with a valid JWT token


At this moment I succeeded to create a Springboot resource server that contacts Keycloak as the authorization server to validate the JWT token being send. This work as expected, so far so good.

Unfortunately although the JWT token is valid I'm getting an 401 Unauthorized from the Springboot Resource server when I try to make a call to the RestController with the valid JWT token. The RestController is implementing the following interface:

@RequestMapping(
        value = "/companies"
        , produces = MediaTypes.HAL_JSON_VALUE
)
public interface ICompanyController {

    String REL_KEY_NAME = "companies";
    String REL_KEY_NAME_SINGLE = "company";

    @GetMapping
    CollectionModel<CompanyResponseDTO> getCompanies(JwtAuthenticationToken principal);

    @GetMapping("/{id}")
    CompanyResponseDTO getCompanyById(@PathVariable Long id);
}

I've already taken a look at my SecurityConfig as well and I don't see anything wrong here:

@EnableGlobalMethodSecurity(
        prePostEnabled = true,
        securedEnabled = true,
        jsr250Enabled = true)
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain getSecurityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeRequests(authz -> authz.antMatchers(HttpMethod.GET, "/companies")
                        .hasAuthority("SCOPE_profile")
                        .anyRequest()
                        .authenticated())
                .oauth2ResourceServer(oauth2 -> oauth2.jwt());

        return http.build();
    }

}

application.yml:

spring:
  application:
    name: API
  datasource:
    url: jdbc:mysql://localhost:3306/resource?useSSL=false&zeroDateTimeBehavior=convertToNull
    username: user
    password: secret
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
        default_schema: api_resource
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://auth-dev.local/realms/bis-realm

management:
  endpoint:
    health:
      show-details: 'when-authorized'
    roles: 'actuator'
  endpoints:
    web:
      exposure:
        include: '*'
logging:
  level:
    root: DEBUG
    com.ictual: TRACE
    com.tsparx: TRACE
server:
  port: 8081
  servlet:
    context-path: /api

Still I'm getting the following logs and Access Denied from Springboot when trying to make an HTTP call to the URL "/companies".

Find the trace below:

2023-02-24 18:47:40.409 DEBUG 74177 --- [nio-8081-exec-5] o.k.a.AuthenticatedActionsHandler        : AuthenticatedActionsValve.invoke http://localhost:8081/api/companies
2023-02-24 18:47:40.409 DEBUG 74177 --- [nio-8081-exec-5] o.k.a.AuthenticatedActionsHandler        : Policy enforcement is disabled.
2023-02-24 18:47:40.410 DEBUG 74177 --- [nio-8081-exec-5] o.s.security.web.FilterChainProxy        : Securing GET /companies
2023-02-24 18:47:40.410 DEBUG 74177 --- [nio-8081-exec-5] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2023-02-24 18:47:40.410 DEBUG 74177 --- [nio-8081-exec-5] org.apache.tomcat.util.http.Parameters   : Set encoding to UTF-8
2023-02-24 18:47:40.411 DEBUG 74177 --- [nio-8081-exec-5] o.s.web.client.RestTemplate              : HTTP GET http://auth-dev.local/realms/bis-realm/protocol/openid-connect/certs
2023-02-24 18:47:40.411 DEBUG 74177 --- [nio-8081-exec-5] o.s.web.client.RestTemplate              : Accept=[text/plain, application/json, application/*+json, */*]
2023-02-24 18:47:40.900 DEBUG 74177 --- [alina-utility-1] org.apache.catalina.session.ManagerBase  : Start expire sessions StandardManager at 1677278860900 sessioncount 0
2023-02-24 18:47:40.900 DEBUG 74177 --- [alina-utility-1] org.apache.catalina.session.ManagerBase  : End expire sessions StandardManager processingTime 0 expired sessions: 0
2023-02-24 18:47:44.233 DEBUG 74177 --- [nio-8081-exec-5] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@157ef9d25 pairs: {GET /realms/bis-realm/protocol/openid-connect/certs HTTP/1.1: null}{Accept: application/json, application/jwk-set+json}{User-Agent: Java/11.0.16.1}{Host: auth-dev.local}{Connection: keep-alive}
2023-02-24 18:47:44.255 DEBUG 74177 --- [nio-8081-exec-5] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@11416fa59 pairs: {null: HTTP/1.1 200 OK}{Referrer-Policy: no-referrer}{X-Frame-Options: SAMEORIGIN}{Strict-Transport-Security: max-age=31536000; includeSubDomains}{Cache-Control: no-cache}{X-Content-Type-Options: nosniff}{X-XSS-Protection: 1; mode=block}{Content-Type: application/json}{content-length: 2933}
2023-02-24 18:47:44.255 DEBUG 74177 --- [nio-8081-exec-5] o.s.web.client.RestTemplate              : Response 200 OK
2023-02-24 18:47:44.255 DEBUG 74177 --- [nio-8081-exec-5] o.s.web.client.RestTemplate              : Reading to [java.lang.String] as "application/json"
2023-02-24 18:47:44.257 DEBUG 74177 --- [nio-8081-exec-5] o.s.s.o.s.r.a.JwtAuthenticationProvider  : Authenticated token
2023-02-24 18:47:44.258 DEBUG 74177 --- [nio-8081-exec-5] .o.s.r.w.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@3380a05e, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[SCOPE_email, SCOPE_profile]]
2023-02-24 18:47:44.258 DEBUG 74177 --- [nio-8081-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorized filter invocation [GET /companies] with attributes [hasAuthority('SCOPE_profile')]
2023-02-24 18:47:44.258 DEBUG 74177 --- [nio-8081-exec-5] o.s.security.web.FilterChainProxy        : Secured GET /companies
2023-02-24 18:47:44.260 DEBUG 74177 --- [nio-8081-exec-5] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2023-02-24 18:47:44.261 DEBUG 74177 --- [nio-8081-exec-5] o.a.c.c.C.[Tomcat].[localhost]           : Processing ErrorPage[errorCode=0, location=/error]
2023-02-24 18:47:44.261 DEBUG 74177 --- [nio-8081-exec-5] o.s.security.web.FilterChainProxy        : Securing GET /error
2023-02-24 18:47:44.261 DEBUG 74177 --- [nio-8081-exec-5] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2023-02-24 18:47:44.261 DEBUG 74177 --- [nio-8081-exec-5] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-02-24 18:47:44.261 DEBUG 74177 --- [nio-8081-exec-5] o.s.security.web.FilterChainProxy        : Secured GET /error
2023-02-24 18:47:44.262 DEBUG 74177 --- [nio-8081-exec-5] a.DefaultWebInvocationPrivilegeEvaluator : filter invocation [/error] denied for AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73) ~[spring-security-core-5.7.6.jar:5.7.6]
    at org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator.isAllowed(DefaultWebInvocationPrivilegeEvaluator.java:100) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator.isAllowed(DefaultWebInvocationPrivilegeEvaluator.java:67) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.isAllowed(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.java:76) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.isAllowed(ErrorPageSecurityFilter.java:88) ~[spring-boot-2.7.8.jar:2.7.8]
    at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.doFilter(ErrorPageSecurityFilter.java:76) ~[spring-boot-2.7.8.jar:2.7.8]
    at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.doFilter(ErrorPageSecurityFilter.java:70) ~[spring-boot-2.7.8.jar:2.7.8]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:87) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.25.jar:5.3.25]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.25.jar:5.3.25]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.25.jar:5.3.25]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.25.jar:5.3.25]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.25.jar:5.3.25]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186) ~[spring-security-web-5.7.6.jar:5.7.6]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) ~[spring-web-5.3.25.jar:5.3.25]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-5.3.25.jar:5.3.25]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.25.jar:5.3.25]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.25.jar:5.3.25]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.25.jar:5.3.25]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.25.jar:5.3.25]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:711) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:461) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:385) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:313) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:388) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:244) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.71.jar:9.0.71]
    at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

I've tried to adjust the SecurityConfig in different ways without any success.


Solution

  • I found the reason why this was not working; I had a keycloak/springboot dependency that was using the old keycloak adaptor way of working:

    <dependency>
      <groupId>org.keycloak</groupId>
      <artifactId>keycloak-spring-boot-starter</artifactId>
      <version>17.0.0</version>
    </dependency>
    

    By removing this dependency everything is working now.