<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mahendar Anumalla</title>
    <description>The latest articles on DEV Community by Mahendar Anumalla (@manumalla).</description>
    <link>https://dev.to/manumalla</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3941861%2F1a4a212c-4f1b-4d68-aa14-8a3b8e7c7bb5.png</url>
      <title>DEV Community: Mahendar Anumalla</title>
      <link>https://dev.to/manumalla</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manumalla"/>
    <language>en</language>
    <item>
      <title>Bulkhead vs Rate limiting.</title>
      <dc:creator>Mahendar Anumalla</dc:creator>
      <pubDate>Wed, 20 May 2026 14:47:30 +0000</pubDate>
      <link>https://dev.to/manumalla/bulkhead-vs-rate-limiting-5f5m</link>
      <guid>https://dev.to/manumalla/bulkhead-vs-rate-limiting-5f5m</guid>
      <description>&lt;p&gt;Rate Limiter (Time-based): A client can make 100 requests in a minute. Assume you have a fleet of automated clients or “boats” doing ping tests. If we don’t set a limit, they can spam the system with infinite requests and absolutely kill the servers. Rate limiting throttles them based on a clock.&lt;/p&gt;

&lt;p&gt;Bulkhead (Concurrency-based): Bulkhead counts the number of active, in-flight calls made to a downstream service. It cares about how many calls are currently processing and waiting for a response. Assume a developer accidentally creates an infinite loop that calls a downstream service across multiple threads simultaneously. Without a bulkhead, your resources get completely exhausted, and your service is gone.&lt;/p&gt;

&lt;p&gt;Basic Settings for Bulkhead:&lt;/p&gt;

&lt;p&gt;Resilience4j:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     Bulkhead:

    fastMethodBulkhead:

   type: SEMAPHORE or Thread

   maxConcurrentCalls: 10   // number of calls can make

    maxWaitDuration: 500ms   // max time it can wait before failing.

   queueCapacity: 10        // number of threads can wait in the queue.

        keepAliveDuration 10s.       //They can wait in the waiting queue.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;fastMethodBulkhead:&lt;br&gt;
            type: THREADPOOL&lt;br&gt;
            max-concurrent-calls: 20&lt;br&gt;
            max-wait-duration: 200ms&lt;br&gt;
            queueCapacity: 10&lt;br&gt;
            keepAliveDuration 10s   &lt;/p&gt;

&lt;p&gt;@Bulkhead(name = “fastMethodBulkhead”, type = Bulkhead.Type.SEMAPHORE)&lt;/p&gt;

&lt;p&gt;public String getQuickReview(String hotelId) {&lt;/p&gt;

&lt;p&gt;return quickClient.getReviews(hotelId);&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;// Long-running method - fewer concurrent threads&lt;/p&gt;

&lt;p&gt;@Bulkhead(name = “longMethodBulkhead”, type = Bulkhead.Type.THREADPOOL)&lt;/p&gt;

&lt;p&gt;public String getFullReview(String hotelId) {&lt;/p&gt;

&lt;p&gt;return slowClient.getDetailedReviews(hotelId);&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;We can override the bulkhead at method level using the service name, all methods need not to follow the same timings.&lt;/p&gt;

&lt;p&gt;When bulkhead rejects the request, it is fail-fast and throws a BulkheadFullException.&lt;/p&gt;

&lt;p&gt;Semaphore Vs ThreadPool&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semaphore is counter based,  based on the count it will allow concurrent threads. use the caller/main thread to update the counter. faster compare to thread pool. &lt;/li&gt;
&lt;li&gt;Thread Pool  use a separate thread to tract the count and executed asynchronously&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bulkhead</category>
      <category>ratelimiting</category>
      <category>microservices</category>
    </item>
    <item>
      <title># Mastering Spring Security: Architecture, JWT Patterns, and Production Gotchas</title>
      <dc:creator>Mahendar Anumalla</dc:creator>
      <pubDate>Wed, 20 May 2026 09:32:48 +0000</pubDate>
      <link>https://dev.to/manumalla/-mastering-spring-security-architecture-jwt-patterns-and-production-gotchas-3ml7</link>
      <guid>https://dev.to/manumalla/-mastering-spring-security-architecture-jwt-patterns-and-production-gotchas-3ml7</guid>
      <description>&lt;p&gt;If you have ever integrated Spring Security into an enterprise application, you know it feels like magic—until a random &lt;code&gt;401 Unauthorized&lt;/code&gt; or &lt;code&gt;403 Forbidden&lt;/code&gt; breaks your production build.&lt;/p&gt;

&lt;p&gt;To build secure, predictable APIs, you have to look past the boilerplate annotations and understand how Spring Security coordinates filters, manages authentication contexts, and delegates authorization. Let's break down how the internal engine works, look at optimal JWT strategies for microservices, and tackle common interview and production questions.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Core Architecture Blueprint
&lt;/h2&gt;

&lt;p&gt;Every request entering a secure Spring Boot application journeys through a layered ecosystem before it ever hits your &lt;code&gt;@RestController&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Request Lifecycle Pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request ➔ DelegatingFilterProxy ➔ SecurityFilterChain (Authentication Filter) 
        ➔ AuthenticationManager ➔ AuthenticationProvider ➔ UserDetailsService 
        ➔ SecurityContextHolder ➔ AuthorizationFilter ➔ Your Controller

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Gateway (&lt;code&gt;DelegatingFilterProxy&lt;/code&gt;):&lt;/strong&gt; Servlet containers don't natively know about Spring beans. This proxy bridges the gap, handing the request over to Spring's managed &lt;code&gt;SecurityFilterChain&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Interceptor (&lt;code&gt;AuthenticationFilter&lt;/code&gt;):&lt;/strong&gt; Filters extract raw credentials. For instance, a &lt;code&gt;BasicAuthenticationFilter&lt;/code&gt; looks for a header starting with &lt;code&gt;Basic&lt;/code&gt;, decodes the Base64 payload, and extracts the username and password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Tokenization:&lt;/strong&gt; Once extracted, the filter wraps these raw credentials into an unauthenticated token object, such as a &lt;code&gt;UsernamePasswordAuthenticationToken&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Coordinator (&lt;code&gt;AuthenticationManager&lt;/code&gt;):&lt;/strong&gt; The manager (typically implemented as &lt;code&gt;ProviderManager&lt;/code&gt;) holds a registry of available &lt;code&gt;AuthenticationProvider&lt;/code&gt; instances. It loops through them, matching a provider via its &lt;code&gt;.supports(Class&amp;lt;?&amp;gt; authentication)&lt;/code&gt; method.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified look under the hood of ProviderManager&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AuthenticationProvider&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;getProviders&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supports&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toTest&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authentication&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;copyDetails&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authentication&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
           &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Authentication succeeded!&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AuthenticationException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// Handled appropriately or thrown down the line&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Resolver (&lt;code&gt;AuthenticationProvider&lt;/code&gt; &amp;amp; &lt;code&gt;UserDetailsService&lt;/code&gt;):&lt;/strong&gt; The provider calls your database or identity provider (via &lt;code&gt;UserDetailsService&lt;/code&gt;), matches the passwords, and returns a fully populated, authenticated &lt;code&gt;Authentication&lt;/code&gt; object.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. Authentication Outcomes: Success vs. Failure
&lt;/h2&gt;

&lt;p&gt;What happens right after the &lt;code&gt;AuthenticationManager&lt;/code&gt; completes its evaluation?&lt;/p&gt;

&lt;h3&gt;
  
  
  On Success: Populating ThreadLocal
&lt;/h3&gt;

&lt;p&gt;The engine constructs a &lt;code&gt;Principal&lt;/code&gt; object containing user information and roles, then injects it directly into the &lt;code&gt;SecurityContextHolder&lt;/code&gt;. By default, this uses a &lt;code&gt;ThreadLocal&lt;/code&gt; strategy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Architectural Win:&lt;/strong&gt; Because it is stored in a &lt;code&gt;ThreadLocal&lt;/code&gt; wrapper, the active user's context stays accessible across any controller, service, or repository executed within that specific request thread—without forcing you to pass user objects as method arguments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  On Failure: The Exception Translation Layer
&lt;/h3&gt;

&lt;p&gt;If credentials mismatch, an exception is thrown. The &lt;code&gt;AuthenticationManager&lt;/code&gt; bubbles this back to the filters. This is where the &lt;code&gt;ExceptionTranslationFilter&lt;/code&gt; shines: it intercepts the security exceptions and translates them into appropriate HTTP responses (like a &lt;code&gt;401 Unauthorized&lt;/code&gt; challenge or custom JSON payload) via the configured &lt;code&gt;AuthenticationEntryPoint&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Stateful vs. Stateless Context Management
&lt;/h2&gt;

&lt;p&gt;How your application remembers authenticated users depends heavily on your architecture layout:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Stateful Session&lt;/th&gt;
&lt;th&gt;Stateless (JWT / Basic Auth)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server memory (&lt;code&gt;HttpSession&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Client-side Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Re-Identification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tracks requests using a &lt;code&gt;JSESSIONID&lt;/code&gt; cookie&lt;/td&gt;
&lt;td&gt;Inspects incoming token header on &lt;em&gt;every&lt;/em&gt; request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Filter Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SecurityContextHolderFilter&lt;/code&gt; extracts context from the session&lt;/td&gt;
&lt;td&gt;Token filter builds context completely fresh per request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thread Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cleared out and reloaded from session store&lt;/td&gt;
&lt;td&gt;Cleared completely at the end of the thread lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. The JWT Pattern for Distributed Microservices
&lt;/h2&gt;

&lt;p&gt;When implementing stateless JSON Web Tokens, you will usually extend &lt;code&gt;OncePerRequestFilter&lt;/code&gt;. This ensures your validation logic triggers exactly once per request lifecycle, bypassing edge cases where internal servlet forwards or redirects double-trigger a regular filter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming Request ➔ JWT Filter ➔ .parseClaimsJws(token) [Validates Signature &amp;amp; Expiry] ➔ Set SecurityContext ➔ Chain Continues

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Propagating JWT Tokens Downstream
&lt;/h3&gt;

&lt;p&gt;In a microservices ecosystem, you have two primary options when Service A needs to call Service B:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Token Propagation (Impersonation):&lt;/strong&gt; Forward the original client token received by your controller to downstream services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service-to-Service Identity:&lt;/strong&gt; Service A obtains its own dedicated machine-to-machine JWT from the Authorization Server (cached until expiry) and attaches it to calls bound for Service B.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are using the Token Propagation approach, you don't need to rebuild the string payload manually from user fields. Spring Security caches the raw token value inside the security context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Extracting the original token inside a RestTemplate/WebClient Interceptor&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;Jwt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
       &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAuthentication&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPrincipal&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
       &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTokenValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Attach 'token' to the Authorization Bearer header for outbound calls&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Method-Level Security &amp;amp; Role Hierarchies
&lt;/h2&gt;

&lt;p&gt;By dropping &lt;code&gt;@EnableMethodSecurity&lt;/code&gt; on a configuration class, Spring creates runtime proxies around your beans, using Aspect-Oriented Programming (AOP) to evaluate security rules before or after a method fires.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up a Clean Role Hierarchy
&lt;/h3&gt;

&lt;p&gt;Instead of cluttering your code with complex expressions like &lt;code&gt;@PreAuthorize("hasAnyRole('SALESREP', 'ADMIN')")&lt;/code&gt;, clean it up by declaring a logical hierarchy bean. This ensures an &lt;code&gt;ADMIN&lt;/code&gt; implicitly inherits all capabilities belonging to a &lt;code&gt;SALESREP&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RoleHierarchy&lt;/span&gt; &lt;span class="nf"&gt;roleHierarchy&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;RoleHierarchyImpl&lt;/span&gt; &lt;span class="n"&gt;hierarchy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RoleHierarchyImpl&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;hierarchy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHierarchy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ROLE_ADMIN &amp;gt; ROLE_SALESREP"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hierarchy&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Overriding Hierarchy Defaults &amp;amp; Custom Beans
&lt;/h3&gt;

&lt;p&gt;If you run into an isolated edgecase where an endpoint &lt;em&gt;must&lt;/em&gt; be accessed by a &lt;code&gt;SALESREP&lt;/code&gt; but explicitly blocked for an &lt;code&gt;ADMIN&lt;/code&gt;, combine logical operators or delegate to a custom Spring component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inline evaluation override&lt;/span&gt;
&lt;span class="nd"&gt;@PreAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hasRole('SALESREP') and !hasRole('ADMIN')"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Delegation to a specialized security service&lt;/span&gt;
&lt;span class="nd"&gt;@PreAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@securityService.canAccessLead(authentication)"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to leverage &lt;code&gt;@PostAuthorize&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;@PreAuthorize&lt;/code&gt; prevents a method from starting if criteria aren't met, &lt;code&gt;@PostAuthorize&lt;/code&gt; allows a method to completely execute, evaluates the returned entity, and throws an Access Denied exception if conditions fail. This is ideal for domain-driven permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"returnObject.isPublic() or returnObject.getOwner() == authentication.name"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="nf"&gt;findDocumentById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;documentRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Real-World Production Deep Dive (Q&amp;amp;A)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q: If a security chain features multiple filters, can we skip remaining filters once one successfully authenticates the request?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No, but also Yes.&lt;/strong&gt; The structure of a &lt;code&gt;FilterChain&lt;/code&gt; is static; once built, the request &lt;em&gt;must&lt;/em&gt; physical pass through every node in that sequence to reach the servlet. However, you can &lt;strong&gt;skip the heavy internal execution logic&lt;/strong&gt; inside your custom filters by wrapping your functional logic in a conditional check (e.g., executing only if &lt;code&gt;SecurityContextHolder.getContext().getAuthentication() == null&lt;/code&gt;). If it is already authenticated, the filter can immediately invoke &lt;code&gt;filterChain.doFilter(request, response)&lt;/code&gt; and return early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: How do we handle Blacklisting/Logouts for stateless JWTs?
&lt;/h3&gt;

&lt;p&gt;Because JWTs are self-contained and stateless, they remain valid until their expiration date, even if a user logs out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Scalable Approach:&lt;/strong&gt; Keep token life-cycles highly restricted (e.g., 15 minutes) to minimize windows of vulnerability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Strict Approach:&lt;/strong&gt; Upon logout, record the token signature into a fast, centralized key-value data cache like Redis with a Time-To-Live (TTL) tracking its remaining validity duration. On subsequent filter evaluations, query Redis. If the token matches the blacklist, halt the request execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Q: What if a JWT token expires right in the middle of downstream processing?
&lt;/h3&gt;

&lt;p&gt;Spring Security introduces a default clock skew tolerance configuration (typically 60 seconds) to account for slight server time variances across instances. To survive unexpected mid-flight expiration scenarios across deeper, time-consuming service calls, ensure downstream services implement sensible internal network timeout/tolerance settings or use API gateways that renew downstream-bound tokens automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Why are users experiencing random 401 Unauthorized or 403 Forbidden errors?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clock Drift:&lt;/strong&gt; If your distributed cluster nodes drift out of sync temporally, a token minted on Server A might be treated as prematurely expired or not-yet-valid by Server B. Ensure all cluster nodes use Network Time Protocol (NTP) daemons to keep system clocks tightly aligned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration Conflicts:&lt;/strong&gt; If a URL path pattern is protected via your &lt;code&gt;SecurityFilterChain&lt;/code&gt; settings (e.g., requiring &lt;code&gt;.hasRole("ADMIN")&lt;/code&gt;) but the underlying method controller is annotated with an explicit &lt;code&gt;@PreAuthorize("hasRole('USER')")&lt;/code&gt;, &lt;strong&gt;both criteria must pass&lt;/strong&gt;. A configuration mismatch here results in a frustrating &lt;code&gt;403 Forbidden&lt;/code&gt; response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Refresh Failures:&lt;/strong&gt; Front-end clients missing precise execution loops for silent token-refresh operations often send expired signatures, causing unexpected &lt;code&gt;401 Unauthorized&lt;/code&gt; errors during normal user flows.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>architecture</category>
      <category>java</category>
      <category>security</category>
      <category>springboot</category>
    </item>
  </channel>
</rss>
