skip to Main Content

I am trying to validate an access token on a resource server.

jwt.io validates the access token with the signature, so I guess the problem is spring configuration

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>uy.edu.anep</groupId>
    <artifactId>aplicacion-funcionarios-service</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>aplicacion-funcionarios-service</name>
    <description>Demo project for Spring Boot</description>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.11.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <start-class>edu.anep.familia.aplicacionfuncionarios.Application</start-class>

        <org.mapstruct.version>1.2.0.Final</org.mapstruct.version>
        <java.version>1.8</java.version>        
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <oauth-autoconfig.version>2.1.11.RELEASE</oauth-autoconfig.version>
        <spring-cloud.version>Greenwich.SR4</spring-cloud.version>

    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <!-- oauth -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        </dependency>




        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>



        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>



        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-envers</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>        
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>                    

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>  

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>${org.mapstruct.version}</version> 
        </dependency>           


        <!-- https://stackoverflow.com/a/43574427/1989579 -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

        <dependency>
            <groupId>edu.anep.microservicios</groupId>  
            <artifactId>spring-DMZ-Utils</artifactId>
            <version>0.0.8-spring_2.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.12.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>        
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.1.GA</version>
            <scope>test</scope>
        </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>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <id>recreate-docker</id>
                        <configuration>
                            <environmentVariables>
                                <COMPOSE_PATH>./</COMPOSE_PATH>
                                <COMPOSE_SERVICE_NAME>aplicacion-funcionarios-service</COMPOSE_SERVICE_NAME>
                                <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
                            </environmentVariables>   
                            <executable>rebuild.sh</executable>
                        </configuration>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>  
                </executions>  
            </plugin> 
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>  
                    <target>1.8</target> 
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.16.22</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>




    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/libs-snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>








</project>

this my application.yml

server:
    port: 8080
endpoints:
    shutdown:
        enabled: true
    restart:
        enabled: true

server.servlet.context-path: /aplicacion-funcionarios-service


security:
    basic:
        enabled: false
    oauth2:
        resource:
            jwt:
                key-value: |
                    -----BEGIN PUBLIC KEY-----
                    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvVSBre7DgM7FS1d9gzwBMaOY6j40AjcFHq3s9zx/0QMGAmRrVD2Eiuc7YdIZu9gRpCAohKDz1v0wmeE9Nafqw9XjcxJX2Te4+TTF8/Pia8adSyKVjpFMvlvCu83fdT+vgM3P08QLHtm19ToImTsI5oNZhH/iZNm8bjMJL4D4DXv3rOKwhKp5Sb2Hn8Qwes8MJSFO2YtVtqLCc60L2ERxPd5vZ/7s4mEIhI1bw/U/n5n5yPVHpXsZoP3Eru2LksDsWoWK/jKjJIhRtEZxbDuycMYEYiCaVaxLJ2Bwu41bob+FrS3YOlSqzQDpyTcpuACOdUmRFmtnRtORF1wnlDiyYQIDAQAB
                    -----END PUBLIC KEY-----


spring:
    cloud.circuit.breaker.enabled: false
    application:
      name: aplicacion-funcionarios-service
    #el formato en que van las fechas por defecto, sin zona horaria
    jackson:
        date-format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
        time-zone: America/Montevideo
    # Spring JDBC configuration
    datasource:
        url: ...
        username: ...
        password: ..     
    # Spring Data JPA configuration
    jpa:   
        properties:
            org.hibernate.envers.audit_table_suffix: _aud
            org.hibernate.envers.default_schema: audit
            hibernate:
                #format_sql: true
                #do_not_audit_optimistic_locking_field: false
                dialect: org.hibernate.dialect.PostgreSQLDialect
                #https://github.com/spring-projects/spring-boot/issues/12007#issuecomment-369388646
                jdbc.lob.non_contextual_creation: true
        hibernate:
            # To be updated in real production usage! update create-drop none
            #ddl-auto:  update
            show-sql: false
    sleuth:
        baggage-keys:
          - x-request-id
          - x-b3-traceid
          - x-b3-spanid
          - x-b3-parentspanid
          - x-b3-sampled
          - x-b3-flags
          - x-ot-span-context
              #configuracion del cache de redis
    cache:
        type: none

#Disable Ribbon 
ribbon.eureka.enabled: false
#se habilita liquidbase
spring.liquibase.change-log: classpath:db/changelog/db.changelog-master.xml



logging.level.root: INFO
logging.level.org.springframework.web: DEBUG
logging.level.org.springframework.security: DEBUG
logging.level.org.springframework.security.oauth2: DEBUG
#logging.level.org.apache: trace

this is my config class

@Configuration
@EnableResourceServer
public class OAuth2SecurityConfig  extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
                .authorizeRequests()
                //.antMatchers("/**").authenticated()
                .antMatchers(
                        "/*/v2/api-docs",
                        "/v2/api-docs",
                        "/swagger-ui.html",
                        "/swagger-ui.html/**",
                        "/webjars/springfox-swagger-ui/**",
                        "/swagger-resources/**", 
                        "/actuator/*"
                ).permitAll()
                .anyRequest().authenticated();

        //http.cors(
        // @formatter:on
    }


}

but I receive the following error in response

401
{
    "error": "invalid_token",
    "error_description": "Cannot convert access token to JSON"
}

the log shows little information

aplicacion-funcionarios-service_1  | 2019-12-14 22:31:57.545 DEBUG [aplicacion-funcionarios-service,2e8a7fbc810ad58c,2e8a7fbc810ad58c,false] 1 --- [nio-8080-exec-1] p.a.OAuth2AuthenticationProcessingFilter : Authentication request failed: error="invalid_token", error_description="Cannot convert access token to JSON"
aplicacion-funcionarios-service_1  | 2019-12-14 22:31:57.594 DEBUG [aplicacion-funcionarios-service,2e8a7fbc810ad58c,2e8a7fbc810ad58c,false] 1 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@64c00153
aplicacion-funcionarios-service_1  | 2019-12-14 22:31:57.598 DEBUG [aplicacion-funcionarios-service,2e8a7fbc810ad58c,2e8a7fbc810ad58c,false] 1 --- [nio-8080-exec-1] s.s.o.p.e.DefaultOAuth2ExceptionRenderer : Written [error="invalid_token", error_description="Cannot convert access token to JSON"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@5739b1cb]
aplicacion-funcionarios-service_1  | 2019-12-14 22:31:57.599 DEBUG [aplicacion-funcionarios-service,2e8a7fbc810ad58c,2e8a7fbc810ad58c,false] 1 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

this is the header sent

Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsIm9yZy5hcGVyZW8uY2FzLnNlcnZpY2VzLlJlZ2lzdGVyZWRTZXJ2aWNlIjoiMSJ9.eyJzdWIiOiJkZW1vZG9jZW50ZSIsInJvbGVzIjpbXSwiaXNzIjoiaHR0cHM6XC9cL3ByZXByb2QuYW5lcC5lZHUudXlcL2NhcyIsIm5vbmNlIjpbIiJdLCJjbGllbnRfaWQiOlsiQVBQRnVuY2lvbmFyaW9zIl0sImF1ZCI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgxXC9cL2xvZ2luIiwiZ3JhbnRfdHlwZSI6WyJBVVRIT1JJWkFUSU9OX0NPREUiXSwicGVybWlzc2lvbnMiOltdLCJzY29wZSI6WyJlbWFpbCIsIm9wZW5pZCIsInByb2ZpbGUiXSwiY2xhaW1zIjpbXSwic2NvcGVzIjpbIm9wZW5pZCBwcm9maWxlIGVtYWlsIl0sInN0YXRlIjpbIiJdLCJleHAiOjE1NzYzOTE0MTgsImlhdCI6MTU3NjM2MjYxOCwianRpIjoiQVQtNS05a3ZvNGhsUFY0TUkzcjdReEticGRaSDYtMnRlNTQtdiJ9.NvFRFpa8XqYwGCYNHV6brBoi2wMsHH9YthbUK4wjifg7Kfeu__R9wialAyCUJViifi1cTCkTNfysbo-tH5WaJN3vrENDVSpSPlBbWJS5fVmNR45-HCDtLJkNsoexeTwNin1R5tz-GHNTnh4rNFjGJwj_gI5_MCFRYODBiuU_19HsVX_eEYJn7mPchk_Q8wujk9e_akPRLHzruCa3yilR6LGOzWWecQwVt3q0ZMgaOt-aG42OVuGySD-vgzpfJfPc4SFzYXyQYtvnuOyb3q1pxECzBVbW296uzzWaEOfV5OlGcMk_i4vN61HBQDMYbYcheehO3T7jZgCulYlGzit6Mw

2

Answers


  1. Chosen as BEST ANSWER

    I couldn't solve this using the spring-oauth dependencies.

    I used

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>   
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.8.3</version>
    </dependency>
    

    I created my custom filter that validate the token

    public class JwtFilter extends GenericFilterBean {
    
    
        private ObjectMapper mapper = new ObjectMapper();
        private JWTVerifier verifier;
    
        public JwtFilter(String key) throws Exception {
    
            String publicKeyPEM = key
                    .replace("-----BEGIN PUBLIC KEY-----", "")
                    .replace("-----END PUBLIC KEY-----", "")
                    .replaceAll("\s", "");
    
            byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            RSAPublicKey publicKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
    
            Algorithm algorithm = Algorithm.RSA512(publicKey, null);
            this.verifier = JWT.require(algorithm)
                    .build();
    
        }
    
        @Override
        public void doFilter(ServletRequest request,
                ServletResponse response,
                FilterChain filterChain)
                throws IOException, ServletException {
    
            Authentication authentication = getAuthentication((HttpServletRequest) request);
    
            SecurityContextHolder.getContext().setAuthentication(authentication);
    
            filterChain.doFilter(request, response);
        }
    
        // Método para validar el token enviado por el cliente
        private Authentication getAuthentication(HttpServletRequest request) {
            try {
    
                // Obtenemos el token que viene en el encabezado de la peticion
                String tokenStr = request.getHeader("Authorization");
                tokenStr = tokenStr.replace("Bearer", "").trim();
    
                //se verifica la firma del token
                DecodedJWT jwt = verifier.verify(tokenStr);
    
                String token = newString((Base64.getDecoder().decode(jwt.getPayload())), "UTF-8");
    
    
    
                //verifica si el token expiro
                Date today = new Date();
                if (jwt.getExpiresAt() != null && (today.compareTo(jwt.getExpiresAt()) > 0)) {
                    return null;
                }
    
                //AHORA VA A CARGAR LOS ROLES EN VASE AL USUARIO               
    
                CustomPrincipal userDetail = new CustomPrincipal();
                userDetail.setRoles(new LinkedList());
    
                ...
    
                List<GrantedAuthority> roles = AuthorityUtils.createAuthorityList(
                        userDetail.getRoles()
                                .stream().toArray(size -> new String[size])
                );
    
                return new UsernamePasswordAuthenticationToken(userDetail, null, roles);
    
            } catch (Throwable t) {
                //t.printStackTrace();
            }
            return null;
        }
    
        public static String newString(final byte[] bytes, final String charsetName) {
            if (bytes == null) {
                return null;
            }
            try {
                return new String(bytes, charsetName);
            } catch (final UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    

    and then configure the filter like this

    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @EnableWebSecurity
    @ConfigurationProperties(prefix = "security.custom")
    public class OAuth2SecurityConfig  extends WebSecurityConfigurerAdapter {
    
        private String publicKey = "...";
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                    //.csrf().disable()
                    .cors().and()
                    // make sure we use stateless session; session won't be used to store user's state.
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    // handle an authorized attempts 
                    .exceptionHandling().authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED));
            http
                    .authorizeRequests()
                    //.antMatchers("/**").authenticated()
                    .antMatchers(
                            "/*/v2/api-docs",
                            "/v2/api-docs",
                            "/swagger-ui.html",
                            "/swagger-ui.html/**",
                            "/webjars/springfox-swagger-ui/**",
                            "/swagger-resources/**", 
                            "/actuator/*"
                    ).permitAll()
                    .anyRequest().authenticated()
                    .and()
                    // Las demás peticiones pasarán por este filtro para validar el token
                    .addFilterBefore(new JwtFilter(publicKey), UsernamePasswordAuthenticationFilter.class);
    
            //http.cors(
            // @formatter:on
        }
    

  2. By default, Oauth2 Spring integration uses its own "type of tokens", if you want to work with JWT ones you have to specify how to deal with them developing the suitable behaviour of JwtAccessTokenConverter.

    I hope the following links help you to solve the problem:

    Option1

    Option2

    In the following link you will be able to see a tutorial with a complete integration with: JWT + Oauth2

    On the other hand, in the next one you will find a fully functional microservice used as Oauth 2.0 security server integrated with Spring, that includes several other customizations:

    Oauth 2 security server

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search