Skip to content

JeffQuandt/bucket4j-spring-boot-starter

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Boot Starter for Bucket4j

This project is a Spring Boot Starter for Bucket4j. It can be used limiting the rate of access to your REST APIs.

  • Prevention of DoS Attacks, brute-force logins attempts

  • Request throttling for specific regions, unauthenticated users, authenticated users, not paying users.

The benefit of this project is the configuration of Bucket4j via properties or yaml files. You don’t have to write a single line of code. .

Getting started

To use the rate limit in your project you have to add the Bucket4j Spring Boot Starter dependency in your project. Additionally you need to add a JSR 107 provider like Ehcache or Hazelcast which will be auto configured with the Spring Boot Starter Cache.

<dependency>
	<groupId>com.giffing.bucket4j.spring.boot.starter</groupId>
	<artifactId>bucket4j-spring-boot-starter</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
	<groupId>org.ehcache</groupId>
	<artifactId>ehcache</artifactId>
</dependency>

The configuration can be done in the application.properties / application.yml. The current configuration limits all requests independently from the user. It allows a maximum of 5 requests within 10 seconds independently from the user.

bucket4j:
  enabled: true
  filters:
  - cache-name: buckets
    url: /*
    rate-limits:
    - bandwidths:
      - capacity: 5
        time: 10
        unit: seconds

For Ehcache 3 you also need a ehcache.xml which can be placed in the classpath. The configured cache name buckets must be defined in the configuration file.

spring:
  cache:
    jcache:
      config: classpath:ehcache.xml
<config xmlns="...">
	<cache alias="buckets">
		<expiry>
			<ttl unit="seconds">3600</ttl>
		</expiry>
		<heap unit="entries">1000000</heap>
	</cache>

</config>

Bucket4j properties

bucket4j.enabled=true # enable/disable bucket4j support
bucket4j.filters[0].cache-name=buckets # the name of the cache key
bucket4j.filters[0].filter-method=servlet # [servlet,zuul]
bucket4j.filters[0].filter-order=0
bucket4j.filters[0].http-response-body={ "message": "Too many requests" } # the json response which should be added to the body
bucket4j.filters[0].url=/*
bucket4j.filters[0].strategy=first # [first, all] if multiple rate limits configured the 'first' strategy stops the processing after the first matching
bucket4j.filters[0].rate-limits[0].filter-key-type=expression [default, ip, expression]
bucket4j.filters[0].rate-limits[0].expression=getRemoteAddress() # if filter-key-type is expression the key can be retrieved by an Spring Expression Language
bucket4j.filters[0].rate-limits[0].execute-condition=1==1 # an optional SpEl expression to decide to execute the rate limit or not
bucket4j.filters[0].rate-limits[0].skip-condition=1==1 # an optional SpEl expression to skip the rate limit
bucket4j.filters[0].rate-limits[0].bandwidths[0].capacity=10
bucket4j.filters[0].rate-limits[0].bandwidths[0].time=1
bucket4j.filters[0].rate-limits[0].bandwidths[0].unit=minutes

Filter types (bad name, should be renamed in the feature)

Filter types are predefined configuration option on how to define the key which should be used to limiting the requests.

Default

The default options doesn’t differentiates between incoming requests (user, ip, etc). Its a general limiting.

IP

The IP filter type limits the access based on the IP address (httpServletRequest.getRemoteAddr()). So each IP address will independently throttled.

Expression

The expression based filter type provides the most flexible one and uses the Spring Expression Language (SpEL). The expression compiles to a Java class which will be used. It provides an easy way to configure the throttling in different environments without writing one line of code.

Limiting based on IP-Address:

getRemoteAddress()

Limiting based on Username - If not logged in use IP-Address:

@securityService.username()?: getRemoteAddr()
/**
* You can define custom beans like the SecurityService which can be used in the SpEl expressions.
**/
@Service
public class SecurityService {

	public String username() {
		String name = SecurityContextHolder.getContext().getAuthentication().getName();
		if(name == "anonymousUser") {
			return null;
		}
		return name;
	}

}

Filter strategy

The filter strategy defines how the execution of the rate limits will be performed.

bucket4j.filters[0].strategy=first # [first, all]

first

The first is the default strategy. This the default strategy which only executes one rate limit configuration.

all

The all strategy executes all rate limit independently.

Configuration via properties

Simple configuration to allow a maximum of 5 requests within 10 seconds independently from the user.

bucket4j:
  enabled: true
  filters:
  - cache-name: buckets
    url: /*
    rate-limits:
    - filter-key-type: default
      bandwidths:
      - capacity: 5
        time: 10
        unit: seconds

Conditional filtering depending of anonymous or logged in user. Because the bucket4j.filters[0].strategy is first you havn’t to check in the second rate-limit that the user is logged in. Only the first one is executed.

bucket4j:
  enabled: true
  filters:
  - cache-name: buckets
    filter-method: servlet
    url: /*
    rate-limits:
    - filter-key-type: expression
      execute-condition:  @securityService.notSignedIn() # only for not logged in users
      expression: "getRemoteAddr()"
      bandwidths:
      - capacity: 10
        time: 1
        unit: minutes
    - filter-key-type: expression
      execute-condition: "@securityService.username() != 'admin'" # strategy is only evaluate first. so the user must be logged in and user is not admin
      expression: @securityService.username()
      bandwidths:
      - capacity: 1000
        time: 1
        unit: minutes
    - filter-key-type: expression
      execute-condition:  "@securityService.username() == 'admin'"  # user is admin
      expression: @securityService.username()
      bandwidths:
      - capacity: 1000000000
        time: 1
        unit: minutes

Configuration of multiple independently filters (servlet filter or zuul) with specific rate limit configurations.

bucket4j:
  enabled: true
  filters: # each config entry creates one servlet filter or zuul filter
  - cache-name: buckets # create new servlet filter with bucket4j configuration
    url: /admin*
    rate-limits:
    - filter-key-type: default # filter all requests independently from the source
      bandwidths: # maximum of 5 requests within 10 seconds
      - capacity: 5
        time: 10
        unit: seconds
  - cache-name: buckets
    url: /public*
    rate-limits:
    - filter-key-type: ip # IP based filter
      bandwidths: # maximum of 5 requests within 10 seconds
      - capacity: 5
        time: 10
        unit: seconds
  - cache-name: buckets
    url: /users*
    rate-limits:
    - filter-key-type: expression  # expression based filter key evaluation
    	  skip-condition: "@securityService.username() == 'admin'" # we don't check the rate limit if user is the admin user
    	  expression: "@securityService.username()?: getRemoteAddr()" # use the username as key. if authenticated use the ip address
      bandwidths:
    - capacity: 100
      time: 1
      unit: seconds
    - capacity: 10000
      time: 1
      unit: minutes

About

Spring Boot Starter for Bucket4j

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 74.7%
  • Shell 10.3%
  • Batchfile 8.0%
  • Groovy 7.0%