/spring-boot-jwt

Spring Boot service which REST API secured by JWT

Primary LanguageJavaMIT LicenseMIT

An example of Spring Boot application for securing a REST API with JSON Web Token (JWT)

Java CI with Gradle Coverage Branches

Main building blocks

Prerequisites:

  • Maven 3
  • JDK 17

How to build:

./gradlew build

How to build Docker image with oauth-service application inside:

cd oauth-server
docker build ./ -t oauth-service-app
cd..

How to build Docker image with oauth-service application inside:

cd resource-server
docker build ./ -t resource-service-app
cd..

To run the application

java -jar ./oauth-server/build/libs/oauth-server-0.0.1-SNAPSHOT.jar
java -jar ./resource-server/build/libs/resource-server-0.0.1-SNAPSHOT.jar

Or import the project into your IDE and run AuthServerApplication & ResourceServerApplication from there.

Or use docker compose:

docker-compose up

To test the application

First you will need the following basic pieces of information:

  • client: testjwtclientid
  • secret: XY7kmzoNzl100
  • Non-admin username and password: john.doe and jwtpass
  • Admin user: admin.admin and jwtpass
  • Example of resource accessible to all authenticated users: http://localhost:9091/foo/123 (any number here)
  • Example of resource accessible to only an admin user: http://localhost:9091/cities

Generate access token

Use the following generic command to generate an access token:

curl -X POST client:secret@localhost:9090/oauth/token -d grant_type=password -d username=user -d password=pwd

For this specific application, to generate an access token for the non-admin user john.doe, run:

curl -X POST testjwtclientid:XY7kmzoNzl100@localhost:9090/oauth/token -d grant_type=password -d username=john.doe -d password=jwtpass
curl -X POST testjwtclientid:XY7kmzoNzl100@localhost:9090/oauth/token -d grant_type=password -d username=admin.admin -d password=jwtpass

You'll receive responses similar to below

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiYWRtaW4uYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiLCJmb28iXSwiZXhwIjoxNTgzODY4MjkwLCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF9VU0VSIiwiQURNSU5fVVNFUiJdLCJqdGkiOiIzYzdmYmM3Ni1mM2VhLTRkYTAtYmRlOC1jYTFlNWY0MzYxMzUiLCJjbGllbnRfaWQiOiJ0ZXN0and0Y2xpZW50aWQifQ.ki0W0_jLX4RqhnqTIdBg1j14yfJOyeipHH9W_7d-WTA",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiYWRtaW4uYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiLCJmb28iXSwiYXRpIjoiM2M3ZmJjNzYtZjNlYS00ZGEwLWJkZTgtY2ExZTVmNDM2MTM1IiwiZXhwIjoxNTg2NDE3MDkwLCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF9VU0VSIiwiQURNSU5fVVNFUiJdLCJqdGkiOiIwMzQyYzJmOC05MDI0LTQxNzItYWZjZC0wZDhhZjJkMjIxYzQiLCJjbGllbnRfaWQiOiJ0ZXN0and0Y2xpZW50aWQifQ.wEg_JSEn97KulZVbqd2gq-7piacX6JZ_KOilIy0W_Pk",
    "expires_in": 43199,
    "scope": "read write foo",
    "jti": "3c7fbc76-f3ea-4da0-bde8-ca1e5f436135"
}

Check existing token

To check existing token use:

curl -X POST client:secret@localhost:9090/oauth/check_token?token=TOKEN_HERE

You'll receive a response similar to below

{
    "aud": ["testjwtresourceid"],
    "user_name": "admin.admin",
    "scope": ["read", "write", "foo"],
    "exp": 1583866486,
    "authorities": ["STANDARD_USER", "ADMIN_USER"],
    "jti": "6b4ebc1b-ea20-4f5a-a0a4-280f48ce41cc",
    "client_id": "testjwtclientid"
}

or next

{
    "error": "invalid_token",
    "error_description": "Encoded token is a refresh token"
}

Get new token pair using refresh token

To get new token pair using existing refresh token use:

curl -X POST -d refresh_token=REFRESH_TOKEN_HERE -d grant_type=refresh_token client:secret@localhost:9090/oauth/token

You'll receive a response similar to below

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiYWRtaW4uYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiLCJmb28iXSwiZXhwIjoxNTgzODcwMjg5LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF9VU0VSIiwiQURNSU5fVVNFUiJdLCJqdGkiOiI2OTVmZmVmMi01YzQ1LTRmNTAtYmJhNy1kZTMyMWY3OWM5NjUiLCJjbGllbnRfaWQiOiJ0ZXN0and0Y2xpZW50aWQifQ.1aB-ke4dLX1_AdBGzVPtWcG9oOEt3Gptsyz8MrSEp0I",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiYWRtaW4uYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiLCJmb28iXSwiYXRpIjoiNjk1ZmZlZjItNWM0NS00ZjUwLWJiYTctZGUzMjFmNzljOTY1IiwiZXhwIjoxNTg2NDE4Mzg0LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF9VU0VSIiwiQURNSU5fVVNFUiJdLCJqdGkiOiJjYTNjN2VkMS0wYmE4LTRhMDUtYTUwZC0wNzNkZmMyN2M0YzIiLCJjbGllbnRfaWQiOiJ0ZXN0and0Y2xpZW50aWQifQ.hXv1I-t1u_eZ3JSv-3tHSkaTzfXb-jRHG3W5sNIfIoc",
    "expires_in": 43199,
    "scope": "read write foo",
    "jti": "695ffef2-5c45-4f50-bba7-de321f79c965"
}

Use tokens to access resources through your RESTful API

Access content available for all authenticated users:

Use the generated tokens as the value of the Bearer in the Authorization header as follows:

curl http://localhost:9091/foos/1 -H "Authorization: Bearer ACCESS_TOKEN_HERE"

The response will be:

{
    "id": 68,
    "name": "rRun"
}

Access content available only for an admin user:

curl http://localhost:9091/cities -H "Authorization: Bearer ACCESS_TOKEN_HERE"

The result will be:

[{
        "id": 1,
        "name": "Bamako"
    }, {
        "id": 2,
        "name": "Nonkon"
    }, {
        "id": 3,
        "name": "Houston"
    }, {
        "id": 4,
        "name": "Toronto"
    }, {
        "id": 5,
        "name": "New York City"
    }, {
        "id": 6,
        "name": "Mopti"
    }, {
        "id": 7,
        "name": "Koulikoro"
    }, {
        "id": 8,
        "name": "Moscow"
    }
]