{"id":14982993,"url":"https://github.com/marcgiffing/bucket4j-spring-boot-starter","last_synced_at":"2025-05-14T20:03:20.667Z","repository":{"id":37611972,"uuid":"96845301","full_name":"MarcGiffing/bucket4j-spring-boot-starter","owner":"MarcGiffing","description":"Spring Boot Starter for Bucket4j","archived":false,"fork":false,"pushed_at":"2025-03-24T18:11:26.000Z","size":1121,"stargazers_count":327,"open_issues_count":22,"forks_count":67,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-13T15:08:57.641Z","etag":null,"topics":["bucket4j","java","rate-limit","spring","spring-boot","spring-boot-starter","spring-mvc","spring-webflux","throttling"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MarcGiffing.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-11T03:11:52.000Z","updated_at":"2025-03-27T10:31:39.000Z","dependencies_parsed_at":"2023-02-09T23:45:19.395Z","dependency_job_id":"6fba31dc-ac31-4ac0-b452-eff9903c5a8f","html_url":"https://github.com/MarcGiffing/bucket4j-spring-boot-starter","commit_stats":{"total_commits":459,"total_committers":25,"mean_commits":18.36,"dds":0.4183006535947712,"last_synced_commit":"7f16098cc93b59b56f3774af00e4c300fb6caadb"},"previous_names":[],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcGiffing%2Fbucket4j-spring-boot-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcGiffing%2Fbucket4j-spring-boot-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcGiffing%2Fbucket4j-spring-boot-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MarcGiffing%2Fbucket4j-spring-boot-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MarcGiffing","download_url":"https://codeload.github.com/MarcGiffing/bucket4j-spring-boot-starter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248732487,"owners_count":21152852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bucket4j","java","rate-limit","spring","spring-boot","spring-boot-starter","spring-mvc","spring-webflux","throttling"],"created_at":"2024-09-24T14:06:32.982Z","updated_at":"2025-04-13T15:09:12.878Z","avatar_url":"https://github.com/MarcGiffing.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":":url-repo: https://github.com/MarcGiffing/bucket4j-spring-boot-starter\n:url: https://github.com/MarcGiffing/bucket4j-spring-boot-starter/tree/master\n:url-examples: {url}/examples\n:url-config-cache: {url}/com/giffing/bucket4j/spring/boot/starter/config/cache\n\nimage:{url-repo}/actions/workflows/maven.yml/badge.svg[Build Status,link={url-repo}/actions/workflows/maven.yml]\nimage:{url-repo}/actions/workflows/codeql.yml/badge.svg[Build Status,link={url-repo}/actions/workflows/codeql.yml]\nimage:{url-repo}/actions/workflows/pmd.yml/badge.svg[Build Status,link={url-repo}/actions/workflows/pmd.yml]\n\nProject version overview:\n\n* \u003e 0.12.8 - Bucket4j 8.10.x - Spring Boot 3.3.x\n* \u003c= 0.12.7 - Bucket4j 8.9.0 - Spring Boot 3.2.x\n* 0.11.x - Bucket4j 8.8.0 - Spring Boot 3.2.x\n* 0.10.x - Bucket4j 8.7.0 - Spring Boot 3.1.x\n\n[[table_of_contents]]\n== Table of Contents\n\n* \u003c\u003cintroduction\u003e\u003e\n** \u003c\u003cintroduction_filter\u003e\u003e\n** \u003c\u003cintroduction_method\u003e\u003e\n* \u003c\u003cproject_configuration\u003e\u003e\n** \u003c\u003cbucket4j_complete_properties\u003e\u003e\n*** \u003c\u003crefill_speed\u003e\u003e\n*** \u003c\u003crate_limit_strategy\u003e\u003e\n*** \u003c\u003cskip_execution_predicates\u003e\u003e\n*** \u003c\u003ccache_key_filter\u003e\u003e\n*** \u003c\u003cpost-execute-condition\u003e\u003e\n\n* \u003c\u003cfeatures\u003e\u003e\n** \u003c\u003cdynamic_config_updates\u003e\u003e\n** \u003c\u003cmonitoring\u003e\u003e\n\n* \u003c\u003cappendix\u003e\u003e\n** \u003c\u003cmigration_guide\u003e\u003e\n** \u003c\u003coverview_cache_autoconfiguration\u003e\u003e\n** \u003c\u003cexamples\u003e\u003e\n** \u003c\u003cproperty_configuration_examples\u003e\u003e\n\n* \u003c\u003cInstallation\u003e\u003e\n\n[[introduction]]\n== Spring Boot Starter for Bucket4j\n\nThis project is a Spring Boot Starter for Bucket4j, allowing you to set access limits on your API effortlessly.\nIts key advantage lies in the configuration via properties or yaml files, eliminating the need for manual code authoring.\n\nHere are some example use cases:\n\n* Preventing DoS Attacks\n* Thwarting brute-force login attempts\n* Implementing request throttling for specific regions, unauthenticated users, and authenticated users\n* Applying rate limits for non-paying users or users with varying permissions\n\nThe project offers several features, some utilizing Spring's Expression Language for dynamic condition interpretation:\n\n* Cache key for differentiate the by username, IP address, ...)\n* Execution based on specific conditions\n* Skipping based on specific conditions\n* \u003c\u003cdynamic_config_updates\u003e\u003e\n* Post-token consumption actions based on filter/method results\n\nYou have two options for rate limit configuration: adding a filter for incoming web requests or applying fine-grained control at the method level.\n\n[[introduction_filter]]\n=== Use Filter for rate limiting\n\nFilters are customizable components designed to intercept incoming web requests, capable of rejecting requests to halt further processing.\nYou can incorporate multiple filters for various URLs or opt to bypass rate limits entirely for authenticated users.\nWhen the limit is exceeded, the web request is aborted, and the client receives an HTTP Status 429 Too Many Requests error.\n\nThis projects supports the following filters:\n\n* https://docs.oracle.com/javaee%2F6%2Fapi%2F%2F/javax/servlet/Filter.html[Servlet Filter] (Default)\n* https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/server/WebFilter.html[Webflux Webfilter] (reactive)\n* https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/global-filters.html[Spring Cloud Gateway Global Filter] (reactive)\n\n[source,properties]\n----\nbucket4j.filters[0].cache-name=buckets # the name of the cache\nbucket4j.filters[0].url=^(/hello).* # regular expression for the url\nbucket4j.filters[0].rate-limits[0].bandwidths[0].capacity=5 # refills 5 tokens every 10 seconds (intervall)\nbucket4j.filters[0].rate-limits[0].bandwidths[0].time=10\nbucket4j.filters[0].rate-limits[0].bandwidths[0].unit=seconds\nbucket4j.filters[0].rate-limits[0].bandwidths[0].refill-speed=intervall\n----\n\n[[introduction_method]]\n=== Use Annotations on methods for rate limiting\n\nUtilizing the '@RateLimiting' annotation, AOP intercepts your method.\nThis grants you comprehensive access to method parameters, empowering you to define the rate limit key or conditionally skip rate limiting with ease.\n\n==== Method Configuration\n\nBucket configuration is done in application.properties or application.yaml.\n\n.application.properties\n[source,properties]\n----\nbucket4j.methods[0].name=not_an_admin # the name of the configuration for annotation reference\nbucket4j.methods[0].cache-name=buckets # the name of the cache\nbucket4j.methods[0].rate-limits[0].bandwidths[0].capacity=5 # refills 5 tokens every 10 seconds (intervall)\nbucket4j.methods[0].rate-limits[0].bandwidths[0].time=10\nbucket4j.methods[0].rate-limits[0].bandwidths[0].unit=seconds\nbucket4j.methods[0].rate-limits[0].bandwidths[0].refill-speed=intervall\nbucket4j.default-method-metric-tags[0].key=IP\nbucket4j.default-method-metric-tags[0].expression=\"@testServiceImpl.getRemoteAddr()\" # reference to a bean method to fill the metric key\nbucket4j.default-method-metric-tags[0].types[0]=REJECTED_COUNTER\nbucket4j.default-method-metric-tags[0].types[1]=CONSUMED_COUNTER\nbucket4j.default-method-metric-tags[0].types[2]=PARKED_COUNTER\nbucket4j.default-method-metric-tags[0].types[3]=INTERRUPTED_COUNTER\nbucket4j.default-method-metric-tags[0].types[4]=DELAYED_COUNTER\n----\n\n.application.yaml\n[source,yaml]\n----\nbucket4j:\n  methods:\n    - name: not_an_admin # the name of the configuration for annotation reference\n      cache-name: buckets # the name of the cache\n      rate-limit:\n        bandwidths:\n          - capacity: 5 # refills 5 tokens every 10 seconds (intervall)\n            time: 30\n            unit: seconds\n            refill-speed: interval\n  default-method-metric-tags:\n    - key: IP\n      expression: \"@testServiceImpl.getRemoteAddr()\" # reference to a bean method to fill the metric key\n      types:\n        - REJECTED_COUNTER\n        - CONSUMED_COUNTER\n        - PARKED_COUNTER\n        - INTERRUPTED_COUNTER\n        - DELAYED_COUNTER\n----\n\nThe in this example configuration referenced testServiceImpl is not part of bucket4j-spring-boot-starter.\nIf you would like to have the IP as metric tag you need to implement you own mechanism for that.\n\nWorking example for method annotation and IPs in metrics: {url-examples}/general-tests/src/main/java/com/giffing/bucket4j/spring/boot/starter/example/jedis-redis[jedis-redis Example project]\n\n==== Method annotation\n\n[source,java]\n----\n@RateLimiting(\n            // reference to the property file\n            name = \"not_an_admin\",\n            // the rate limit is per user\n            cacheKey= \"#username\",\n            // only when the parameter is not admin\n            executeCondition = \"#username != 'admin'\",\n            // skip when parameter equals admin\n            skipCondition = \"#username eq 'admin\",\n            // the method name is added to the cache key to  prevent conflicts with other methods\n            ratePerMethod = true,\n            // if the limit is exceeded the fallback method is called. If not provided an exception is thrown\n            fallbackMethodName = \"myFallbackMethod\")\n    public String execute(String username) {\n        log.info(\"Method with Param {} executed\", username);\n        return myParamName;\n    }\n\n    // the fallback method must have the same signature\n    public String myFallbackMethod(String username) {\n        log.info(\"Fallback-Method with Param {} executed\", username);\n        return myParamName;\n    }\n----\n\nThe '@RateLimiting' annotation on class level executes the rate limit on all public methods of the class.\nWith '@IgnoreRateLimiting' you can ignore the rate limit at all on class level or for specific method on method level.\n\n[source,java]\n----\n@Component\n@Slf4j\n@RateLimiting(name = \"default\")\npublic class TestService {\n\n    public void notAnnotatedMethod() {\n        log.info(\"Method notAnnotatedMethod\");\n    }\n\n    @IgnoreRateLimiting\n    public void ignoreMethod() {\n        log.info(\"Method ignoreMethod\");\n    }\n\n}\n----\n\n==== Method dependencies\n\nAs the @RateLimiting mechanism uses AOP you need to ensure your spring-boot provides the necessary dependencies.\n\nJust add\n\n[source,xml]\n----\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-starter-aop\u003c/artifactId\u003e\n\u003c/dependency\u003e\n----\n\nto your project.\n\nYou can find some Configuration examples in the test project: {url-examples}/general-tests/src/main/java/com/giffing/bucket4j/spring/boot/starter/general/tests/method[Examples]\n\n[[project_configuration]]\n== Project Configuration\n\n[[bucket4j_complete_properties]]\n=== General Bucket4j properties\n\n[source,properties]\n----\nbucket4j.enabled=true # enable/disable bucket4j support\nbucket4j.cache-to-use= # If you use multiple caching implementation in your project and you want to choose a specific one you can set the cache here (jcache, hazelcast, ignite, redis)\n\n# Optional default metric tags for all filters\nbucket4j.default-metric-tags[0].key=IP\nbucket4j.default-metric-tags[0].expression=getRemoteAddr()\nbucket4j.default-metric-tags[0].types=REJECTED_COUNTER\n----\n\n==== Filter Bucket4j properties\n\n[source,properties]\n----\nbucket4j.filter-config-caching-enabled=true  #Enable/disable caching of filter configurations.\nbucket4j.filter-config-cache-name=filterConfigCache #The name of the cache where the configurations are stored. Defaults to 'filterConfigCache'.\nbucket4j.filters[0].id=filter1 # The id of the filter. This field is mandatory when configuration caching is enabled and should always be a unique string.\nbucket4j.filters[0].major-version=1 # [min = 1, max = 92 million] Major version number of the configuration.\nbucket4j.filters[0].minor-version=1 # [min = 1, max = 99 billion] Minor version number of the configuration. (intended for internal updates, for example based on CPU-usage, but can also be used for regular updates)\nbucket4j.filters[0].cache-name=buckets # the name of the cache key\nbucket4j.filters[0].filter-method=servlet # [servlet,webflux,gateway]\nbucket4j.filters[0].filter-order= # Per default the lowest integer plus 10. Set it to a number higher then zero to execute it after e.g. Spring Security.\nbucket4j.filters[0].http-content-type=application/json\nbucket4j.filters[0].http-status-code=TOO_MANY_REQUESTS # Enum value of org.springframework.http.HttpStatus\nbucket4j.filters[0].http-response-body={ \"message\": \"Too many requests\" } # the json response which should be added to the body\nbucket4j.filters[0].http-response-headers.\u003cMY_CUSTOM_HEADER\u003e=MY_CUSTOM_HEADER_VALUE # You can add any numbers of custom headers\nbucket4j.filters[0].hide-http-response-headers=true # Hides response headers like x-rate-limit-remaining or x-rate-limit-retry-after-seconds on rate limiting\nbucket4j.filters[0].url=.* # a regular expression\nbucket4j.filters[0].strategy=first # [first, all] if multiple rate limits configured the 'first' strategy stops the processing after the first matching\nbucket4j.filters[0].rate-limits[0].cache-key=getRemoteAddr() # defines the cache key. It will be evaluated with the Spring Expression Language\nbucket4j.filters[0].rate-limits[0].num-tokens=1 # The number of tokens to consume\nbucket4j.filters[0].rate-limits[0].execute-condition=1==1 # an optional SpEl expression to decide to execute the rate limit or not\nbucket4j.filters[1].rate-limits[0].post-execute-condition= # an optional SpEl expression to decide if the token consumption should only estimated for the incoming request and the returning response used to check if the token must be consumed: getStatus() eq 401\nbucket4j.filters[0].rate-limits[0].execute-predicates[0]=PATH=/hello,/world # On the HTTP Path as a list\nbucket4j.filters[0].rate-limits[0].execute-predicates[1]=METHOD=GET,POST # On the HTTP Method\nbucket4j.filters[0].rate-limits[0].execute-predicates[2]=QUERY=HELLO # Checks for the existence of a Query Parameter\nbucket4j.filters[0].rate-limits[0].skip-condition=1==1 # an optional SpEl expression to skip the rate limit\nbucket4j.filters[0].rate-limits[0].tokens-inheritance-strategy=RESET # [RESET, AS_IS, ADDITIVE, PROPORTIONALLY], defaults to RESET and is only used for dynamically updating configurations\nbucket4j.filters[0].rate-limits[0].bandwidths[0].id=bandwidthId # Optional when using tokensInheritanceStrategy.RESET or if the rate-limit only contains 1 bandwidth. The id should be unique within the rate-limit.\nbucket4j.filters[0].rate-limits[0].bandwidths[0].capacity=10\nbucket4j.filters[0].rate-limits[0].bandwidths[0].refill-capacity= # default is capacity\nbucket4j.filters[0].rate-limits[0].bandwidths[0].time=1\nbucket4j.filters[0].rate-limits[0].bandwidths[0].unit=minutes\nbucket4j.filters[0].rate-limits[0].bandwidths[0].initial-capacity= # Optional initial tokens\nbucket4j.filters[0].rate-limits[0].bandwidths[0].refill-speed=greedy # [greedy,interval]\nbucket4j.filters[0].metrics.enabled=true\nbucket4j.filters[0].metrics.types=CONSUMED_COUNTER,REJECTED_COUNTER # (optional) if your not interested in the consumed counter you can specify only the rejected counter\nbucket4j.filters[0].metrics.tags[0].key=IP\nbucket4j.filters[0].metrics.tags[0].expression=getRemoteAddr()\nbucket4j.filters[0].metrics.tags[0].types=REJECTED_COUNTER # (optional) this tag should for example only be applied for the rejected counter\nbucket4j.filters[0].metrics.tags[1].key=URL\nbucket4j.filters[0].metrics.tags[1].expression=getRequestURI()\nbucket4j.filters[0].metrics.tags[2].key=USERNAME\nbucket4j.filters[0].metrics.tags[2].expression=@securityService.username() != null ? @securityService.username() : 'anonym'\n----\n\n[[refill_speed]]\n==== Refill Speed\n\nThe refill speed defines the period of the regeneration of consumed tokens.\nThis starter supports two types of token regeneration.\nThe refill speed can be set with the following property:\n\n[source,properties]\n----\nbucket4j.filters[0].rate-limits[0].bandwidths[0].refill-speed=greedy # [greedy,interval]\n----\n\n* *greedy*: This is the default refill speed and tries to add tokens as soon as possible.\n* *interval*: You can alternatively chose *interval* for the token regeneration which refills the token in a fixed interval.\n\nYou can read more about the refill speed in the https://bucket4j.com/8.1.1/toc.html#refill[official documentation].\n\n[[rate_limit_strategy]]\n==== Rate Limit Strategy\n\nIf multiple rate limits are defined the strategy defines how many of them should be executed.\n\n[source,properties]\n----\nbucket4j.filters[0].strategy=first # [first, all]\n----\n\n===== first\n\nThe *first* is the default strategy.\nThis the default strategy which only executes one rate limit configuration.\nIf a rate limit configuration is skipped due to the provided condition.\nIt does not count as an executed rate limit.\n\n===== all\n\nThe *all* strategy executes all rate limit independently.\n\n[[skip_execution_predicates]]\n==== Skip and Execution Predicates (experimental)\n\nSkip and Execution Predicates can be used to conditionally skip or execute the rate limiting.\nEach predicate has a unique name and a self-contained configuration.\nThe following section describes the build in Execution Predicates and how to use them.\n\n===== Path Predicates\n\nThe Path Predicate takes a list of path parameters where any of the paths must match.\nSee https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java[PathPattern] for the available configuration options.\nSegments are not evaluated further.\n\n[source,properties]\n----\nbucket4j.filters[0].rate-limits[0].skip-predicates[0]=PATH=/hello,/world,/admin\nbucket4j.filters[0].rate-limits[0].execute-predicates[0]=PATH=/hello,/world,/admin\n----\n\nMatches the paths '/hello', '/world' or '/admin'.\n\n===== Method Predicate\n\nThe Method Predicate takes a list of method parameters where any of the methods must match the used HTTP method.\n\n----\nbucket4j.filters[0].rate-limits[0].skip-predicates[0]=METHOD=GET,POST\nbucket4j.filters[0].rate-limits[0].execute-predicates[0]=METHOD=GET,POST\n----\n\nMatches if the HTTP method is 'GET' or 'POST'.\n\n===== Query Predicate\n\nThe Query Predicate takes a single parameter to check for the existence of the query parameter.\n\n----\nbucket4j.filters[0].rate-limits[0].skip-predicates[0]=QUERY=PARAM_1\nbucket4j.filters[0].rate-limits[0].execute-predicates[0]=QUERY=PARAM_1\n----\n\nMatches if the query parameter 'PARAM_1' exists.\n\n===== Header Predicate\n\nThe Header Predicate takes to parameters.\n\n. First - The name of the Header Parameter which must match exactly\n. Second - An optional regular expression where any existing header under the name must match\n\n----\nbucket4j.filters[0].rate-limits[0].execute-predicates[0]=Content-Type,.*PDF.*\n----\n\nMatches if the query parameter 'PARAM_1' exists.\n\n===== Custom Predicate\n\nYou can also define you own Execution Predicate:\n\n[source,java]\n----\n@Component\n@Slf4j\npublic class MyQueryExecutePredicate extends ExecutePredicate\u003cHttpServletRequest\u003e {\n\n\tprivate String query;\n\n\tpublic String name() {\n\t\t// The name which can be used on the properties\n\t\treturn \"MY_QUERY\";\n\t}\n\n\tpublic boolean test(HttpServletRequest t) {\n\t    // the logic to implement the predicate\n\t\tboolean result = t.getParameterMap().containsKey(query);\n\t\tlog.debug(\"my-query-parameter;value:%s;result:%s\".formatted(query, result));\n\t\treturn result;\n\t}\n\n\tpublic ExecutePredicate\u003cHttpServletRequest\u003e parseSimpleConfig(String simpleConfig) {\n\t\t// the configuration which is configured behind the equal sign\n\t\t// MY_QUERY=P_1 -\u003e simpleConfig == \"P_1\"\n\t\t//\n\t\tthis.query = simpleConfig;\n\t\treturn this;\n\t}\n}\n----\n\n[[cache_key_filter]]\n=== Cache Key for Filter\n\nTo differentiate incoming request (e.g. by IP address) you can provide an expression which is used as a key resolver for the underlying cache.\n\nDepending on the filter method [servlet, webflux, gateway] different SpEL root objects can be used in the expression so that you have a direct access to the method of these request objects:\n\n* servlet: jakarta.servlet.http.HttpServletRequest (e.g. getRemoteAddr() or getRequestURI())\n* webflux: org.springframework.http.server.reactive.ServerHttpRequest\n* gateway: org.springframework.http.server.reactive.ServerHttpRequest\n\nThe configured URL which is used for filtering is added to the cache-key to provide a unique cache-key for multiple URL.\nYou can read more about it https://github.com/MarcGiffing/bucket4j-spring-boot-starter/issues/19[here].\n\n*Limiting based on IP-Address*:\n\n[source]\n----\ngetRemoteAddress()\n----\n\n*Limiting based on Username - If not logged in use IP-Address*:\n\n[source]\n----\n@securityService.username()?: getRemoteAddr()\n----\n\n[source,java]\n----\n/**\n* You can define custom beans like the SecurityService which can be used in the SpEl expressions.\n**/\n@Service\npublic class SecurityService {\n\n\tpublic String username() {\n\t\tString name = SecurityContextHolder.getContext().getAuthentication().getName();\n\t\tif(name.equals(\"anonymousUser\")) {\n\t\t\treturn null;\n\t\t}\n\t\treturn name;\n\t}\n\n}\n----\n\n[[post-execute-condition]]\n=== Post Execution (Consume) Condition\n\nIf you define a post execution condition the available tokens are not consumed on a rate limit configuration execution.\nIt will only estimate the remaining available tokens.\nOnly if there are no tokens left the rate limit is applied by.\nIf the request was proceeded by the application we can check the return value check if the token should be consumed.\n\nExample: You want to limit the rate only for unauthorized users.\nYou can't consume the available token for the incoming request because you don't know if the user will be authenticated afterward.\nWith the post execute condition you can check the HTTP response status code and only consume the token if it has the status Code 401 UNAUTHORIZED.\n\nimage::src/main/doc/plantuml/post_execution_condition.png[]\n\n[[features]]\n== Features\n\n[[dynamic_config_updates]]\n=== Dynamically updating rate limits (experimental)\n\nSometimes it might be useful to modify filter configurations during runtime.\nIn order to support this behaviour a cache-based configuration update system has been added.\nThe following section describes what configurations are required to enable this feature.\n\n==== Properties\n\n===== base properties\n\nIn order to dynamically update rate limits, it is required to enable caching for filter configurations.\n\n[source,properties]\n----\nbucket4j.filter-config-caching-enabled=true  #Enable/disable caching of filter configurations.\nbucket4j.filter-config-cache-name=filterConfigCache #The name of the cache where the configurations are stored. Defaults to 'filterConfigCache'.\n----\n\n===== Filter properties\n\n- When filter caching is enabled, it is mandatory to configure a unique id for every filter.\n- Configurations are implicitly replaced based on a combination of the major and minor version.\nIf changes are made to the configuration without increasing either of the version numbers, it is most likely that the changes will not be applied.\nInstead the cached configuration will be used.\n\n[source,properties]\n----\nbucket4j.filters[0].id=filter1 #The id of the filter. This should always be a unique string.\nbucket4j.filters[0].major-version=1 #[min = 1, max = 92 million] Major version number.\nbucket4j.filters[0].minor-version=1 #[min = 1, max = 99 billion] Minor version number. (intended for internal updates, for example based on CPU-usage, but can also be used for regular updates)\n----\n\n===== RateLimit properties\n\nFor each ratelimit a tokens inheritance strategy can be configured.\nThis strategy will determine how to handle existing rate limits when replacing a configuration.\nIf no strategy is configured it will default to 'RESET'.\n\nFurther explanation of the strategies can be found at https://bucket4j.com/8.1.1/toc.html#tokensinheritancestrategy-explanation[Bucket4J TokensInheritanceStrategy explanation]\n\n[source,properties]\n----\nbucket4j.filters[0].rate-limits[0].tokens-inheritance-strategy=RESET #[RESET, AS_IS, ADDITIVE, PROPORTIONALLY]\n----\n\n===== Bandwidth properties\n\nThis property is only mandatory when *BOTH* of the following statements apply to your configuration.\n\n- The rate-limit uses a different TokensInheritanceStrategy than 'RESET'\n- The rate-limit contains more than 1 bandwidth\n\nThis is required so Bucket4J knows how to map the current bandwidth tokens to the updated bandwidths.\nIt is possible to configure id's when 'RESET' strategy is applied, but the id's should still be unique within the rate-limit then.\n\n[source,properties]\n----\nbucket4j.filters[0].rate-limits[0].bandwidths[0].id=bandwidthId #The id of the bandwidth; Optional when the rate-limit only contains 1 bandwidth or when using tokensInheritanceStrategy.RESET.\n----\n\n==== Example project\n\nAn example on how to dynamically update a filter can be found at:\n{url-examples}/caffeine[Caffeine example project].\n\nSome important considerations:\n\n- This is an experimental feature and might be subject to changes.\n- Configurations will be read from the cache during startup (when using a persistent cache).\nThis means that putting corrupted configurations into the cache during runtime can cause the application to crash during startup.\n- Most configuration errors can be prevented by using the Jakarta validator to validate updated configurations.\nIn the example this is done by adding @Valid to the request body method parameter, but it is also possible to @Autowire the Validator and use it directly to validate the configuration.\n- Some Filter properties are not intended to be modified during runtime.\nTo simplify validating a configuration update the Bucket4JUtils.validateConfigurationUpdate method has been added.\nThis method executes the following validations and will return a ResponseEntity:\n** old configuration != null  -\u003e NOT_FOUND\n** new configuration has a higher version than the old configuration -\u003e BAD_REQUEST\n** filterMethod not changed -\u003e BAD_REQUEST\n** filterOrder not changed -\u003e BAD_REQUEST\n** cacheName not changed -\u003e BAD_REQUEST\n- The configCacheManager currently does *not* contain validation in the setValue method.\nThe configuration should be validated before calling the this method.\n\n[[monitoring]]\n=== Monitoring - Spring Boot Actuator\n\nSpring Boot ships with a great support for collecting metrics.\nThis project automatically provides metric information about the consumed and rejected buckets.\nYou can extend these information with configurable https://micrometer.io/docs/concepts#_tag_naming[custom tags] like the username or the IP-Address which can then be evaluated in a monitoring system like prometheus/grafana.\n\n[source,yml]\n----\nbucket4j:\n  enabled: true\n  filters:\n  - cache-name: buckets\n    filter-method: servlet\n    filter-order: 1\n    url: .*\n    metrics:\n      tags:\n        - key: IP\n          expression: getRemoteAddr()\n          types: REJECTED_COUNTER # for data privacy reasons the IP should only be collected on bucket rejections\n        - key: USERNAME\n          expression: \"@securityService.username() != null ? @securityService.username() : 'anonym'\"\n        - key: URL\n          expression: getRequestURI()\n    rate-limits:\n      - execute-condition:  \"@securityService.username() == 'admin'\"\n        cache-key: \"@securityService.username()?: getRemoteAddr()\"\n        bandwidths:\n        - capacity: 30\n          time: 1\n          unit: minutes\n----\n\n[[appendix]]\n== Appendix\n\n[[migration_guide]]\n=== Migration Guide\n\nThis section is meant to help you migrate your application to new version of this starter project.\n\n==== Spring Boot Starter Bucket4j 0.12\n\n* Removed deprecated 'bucket4j.filters[x].rate-limits[x].expression' property.\nUse 'bucket4j.filters[x].rate-limits[x].cache-key' instead.\n* three new metric counter are added per default (PARKED, INTERRUPTED and DELAYED)\n\n==== Spring Boot Starter Bucket4j 0.9\n\n* Upgrade to Spring Boot 3\n* Spring Boot 3 requires Java 17 so use at least Java 17\n* Replaced Java 8 compatible Bucket4j dependencies\n* Exclude example webflux-infinispan due to startup problems\n\n==== Spring Boot Starter Bucket4j 0.8\n\n===== Compatibility to Java 8\n\nThe version 0.8 tries to be compatible with Java 8 as long as Bucket4j is supporting Java 8. With the release of Bucket4j 8.0.0 Bucket4j decided to migrate to Java 11 but provides dedicated artifacts for Java 8.\nThe project is switching to the dedicated artifacts which supports Java 8. You can read more about it https://github.com/bucket4j/bucket4j#java-compatibility-matrix[here].\n\n===== Rename property expression to cache-key\n\nThe property *..rate-limits[0].expression* is renamed to *..rate-limits[0].cache-key*.\nAn Exception is thrown on startup if the *expression* property is configured.\n\nTo ensure that the property is not filled falsely the property is marked with *@Null*.\nThis change requires a Bean Validation implementation.\n\n===== JSR 380 - Bean Validation implementation required\n\nTo ensure that the Bucket4j property configuration is correct an Validation API implementation is required.\nYou can add the Spring Boot Starter Validation which will automatically configures one.\n\n[source,xml]\n----\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n  \u003cartifactId\u003espring-boot-starter-validation\u003c/artifactId\u003e\n\u003c/dependency\u003e\n----\n\n===== Explicit Configuration of the Refill Speed - API Break\n\nThe refill speed of the Buckets can now configured explicitly with the Enum RefillSpeed.\nYou can choose between a greedy or interval refill see the https://bucket4j.com/8.1.1/toc.html#refill[official documentation].\n\nBefore 0.8 the refill speed was configured implicitly by setting the fixed-refill-interval property explicit.\n\n[source,properties]\n----\nbucket4j.filters[0].rate-limits[0].bandwidths[0].fixed-refill-interval=0\nbucket4j.filters[0].rate-limits[0].bandwidths[0].fixed-refill-interval-unit=minutes\n----\n\nThese properties are removed and replaced by the following configuration:\n\n[source,properties]\n----\nbucket4j.filters[0].rate-limits[0].bandwidths[0].refill-speed=interval\n----\n\nYou can read more about the refill speed configuration here \u003c\u003crefill_speed\u003e\u003e\n\n[[overview_cache_autoconfiguration]]\n=== Overview Cache Autoconfiguration\n\nThe following list contains the Caching implementation which will be autoconfigured by this starter.\n\n[cols=\"1,1,1\"]\n|===\n|*Reactive*\n|*Name*\n|*cache-to-use*\n\n|N\n|{url-config-cache}/jcache/JCacheBucket4jConfiguration.java[JSR 107 -JCache]\n|jcache\n\n|Yes\n|{url-config-cache}/ignite/IgniteBucket4jCacheConfiguration.java[Ignite]\n|jcache-ignite\n\n|no\n|{url-config-cache}/hazelcast/HazelcastSpringBucket4jCacheConfiguration.java[Hazelcast]\n|hazelcast-spring\n\n|yes\n|{url-config-cache}/hazelcast/HazelcastReactiveBucket4jCacheConfiguration.java[Hazelcast]\n|hazelcast-reactive\n\n|Yes\n|{url-config-cache}/infinispan/InfinispanBucket4jCacheConfiguration.java[Infinispan]\n|infinispan\n\n|No\n|{url-config-cache}/redis/jedis/JedisBucket4jConfiguration.java[Redis-Jedis]\n|redis-jedis\n\n|Yes\n|{url-config-cache}/redis/lettuce/LettuceBucket4jConfiguration.java[Redis-Lettuce]\n|redis-lettuce\n\n|Yes\n|{url-config-cache}/redis/redission/RedissonBucket4jConfiguration.java[Redis-Redisson]\n|redis-redisson\n\n|===\n\nInstead of determine the Caching Provider by the Bucket4j Spring Boot Starter project you can implement the SynchCacheResolver or the AsynchCacheResolver by yourself.\n\nYou can enable the cache auto configuration explicitly by using the *cache-to-use* property name or setting it to an invalid value to disable all auto configurations.\n\n[source,properties]\n----\nbucket4j.cache-to-use=jcache #\n----\n\n[[examples]]\n=== Examples\n\n* {url-examples}/ehcache[Ehcache]\n* {url-examples}/hazelcast[Hazelcast]\n* {url-examples}/caffeine[Caffeine]\n* {url-examples}/redis-jedis[Redis Jedis]\n* {url-examples}/redis-lettuce[Redis Lettuce]\n* {url-examples}/redis-redisson[Redis Redisson]\n* {url-examples}/webflux[Webflux (Async)]\n* {url-examples}/gateway[Spring Cloud Gateway (Async)]\n* {url-examples}/webflux-infinispan[Infinispan]\n\n[[property_configuration_examples]]\n=== Property Configuration Examples\n\nSimple configuration to allow a maximum of 5 requests within 10 seconds independently from the user.\n\n[source,yml]\n----\nbucket4j:\n  enabled: true\n  filters:\n  - cache-name: buckets\n    url: .*\n    rate-limits:\n      - bandwidths:\n        - capacity: 5\n          time: 10\n          unit: seconds\n----\n\nConditional filtering depending of anonymous or logged in user.\nBecause the *bucket4j.filters[0].strategy* is *first*\nyou don't have to check in the second rate-limit that the user is logged in.\nOnly the first one is executed.\n\n[source,yml]\n----\nbucket4j:\n  enabled: true\n  filters:\n  - cache-name: buckets\n    filter-method: servlet\n    url: .*\n    rate-limits:\n      - execute-condition:  @securityService.notSignedIn() # only for not logged in users\n        cache-key: \"getRemoteAddr()\"\n        bandwidths:\n        - capacity: 10\n          time: 1\n          unit: minutes\n      - execute-condition: \"@securityService.username() != 'admin'\" # strategy is only evaluate first. so the user must be logged in and user is not admin\n        cache-key: @securityService.username()\n        bandwidths:\n        - capacity: 1000\n          time: 1\n          unit: minutes\n      - execute-condition:  \"@securityService.username() == 'admin'\"  # user is admin\n        cache-key: @securityService.username()\n        bandwidths:\n        - capacity: 1000000000\n          time: 1\n          unit: minutes\n----\n\nConfiguration of multiple independently filters (servlet|gateway|webflux filters) with specific rate limit configurations.\n\n[source,yml]\n----\nbucket4j:\n  enabled: true\n  filters: # each config entry creates one servlet filter or other filter\n  - cache-name: buckets # create new servlet filter with bucket4j configuration\n    url: /admin*\n    rate-limits:\n      bandwidths: # maximum of 5 requests within 10 seconds\n      - capacity: 5\n        time: 10\n        unit: seconds\n  - cache-name: buckets\n    url: /public*\n    rate-limits:\n      - cache-key: getRemoteAddress() # IP based filter\n        bandwidths: # maximum of 5 requests within 10 seconds\n        - capacity: 5\n          time: 10\n          unit: seconds\n  - cache-name: buckets\n    url: /users*\n    rate-limits:\n      - skip-condition: \"@securityService.username() == 'admin'\" # we don't check the rate limit if user is the admin user\n        cache-key: \"@securityService.username()?: getRemoteAddr()\" # use the username as key. if authenticated use the ip address\n        bandwidths:\n        - capacity: 100\n          time: 1\n          unit: seconds\n        - capacity: 10000\n          time: 1\n          unit: minutes\n----\n\n[[installation]]\n== Installation\n\nAdding `bucket4j-spring-boot-starter` to your project.\n\nhttps://mvnrepository.com/artifact/com.giffing.bucket4j.spring.boot.starter/bucket4j-spring-boot-starter[Maven Central]\n\n[source,xml]\n----\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.giffing.bucket4j\u003c/groupId\u003e\n    \u003cartifactId\u003ebucket4j-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e${version}\u003c/version\u003e\n\u003c/dependency\u003e\n----\n\n\n* Back to \u003c\u003ctable_of_contents\u003e\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcgiffing%2Fbucket4j-spring-boot-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcgiffing%2Fbucket4j-spring-boot-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcgiffing%2Fbucket4j-spring-boot-starter/lists"}