{"id":19,"date":"2024-09-13T19:47:59","date_gmt":"2024-09-13T19:47:59","guid":{"rendered":"https:\/\/marcinzygmunt.pl\/blog\/?p=19"},"modified":"2025-09-24T18:44:41","modified_gmt":"2025-09-24T18:44:41","slug":"extending-simple-jwt-authentication-with-refresh-tokens-with-spring-boot-cache-version","status":"publish","type":"post","link":"https:\/\/marcinzygmunt.pl\/blog\/extending-simple-jwt-authentication-with-refresh-tokens-with-spring-boot-cache-version\/","title":{"rendered":"Extending Simple JWT Authentication with Refresh Tokens with Spring Boot (Cache version)\u00a0"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\"><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">After building <strong>spring-boot-jwt-with-refresh<\/strong>, I wanted to demonstrate how to <strong>improve performance by caching refresh tokens<\/strong>. That\u2019s why I created <strong><a href=\"https:\/\/github.com\/marcinzygmunt-pl\/spring-boot-jwt-with-refresh-cache\">spring-boot-jwt-with-refresh-cache<\/a><\/strong>. This project shows how to securely manage JWT authentication with refresh tokens stored in a cache, reducing database lookups and supporting scalable applications.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\"><strong>TIP:<\/strong> Using a cache like Spring Cache improves performance. However, <strong>in multi-instance deployments, local caches are isolated per instance<\/strong>, which can lead to inconsistencies if a refresh token is used on a different server. This limitation is addressed in the <strong>Hazelcast version<\/strong>, where a shared cluster ensures all instances have access to the same refresh tokens.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Like the previous examples, this project is built with <strong>Spring Boot 3<\/strong>.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6aac8c90337eb&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6aac8c90337eb\" class=\"wp-block-image size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"882\" height=\"1024\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/marcinzygmunt.pl\/blog\/wp-content\/uploads\/2025\/09\/jwt_3-882x1024.png\" alt=\"\" class=\"wp-image-29\" srcset=\"https:\/\/marcinzygmunt.pl\/blog\/wp-content\/uploads\/2025\/09\/jwt_3-882x1024.png 882w, https:\/\/marcinzygmunt.pl\/blog\/wp-content\/uploads\/2025\/09\/jwt_3-258x300.png 258w, https:\/\/marcinzygmunt.pl\/blog\/wp-content\/uploads\/2025\/09\/jwt_3-768x892.png 768w, https:\/\/marcinzygmunt.pl\/blog\/wp-content\/uploads\/2025\/09\/jwt_3.png 1098w\" sizes=\"auto, (max-width: 882px) 100vw, 882px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">What You Get<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Basic Auth + JWT<\/strong>: Users log in with credentials and receive a JWT token.<\/li>\n\n\n\n<li><strong>Refresh Tokens in Cache<\/strong>: Refresh tokens are stored in a cache, improving performance.<\/li>\n\n\n\n<li><strong>Swagger UI<\/strong>: Explore and test endpoints at <code>http:\/\/localhost:8088\/swagger-ui\/index.html<\/code>.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Setup in 4 Steps<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Clone the repository<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/marcinzygmunt-pl\/spring-boot-jwt-with-refresh-cache.git\ncd spring-boot-jwt-with-refresh-cache<\/code><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Configure PostgreSQL and Cache<\/strong><br>Edit <code>application.yml<\/code> with your database credentials and cache settings.<\/li>\n\n\n\n<li><strong>Run the application<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/mvnw spring-boot:run<\/code><\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>Test endpoints in Swagger UI<\/strong><br>Open <code>http:\/\/localhost:8088\/swagger-ui\/index.html<\/code> and test login, token refresh, and protected endpoints.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Optional: Go Further<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For <strong>multi-instance deployments<\/strong>, consider <strong>Hazelcast<\/strong> to share refresh tokens across all nodes reliably.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">These options are explored in other branches for more advanced setups.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Final Note<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>spring-boot-jwt-with-refresh-cache<\/strong> is the next step in the series: it builds on the <strong>refresh token mechanism<\/strong> and adds <strong>caching for efficiency<\/strong>. Be aware that local caches have limitations in multi-instance setups. The <strong>Hazelcast version<\/strong> resolves this issue by providing a shared cluster accessible by all instances, ensuring consistent token validation and refresh across the application.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>After building spring-boot-jwt-with-refresh, I wanted to demonstrate how to improve performance by caching refresh tokens. That\u2019s why I created spring-boot-jwt-with-refresh-cache. This project shows how to securely manage JWT\u2026<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[9,5],"class_list":["post-19","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-jwt","tag-springboot"],"jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/posts\/19","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/comments?post=19"}],"version-history":[{"count":7,"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/posts\/19\/revisions"}],"predecessor-version":[{"id":155,"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/posts\/19\/revisions\/155"}],"wp:attachment":[{"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/media?parent=19"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/categories?post=19"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/marcinzygmunt.pl\/blog\/wp-json\/wp\/v2\/tags?post=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}