What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

This template gives you the modern foundation for a secured Java REST API: Spring Boot acts as an OAuth 2.0 resource server, Keycloak issues OpenID Connect access tokens, PostgreSQL stores application data, and Flyway manages schema changes. The API validates bearer JWTs without querying the application database for every request.

You can run the local infrastructure with Docker Compose, protect CRUD endpoints with scopes or roles, and verify the complete authentication path with Testcontainers.

Architecture

Client
  | obtains an access token
  v
Keycloak
  | Authorization: Bearer JWT
  v
Spring Boot API
  | validates issuer, signature, expiry and claims
  v
PostgreSQL application database

Keycloak is the identity provider and authorization server. Spring Boot is the resource server. PostgreSQL stores business data such as products, projects or orders.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep Keycloak’s internal database separate from the application’s database. Sharing one PostgreSQL server is reasonable for local development, but use separate databases, credentials or schemas. Production deployments may warrant separate managed instances.

#1 Best Overall
Crucial 32GB DDR5 RAM Kit (2x16GB), 5600MHz (or 5200MHz or 4800MHz) Laptop Memory 262-Pin SODIMM, Compatible with Intel Core and AMD Ryzen 7000, Black - CT2K16G56C46S5
  • Boosts System Performance: 32GB DDR5 RAM laptop memory kit (2x16GB) that operates at 5600MHz, 5200MHz, or 4800MHz to improve multitasking and system responsiveness for smoother performance
  • Accelerated gaming performance: Every millisecond gained in fast-paced gameplay counts—power through heavy workloads and benefit from versatile downclocking and higher frame rates
  • Optimized DDR5 compatibility: Best for 12th Gen Intel Core and AMD Ryzen 7000 Series processors — Intel XMP 3.0 and AMD EXPO also supported on the same RAM module
  • Trusted Micron Quality: Backed by 42 years of memory expertise, this DDR5 RAM is rigorously tested at both component and module levels, ensuring top performance and reliability
  • ECC Type = Non-ECC, Form Factor = SODIMM, Pin Count = 262-Pin, PC Speed = PC5-44800, Voltage = 1.1V, Rank And Configuration = 1Rx8
Requirement Spring Security capability
API receives a bearer token OAuth2 Resource Server
Web application redirects users to Keycloak OAuth2 Client/Login
Backend calls another protected API OAuth2 Client

For a bearer-token API, use standard Spring Security OAuth2 Resource Server support rather than older Keycloak-specific Spring adapters. See the Spring Boot OAuth2 documentation and Spring Security OAuth2 documentation.

Project dependencies

Pin a tested Java, Spring Boot, PostgreSQL, Keycloak and Testcontainers version matrix in the repository. Avoid describing the stack as “latest”; compatibility changes over time.

<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-oauth2-resource-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Add spring-boot-starter-oauth2-client only when the application needs browser login or outbound OAuth2 calls. Optional additions include OpenAPI documentation, Testcontainers’ PostgreSQL and Keycloak modules, and a cache or Redis starter when the application actually needs them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

PostgreSQL and application configuration

spring:
  application:
    name: secured-api

  datasource:
    url: ${DB_URL:jdbc:postgresql://localhost:5432/appdb}
    username: ${DB_USERNAME:app}
    password: ${DB_PASSWORD:app}

  jpa:
    open-in-view: false
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        format_sql: true

  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: ${KEYCLOAK_ISSUER_URI:http://localhost:8080/realms/demo}
          audiences:
            - secured-api

  flyway:
    enabled: true

The issuer must match the token’s iss claim. Spring Boot uses the issuer metadata and signing-key information to configure JWT validation. Audience validation is separate: a token can come from the correct realm while still being intended for another service. Configure the expected audience when that distinction matters. The emitted audience depends on Keycloak client and protocol-mapper settings.

Use Flyway or Liquibase as the owner of schema changes and keep Hibernate at validate. Never use create or create-drop in production. Keep database credentials in environment variables or a secret manager, configure connection-pool limits, and expose only the actuator endpoints you need.

Rank #2
A-Tech DDR4 RAM 16GB 3200MHz PC4-25600 SODIMM Laptop Memory
  • A-Tech 16GB RAM Module, DDR4 SO-DIMM 260-Pin, 3200MHz PC4-25600 (PC4-3200AA)
  • Non-ECC Unbuffered, JEDEC DDR4 Standard 1.2V Operating Voltage
  • Compatible with select Laptop, Notebook, Mini PC, and All-in-One (AIO) systems. Please verify your system's memory type, form factor, and maximum supported capacity before purchasing
  • Not compatible with desktop DIMM, non DDR4 memory, or ECC memory types such as RDIMM, LRDIMM, and ECC UDIMM
  • Increases available memory capacity to enhance system responsiveness, application performance, and multitasking capabilities.

JPA or JDBC?

JPA is a practical default for aggregate-oriented CRUD applications with repositories and entity relationships. Spring JDBC is often a better choice for SQL-heavy reporting, PostgreSQL-specific queries and data-oriented workflows. Neither removes the need for explicit transactions, indexes, constraints, pagination and query review.

Design PostgreSQL deliberately: choose UUID or numeric identifiers, use appropriate timestamp semantics, add unique and foreign-key constraints, index JSONB only when justified, use stable ordering for pagination, and consider optimistic locking for concurrent updates.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Docker Compose for local development

Run separate logical databases for the API and Keycloak. Pin image versions that you have tested.

services:
  app-db:
    image: postgres:<tested-version>
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
    ports:
      - "5432:5432"
    volumes:
      - app-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
      interval: 5s
      timeout: 5s
      retries: 20

  keycloak-db:
    image: postgres:<tested-version>
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloak
    volumes:
      - keycloak-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
      interval: 5s
      timeout: 5s
      retries: 20

  keycloak:
    image: quay.io/keycloak/keycloak:<tested-version>
    command: start-dev
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak
      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: admin
    ports:
      - "8080:8080"
    depends_on:
      keycloak-db:
        condition: service_healthy

volumes:
  app-db-data:
  keycloak-db-data:

This uses start-dev and simple credentials for local work only. Keycloak’s container documentation covers container and PostgreSQL deployment configuration. In production, use HTTPS, a stable hostname, injected secrets, durable storage, backups, health monitoring and a supported deployment strategy. Container startup order also does not guarantee that discovery metadata is ready.

Configure Keycloak

  1. Start the services with docker compose up -d app-db keycloak-db keycloak.
  2. Open the Keycloak administration console on port 8080.
  3. Create a realm named demo.
  4. Create a client representing the frontend, CLI, mobile app or service that obtains tokens. Do not use one universal client configuration for every caller.
  5. Create explicit scopes such as products:read and products:write, or create client roles such as admin.
  6. Create a temporary local test user and assign the required permissions.

For browser applications, use Authorization Code with PKCE and exact redirect URIs and web origins. For machine-to-machine calls, use a confidential client and client credentials. Avoid enabling the password grant as a default. A pure resource server does not need a client secret merely to validate JWTs.

Rank #3
Sale
Crucial 16GB DDR4 RAM, 3200MHz CL22 (or 2933MHz or 2666MHz) Laptop Memory, SODIMM 260-Pin, Compatible with 13th Gen Intel Core and AMD Ryzen 7000 - CT16G4SFRA32A
  • Boosts System Performance:16GB DDR4 laptop memory that operates at 3200MHz to improve multitasking and system responsiveness for smoother performance
  • Easy Installation: Upgrade your laptop RAM with ease—no computer skills required Follow step-by-step how-to guides available at Crucial for a smooth, worry-free installation
  • Compatibility Guaranteed: Ensure seamless compatibility with your laptop by using the Crucial System Scanner or Crucial Upgrade Selector—get accurate recommendations for your specific device
  • Trusted Micron Quality: Backed by 42 years of memory expertise, this DDR4 RAM is rigorously tested at both component and module levels, ensuring top performance and reliability for your Mac system
  • ECC Type = Non-ECC, Form Factor = SODIMM, Pin Count = 260-pin, PC Speed = PC4-25600, Voltage = 1.2V, Rank and Configuration = 1Rx8 or 2Rx8

Realm imports can make local setup reproducible, but never commit real client secrets, administrator credentials or production user data. See the current Keycloak documentation for version-specific configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure Spring Security

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health", "/v3/api-docs/**", "/swagger-ui/**").permitAll()
                .requestMatchers(HttpMethod.GET, "/api/products/**")
                    .hasAuthority("SCOPE_products:read")
                .requestMatchers(HttpMethod.POST, "/api/products/**")
                    .hasAuthority("SCOPE_products:write")
                .requestMatchers(HttpMethod.DELETE, "/api/products/**")
                    .hasRole("admin")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));

        return http.build();
    }
}

Disabling CSRF is usually appropriate for a stateless API that authenticates exclusively with bearer tokens in the Authorization header. It is not a universal setting. Cookie- or session-authenticated browser applications should retain and configure CSRF protection; mixed applications need a deliberate design.

Scopes and roles are not interchangeable

Spring Security commonly converts scope or scp claims into authorities such as SCOPE_products:read. Keycloak roles commonly appear under realm_access.roles or resource_access.<client>.roles. They do not automatically become ROLE_... authorities in every configuration.

For realm roles, add an explicit converter:

@Bean
JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter scopes = new JwtGrantedAuthoritiesConverter();
    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();

    converter.setJwtGrantedAuthoritiesConverter(jwt -> {
        Set<GrantedAuthority> authorities = new HashSet<>(scopes.convert(jwt));
        Map<String, Object> realmAccess = jwt.getClaim("realm_access");

        if (realmAccess != null && realmAccess.get("roles") instanceof Collection<?> roles) {
            roles.forEach(role -> authorities.add(
                new SimpleGrantedAuthority("ROLE_" + role)));
        }
        return authorities;
    });
    return converter;
}
.oauth2ResourceServer(oauth2 -> oauth2
    .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())))

Choose one policy deliberately: scopes for API permissions, client roles for application-specific roles, realm roles for genuinely realm-wide permissions, groups for organizational membership, and custom claims only where standard claims are insufficient. Always verify the actual access token and ensure the Keycloak claim location matches the Spring authority rule.

Method security

@Configuration
@EnableMethodSecurity
class MethodSecurityConfig { }

@RestController
@RequestMapping("/api/products")
class ProductController {
    @GetMapping
    @PreAuthorize("hasAuthority('SCOPE_products:read')")
    List<ProductResponse> list() { return List.of(); }

    @PostMapping
    @PreAuthorize("hasAuthority('SCOPE_products:write')")
    ResponseEntity<ProductResponse> create(
            @Valid @RequestBody CreateProductRequest request) {
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
}

URL rules provide perimeter protection; method rules protect operations closer to the service boundary. Neither replaces object-level authorization. For example, a user with a valid write scope may still need a check that the project belongs to the user’s organization. Use the Keycloak subject identifier, sub, as the stable external identity key rather than email, which can change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Timetec 16GB DDR4 2666MHz (PC4-2666V) PC4-21300 SODIMM Laptop RAM – 260-Pin 1.2V CL19 Non-ECC Unbuffered Memory Module for Laptop, Notebook, Mini PC, All-in-One
  • Capacity – Single Module 16GB Speed up to 2666MHz Non-ECC Unbuffered 260-Pin 1.2V SODIMM.
  • Specs – PCB Color (Green or Black) and Rank (1Rx8 or 2Rx8) may vary depending on production batch. Performance and quality remain consistent across all Timetec products.
  • Compatibility – Designed for selected DDR4 Laptop, Notebook, Mini PCs, and All-In-One systems(AIO) that support 260-Pin SODIMM memory. NOT compatible with Desktop DIMM slots.
  • Installation – Plug-and-Play Upgrade, Quick and Easy to Install, no expertise required (please refer to your system's manual for guidelines).
  • Warranty – All Timetec products are high-quality and rigorously tested to meet stringent standards. Backed by Timetec Limited Lifetime Warranty and professional technical support based in the United States.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Build and call a protected endpoint

A small resource such as Product is enough to demonstrate the complete path:

GET    /api/products              authenticated
GET    /api/products/{id}         authenticated
POST   /api/products              products:write
PUT    /api/products/{id}         products:write
DELETE /api/products/{id}          admin
GET    /actuator/health           public

Use a normal controller-service-repository structure, Bean Validation on request DTOs, transactions at service boundaries, consistent error responses and a Flyway migration such as V1__create_products.sql. Keep entity exposure separate from API response models.

Set local variables and start the application:

export DB_URL=jdbc:postgresql://localhost:5432/appdb
export DB_USERNAME=app
export DB_PASSWORD=app
export KEYCLOAK_ISSUER_URI=http://localhost:8080/realms/demo
./mvnw spring-boot:run

PowerShell:

$env:DB_URL="jdbc:postgresql://localhost:5432/appdb"
$env:DB_USERNAME="app"
$env:DB_PASSWORD="app"
$env:KEYCLOAK_ISSUER_URI="http://localhost:8080/realms/demo"
./mvnw spring-boot:run

After migrations run and issuer discovery succeeds, call the API:

curl http://localhost:8081/api/products 
  -H "Authorization: Bearer $ACCESS_TOKEN"

No token should produce 401 Unauthorized. A valid token without the required scope should produce 403 Forbidden. A valid token with the required authority should succeed.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The token request depends on the caller. Prefer Authorization Code with PKCE for user-facing applications, client credentials for service-to-service calls, and device authorization for suitable devices or command-line clients.

Best Value
Silicon Power DDR3L 16GB (2x8GB) RAM 1600MHz (PC3 12800) 204 pin CL11 1.35V Non ECC Unbuffered SODIMM Laptop Notebook Memory RAM Module Upgrade
  • 1600MHz (PC3 12800) 204-pin CL11 SODIMM for laptop memory
  • Runs at low voltage of 1.35V that enables to effectively decrease hardware power consumption.
  • Compatible with MacBook Pro13-inch/15-inch Mid 2012, iMac 21.5-inch Late 2012/ Early/Late 2013
  • Backed by a lifetime warranty to promise complete services and technical support.

Testing strategy

Use three layers:

  1. Unit tests: services, validation, JWT authority conversion and authorization policies.
  2. Security/MVC tests: missing, malformed, expired and insufficiently privileged tokens.
  3. Integration tests: the application, real PostgreSQL, migrations and a disposable Keycloak instance.
Scenario Expected result
No Authorization header 401
Malformed or expired token 401
Wrong issuer or signing key 401
Valid token without permission 403
Valid read scope on GET 200
Valid write scope on POST 201

Mocked JWT tests are fast and useful, but they can hide mismatches in Keycloak claim mapping. Include at least one real token flow. Docker’s Spring Boot, Keycloak and Testcontainers guide demonstrates this style of integration.

Troubleshooting

401 despite a seemingly correct token

  • Check expiration, clock skew, realm and signing key.
  • Confirm the issuer URL exactly matches the token’s iss claim.
  • Ensure the API can reach Keycloak metadata and JWK endpoints.
  • Check that the client sent an access token, not an ID token.
  • Behind a proxy, verify the externally visible hostname and HTTPS configuration.

403 despite a Keycloak role

  • The role may be in realm_access.roles while the API checks scopes.
  • The role may be under resource_access for another client.
  • The application may expect ROLE_ADMIN while the converter emits admin.
  • The role may appear in the console but not in the issued access token.

Hostnames and startup

A host-run application normally uses http://localhost:8080/realms/demo. An application inside Compose usually reaches Keycloak at http://keycloak:8080/realms/demo. Issuer values, browser access and reverse-proxy configuration must remain consistent. Add readiness checks and retry behavior; a running container is not necessarily a ready identity server.

CORS is a browser-origin policy, not authentication. Configure exact frontend origins when needed and avoid wildcard origins with credentials. Do not expose secrets through actuator endpoints or permissive Compose files.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Production hardening

  • Use TLS for Keycloak, PostgreSQL and API traffic.
  • Configure a stable external Keycloak hostname and proxy settings.
  • Inject credentials through a secret manager rather than source control.
  • Use separate least-privilege database users and migration permissions.
  • Back up both application and Keycloak databases and test restoration.
  • Pin dependencies and container images; patch them regularly.
  • Validate audience as well as issuer where the trust boundary requires it.
  • Redact tokens, passwords and personal data from logs.
  • Monitor Keycloak availability, database health, key rotation and API failures.
  • Plan revocation, logout, refresh-token handling and key rotation; local JWT validation does not make identity lifecycle concerns disappear.
  • Apply rate limiting and protect administrative endpoints.

Docker Compose and start-dev are excellent for a local template, but they are not automatically a production identity architecture. Keycloak is open-source software, yet hosting, backups, monitoring, upgrades and security operations still have real costs.

Keycloak versus managed identity

Criterion Keycloak Managed provider
Hosting Team-managed Vendor-managed
Customization High Provider-dependent
Operational burden Higher Lower
Control of identity data Greater Subject to provider and region
Local development Convenient with Docker May require a remote tenant

Keycloak suits teams that need self-hosting, realm customization or protocol flexibility and can operate security-critical infrastructure. A managed provider may be better when minimizing identity operations is more important than control. Compare current product terms, pricing, quotas and regional availability before choosing.

JWT versus opaque tokens

JWT validation is usually efficient because the API validates a signed token locally using public keys. Opaque-token introspection lets the authorization server make a live validation decision but adds a network dependency. Both approaches are supported by Spring Security. JWTs still require deliberate revocation, expiry, key rotation and logout policies.

Useful official references

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.