{"id":1166,"date":"2023-01-14T01:07:18","date_gmt":"2023-01-13T21:37:18","guid":{"rendered":"https:\/\/m-shaeri.ir\/blog\/?p=1166"},"modified":"2023-03-01T13:50:05","modified_gmt":"2023-03-01T10:20:05","slug":"securing-and-rate-limiting-endpoints-with-spring-cloud-gateway-spring-security-and-jwt","status":"publish","type":"post","link":"https:\/\/mshaeri.com\/blog\/securing-and-rate-limiting-endpoints-with-spring-cloud-gateway-spring-security-and-jwt\/","title":{"rendered":"Securing And Rate Limiting Endpoints With Spring Cloud Gateway, Spring Security And JWT"},"content":{"rendered":"\n<p>API gateway is an infrastructure layer that places in front of application to route requests from the client side to the appropriate service. Spring Cloud Gateway is an API gateway on top of Spring WebFlux (non-blocking and asynchronous), also implements other cross cutting concerns such as security, rate limiting, resiliency and etc.<\/p>\n\n\n\n<p>Although applying security checks on api-gateway is a necessity, it is not enough to prevent attacks such as DDoS. So, considering our resources, we need a mechanism to monitor, control, and limit the number of requests we recieve\/process. This is what the Spring Cloud Gateway does for us. In this post, I am going to explain how to implement routing, securing and rate limiting tasks in microservices architecture using Spring Cloud Gateway and spring security in a sample project. You can find final project from the <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/birddevelper\/MicroservicesWithSpringSecurityJWT\" target=\"_blank\"><strong>Github repository<\/strong><\/a>.<\/p>\n\n\n\n<p>The project consists of two back-end services and one api-gateway as follows :<\/p>\n\n\n\n<ol><li><strong>Product service  : <\/strong> It serves requests related to product domain. It has endpoints accessible to everyone, only users, and only admins.<\/li><li><strong>Authentication service :<\/strong> It serves the authentication request, verifies the credentials, generates JSON Web Token (JWT) and send it to client.<\/li><li><strong>Api-Gateway :<\/strong> It routes requests from clients to the right back-end service ( here we have only one back-end services).<\/li><\/ol>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/m-shaeri.ir\/blog\/wp-content\/uploads\/2023\/01\/secure_rate_limiting_spring_cloud_gateway_jwt_redis_spring_security_jwt.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/m-shaeri.ir\/blog\/wp-content\/uploads\/2023\/01\/secure_rate_limiting_spring_cloud_gateway_jwt_redis_spring_security_jwt-1024x555.jpg\" alt=\"\" class=\"wp-image-1236\" width=\"512\" height=\"278\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/secure_rate_limiting_spring_cloud_gateway_jwt_redis_spring_security_jwt-1024x555.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/secure_rate_limiting_spring_cloud_gateway_jwt_redis_spring_security_jwt-300x163.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/secure_rate_limiting_spring_cloud_gateway_jwt_redis_spring_security_jwt-768x416.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/secure_rate_limiting_spring_cloud_gateway_jwt_redis_spring_security_jwt.jpg 1171w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/a><figcaption>Microservices using spring security and rate limitation on api-gateway<\/figcaption><\/figure><\/div>\n\n\n\n<p>First, we need to add dependencies related to JWT and spring security  to maven pom.xml file of all services including API-gateway, because we work with them in all the services.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">\n&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-security&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;io.jsonwebtoken&lt;\/groupId&gt;\n    &lt;artifactId&gt;jjwt-api&lt;\/artifactId&gt;\n    &lt;version&gt;0.11.1&lt;\/version&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;io.jsonwebtoken&lt;\/groupId&gt;\n    &lt;artifactId&gt;jjwt-impl&lt;\/artifactId&gt;\n    &lt;version&gt;0.11.1&lt;\/version&gt;\n    &lt;scope&gt;runtime&lt;\/scope&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>API Gateway<\/strong><\/h3>\n\n\n\n<p>Add Spring cloud gateway and Redis dependencies to the ApiGateway project. We add Redis dependency because for rate limiting, the SCG uses redis to store request information, also we need a Redis service in our project.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-cloud-starter-gateway&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-data-redis-reactive&lt;\/artifactId&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n\n\n\n<p>Defining routing rules in <strong>application.yml<\/strong> is  more preferred way comparing to programmatic way, because it&#8217;s more readable and extendable.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">jwt:\n  secret: WillBeSetByDockerCompose\nserver:\n  port: 8010\nspring:\n  redis:\n    host: redis\n    port: 6379\n  application:\n    name: gateway\n  cloud:\n    gateway:\n      routes:\n        - id: product-service\n          uri: http:\/\/productService:8084\n          predicates:\n            - Path=\/products\/**\n          filters:\n            - StripPrefix=0\n            - name : RequestRateLimiter\n              args:\n                redis-rate-limiter.replenishRate: 1\n                redis-rate-limiter.burstCapacity: 2\n                redis-rate-limiter.requestedTokens: 2\n        - id: auth-service\n          uri: http:\/\/authService:8080 <em>\n          <\/em>predicates:\n            - Path=\/auth\/**\n          filters :\n            - StripPrefix=0\n\n<\/code><\/pre>\n\n\n\n<p>In the  <strong>application.yml<\/strong> file, I defined the<strong> jwt.secret<\/strong> key, it will be overridden by environment variable that we define in docker compose and read from a .env file beside docker-compose.yml file, it later will be used in jwt token creation and validation, however, in real world project working with Kubernetes, it&#8217;s better to use other methods to store your critical secret keys such as <a href=\"https:\/\/developer.hashicorp.com\/vault\/docs\/platform\/k8s\">Valut<\/a> that is much more secure than storing secrets in files. <\/p>\n\n\n\n<p>In the next line Api-gateway listening port is set to 8010. Next, we configure the routing rules. As we run our project on docker compose, we don&#8217;t need to use IP address of services in routing rules, instead, we can use the service name defined in docker compose file. For example, for product service definition in docker-compose file , I used <strong>productService <\/strong>to name the service, then I routed request targeting product service to <strong>http:\/\/productService:8084<\/strong>. With the <strong>StripPrefix <\/strong>parameter we can set the number of parts in the path to be removed from the request before sending it to the back-end service. For example, in this project we are going to place <strong>api\/<\/strong> prefix behind product back-end service APIs, so that, clients should call <strong>http:\/\/serverAddress:8010\/api\/products\/productList<\/strong> to reach <strong><strong>products\/productList<\/strong> <\/strong>endpoint of the product service, but if we send the client request to product service without modification, we will get 404 error, because, target service does not have endpoint path starting by <strong>api\/<\/strong>. So, we need to remove api\/ from client request path by setting <strong>StripPrefix = 1<\/strong>. It simply remove first part of the path(<strong>\/api<\/strong>) from the request.<\/p>\n\n\n\n<p>In configuration related to API rate limiting, you can see three parameters :<\/p>\n\n\n\n<ul><li><span class=\"has-inline-color has-vivid-cyan-blue-color\"><strong>redis-rate-limiter.replenishRate<\/strong><\/span> : Indicates the number of requests per second the server would process.<\/li><li><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">redis-rate-limiter.burstCapacity<\/span><\/strong> : Indicates the maximum number of requests clients are allowed to send in a second.<\/li><li><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">redis-ratelimiter.requestedTokens<\/span><\/strong> : One API may need to perform more than one light\/heavy operation to fulfill a request. This parameter helps to recognize that API may cost more than one simple request with a simple operation.<\/li><\/ul>\n\n\n\n<p><span class=\"has-inline-color has-vivid-red-color\">Assume that an API is allowed to have a burst (peak) of 300 requests in a single second and your replenish rate is 100 request per second. It means that if a burst in the number of requests occurs, clients have already passed two cycles&#8217; allowance so the server accepts those 300 requests, but all other requests will be dropped in next 3 seconds.<\/span><\/p>\n\n\n\n<p>To let the api rate limiting configuration works, we need to declare a simple custom <strong>KeyResolver<\/strong> bean :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">@Bean\nKeyResolver userKeyResolver() {\n      return exchange -&gt; Mono.just(\"1\");\n }<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>In this project Api-gateway has one other important responsibility, it should secure the application and prevent unauthorized access to endpoints. We do it by implementing <strong>WebFilter <\/strong>interface and applying it in <strong>SecurityWebFilterChain<\/strong>. As I said earlier Spring cloud gateway is based on Webflux, whereas two other services ( Authentication service and Product service) are Servlet based application, so dealing with filters is a little bit different in these applications.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">@RequiredArgsConstructor\npublic class JwtTokenAuthenticationFilter implements WebFilter {\n\n    public static final String <em>HEADER_PREFIX <\/em>= \"Bearer \";\n\n    private final JwtTokenProvider tokenProvider;\n\n    @Override\n    public Mono&lt;Void&gt; filter(ServerWebExchange exchange, WebFilterChain chain) {\n        String token = resolveToken(exchange.getRequest());\n        if (StringUtils.<em>hasText<\/em>(token) &amp;&amp; this.tokenProvider.validateToken(token)) {\n            Authentication authentication = this.tokenProvider.getAuthentication(token);\n            return chain.filter(exchange)\n                    .contextWrite(ReactiveSecurityContextHolder.<em>withAuthentication<\/em>(authentication));\n        }\n        return chain.filter(exchange);\n    }\n\n    private String resolveToken(ServerHttpRequest request) {\n        String bearerToken = request.getHeaders().getFirst(HttpHeaders.<em>AUTHORIZATION<\/em>);\n        if (StringUtils.<em>hasText<\/em>(bearerToken) &amp;&amp; bearerToken.startsWith(<em>HEADER_PREFIX<\/em>)) {\n            return bearerToken.substring(7);\n        }\n        return null;\n    }\n\n}\n<\/code><\/pre>\n\n\n\n<p>In the filter method declaration, first it checks existence of jwt token in requests, if the token exists then it authenticates the request based on the provided token and in case of successful authentication it retrieves the user information from the token using <strong>JwtTokenProvider<\/strong>.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Now, we configure the  <strong>SecurityWebFilterChain  <\/strong>to use the  <strong>JwtTokenAuthenticationFilter <\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">@Slf4j\n@Configuration\npublic class SecurityConfig {\n\n    @SuppressWarnings(\"SpringJavaInjectionPointsAutowiringInspection\")\n    @Bean\n    SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,\n                                                JwtTokenProvider tokenProvider) {\n\n        return http.csrf(ServerHttpSecurity.CsrfSpec::disable)\n                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)\n                .securityContextRepository(NoOpServerSecurityContextRepository.<em>getInstance<\/em>())\n                .authorizeExchange(it -&gt; it\n                        .pathMatchers(\"\/auth\/login\",\"\/auth\/refresh\",\"\/guest\").permitAll()\n                        .pathMatchers(\"\/products\/**\").hasAnyRole(\"USER\",\"ADMIN\")\n                        .anyExchange().authenticated()\n                )\n                .addFilterAt(new JwtTokenAuthenticationFilter(tokenProvider), SecurityWebFiltersOrder.<em>HTTP_BASIC<\/em>)\n                .build();\n    }\n\n\n\n\n\n}\n<\/code><\/pre>\n\n\n\n<p>As our application is stateless, we use <strong>NoOpServerSecurityContextRepository <\/strong>in HTTP security configuration. Using <strong>authorizeExchange <\/strong>we define application rules for accessing back-end endpoints. Then we add the <strong>JwtTokenAuthenticationFilter <\/strong>to the filter chain using <strong>addFilterAt<\/strong>. It accepts two arguments, the filter we want to add and the filter class that we want our custom filter place at same order as it is. For the first argument we pass our custom filter <strong>JwtTokenAuthenticationFilter<\/strong>, and for the second argument we pass  <strong>SecurityWebFiltersOrder.<em>HTTP_BASIC<\/em><\/strong>. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Securing Back-end Service<\/strong> (Product Service)<\/h2>\n\n\n\n<p>A secure back-end service must guarantee that the API it expose will be secure by making it visible only to the users and other services that are authorized to consume it.&nbsp;Every back-end service should be able to determine the user information including its roles, so in Api-gateway we need to forward the JWT token of the requests to back-end service to make it possible for back-end services to validate and authenticate the requests. <\/p>\n\n\n\n<p>Now, let&#8217;s create the <strong>JwtTokenAuthenticationFilter<\/strong> for product service. Although its implementation is similar to <strong>JwtTokenAuthenticationFilter<\/strong> that we created for API-gateway, there are some differences, because it&#8217;s a servlet based web application and spring cloud gateway works on <strong>WebFlux<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">@RequiredArgsConstructor\n@Component\npublic class JwtTokenAuthenticationFilter extends OncePerRequestFilter {\n\n    public static final String <em>HEADER_PREFIX <\/em>= \"Bearer \";\n\n    private final JwtTokenProvider tokenProvider;\n\n\n    private String resolveToken(HttpServletRequest request) {\n        String bearerToken = request.getHeader(HttpHeaders.<em>AUTHORIZATION<\/em>);\n        if (StringUtils.<em>hasText<\/em>(bearerToken) &amp;&amp; bearerToken.startsWith(<em>HEADER_PREFIX<\/em>)) {\n            return bearerToken.substring(7);\n        }\n        return null;\n    }\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {\n\n        String token = resolveToken(request);\n        if (StringUtils.<em>hasText<\/em>(token) &amp;&amp; this.tokenProvider.validateToken(token)) {\n\n            Authentication authentication = tokenProvider.getAuthentication(token);\n\n            UsernamePasswordAuthenticationToken\n                    usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(\n                    authentication.getPrincipal(), null,\n                    authentication.getPrincipal() == null ?\n                            Arrays.<em>asList<\/em>() : authentication.getAuthorities()\n            );\n\n            usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));\n            SecurityContextHolder.<em>getContext<\/em>().setAuthentication(usernamePasswordAuthenticationToken);\n            chain.doFilter(request, response);\n            return;\n        }\n\n        chain.doFilter(request, response);\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Now, we should apply the filter we have just created to the request in the filter chain in a configuration class :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">@EnableWebSecurity\n@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)\n@RequiredArgsConstructor\npublic class SecurityConfig {\n\n  private JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter;\n\n  @Autowired\n  public SecurityConfig(JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter) {\n    this.jwtTokenAuthenticationFilter = jwtTokenAuthenticationFilter;\n  }\n\n\n\n  @Bean\n  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n    \/\/ Enable CORS and disable CSRF\n    http.cors().and().csrf().disable();\n\n    \/\/ Set session management to stateless\n    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.<em>STATELESS<\/em>);\n\n    \/\/ Set unauthorized requests exception handler\n    http.exceptionHandling(\n        (exceptions) -&gt;\n            exceptions\n                .authenticationEntryPoint((req, rsp, e) -&gt; rsp.sendError(HttpServletResponse.<em>SC_UNAUTHORIZED<\/em>))\n                .accessDeniedHandler((req, rsp, e) -&gt; rsp.sendError(HttpServletResponse.<em>SC_FORBIDDEN<\/em>)));\n\n    \/\/ Set permissions on endpoints\n    http.authorizeRequests()\n\n        .antMatchers(\"\/products\/public\" )\n        .permitAll()\n        \/\/ Our private endpoints\n        .anyRequest()\n        .authenticated();\n        \n    \n    http.addFilterBefore(jwtTokenAuthenticationFilter,\n            UsernamePasswordAuthenticationFilter.class);\n\n    return http.build();\n  }\n\n\n  \/\/ Used by spring security if CORS is enabled.\n  @Bean\n  public CorsFilter corsFilter() {\n    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n    CorsConfiguration config = new CorsConfiguration();\n    config.setAllowCredentials(true);\n    config.addAllowedOrigin(\"*\");\n    config.addAllowedHeader(\"*\");\n    config.addAllowedMethod(\"*\");\n    source.registerCorsConfiguration(\"\/**\", config);\n    return new CorsFilter(source);\n  }\n\n\n}\n<\/code><\/pre>\n\n\n\n<p>As you can see, I added our custom filter to the filter chain and also I set restriction on wccessing endpoints.<\/p>\n\n\n\n<p>In the controller, we define our endpoint and set the roles that are allowed to use them :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">package mst.example.productservice.controllers;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"\/products\")\npublic class ProductController {\n\n    @GetMapping(\"public\")\n    public String publicProducts(){\n\n        return \"Hello all! It's a public endpoint. Every user can reach me.\";\n    }\n\n\n    @GetMapping(\"user\")\n    @PreAuthorize(\"hasRole('USER')\")\n    public String userProducts(){\n\n        return \"Hello dear User! This endpoint is available to USERs only.\";\n    }\n\n\n    @GetMapping(\"admin\")\n    @PreAuthorize(\"hasRole('USER')\")\n    public String adminProducts(){\n\n        return \"Hello dear Admin! This endpoint is available to ADMINs only.\";\n    }\n\n\n    @GetMapping(\"productList\")\n    @PreAuthorize(\"hasAnyRole('USER','ADMIN')\")\n    public String ProductList(){\n\n        return \"Products List here!\";\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>As you can see, I used @PreAuthorize annotation to restrict accessing the endpoint.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Authentication Server<\/strong><\/h2>\n\n\n\n<p>Finally, we need an Authentication service to check provided credential with the user database, generate jwt token and send it to client.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">@EnableWebSecurity\n@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)\n@RequiredArgsConstructor\npublic class SecurityConfig {\n\n  JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter;\n\n  @Autowired\n  public SecurityConfig(JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter) {\n    this.jwtTokenAuthenticationFilter = jwtTokenAuthenticationFilter;\n  }\n\n  \n\n  @Bean\n  public AuthenticationManager authenticationManager(\n          HttpSecurity http) throws Exception {\n    PasswordEncoder encoder = PasswordEncoderFactories.<em>createDelegatingPasswordEncoder<\/em>();\n    return http.getSharedObject(AuthenticationManagerBuilder.class)\n            .inMemoryAuthentication()\n            .withUser(\"admin\").password(encoder.encode(\"admin\")).roles(\"ADMIN\", \"USER\").and()\n            .withUser(\"mosy\").password(encoder.encode(\"1234\")).roles(\"USER\")\n            .and().and().parentAuthenticationManager(null).build();\n  }\n\n\n  @Bean\n  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n    \/\/ Enable CORS and disable CSRF\n    http.cors().and().csrf().disable();\n\n    \/\/ Set session management to stateless\n    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.<em>STATELESS<\/em>);\n\n    \/\/ Set unauthorized requests exception handler\n    http.exceptionHandling(\n        (exceptions) -&gt;\n            exceptions\n\n                .authenticationEntryPoint((req, rsp, e) -&gt; rsp.sendError(HttpServletResponse.<em>SC_UNAUTHORIZED<\/em>))\n                .accessDeniedHandler((req, rsp, e) -&gt; rsp.sendError(HttpServletResponse.<em>SC_FORBIDDEN<\/em>)));\n\n    \/\/ Set permissions on endpoints\n    http.authorizeRequests()\n        \n        .antMatchers(\"\/auth\/**\" )\n        .permitAll()\n        \/\/ Our private endpoints\n        .anyRequest()\n        .authenticated();\n\n    http.addFilterBefore(jwtTokenAuthenticationFilter,\n            UsernamePasswordAuthenticationFilter.class);\n\n    return http.build();\n  }\n\n\n  \/\/ Used by spring security if CORS is enabled.\n  @Bean\n  public CorsFilter corsFilter() {\n    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n    CorsConfiguration config = new CorsConfiguration();\n    config.setAllowCredentials(true);\n    config.addAllowedOrigin(\"*\");\n    config.addAllowedHeader(\"*\");\n    config.addAllowedMethod(\"*\");\n    source.registerCorsConfiguration(\"\/**\", config);\n    return new CorsFilter(source);\n  }\n\n}<\/code><\/pre>\n\n\n\n<p>For simplicity I used in-memory authentication, and defined two users with different roles. <\/p>\n\n\n\n<p>In the AuthController we need to define two endpoints,<strong> \/login <\/strong>and <strong>\/refresh <\/strong>endpoint :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java\">\n@RestController\n@RequestMapping(\"\/auth\")\n@RequiredArgsConstructor\n@Slf4j\npublic class AuthController {\n\n\n    private final JwtTokenProvider tokenProvider;\n    private final AuthenticationManager authenticationManager;\n\n    @PostMapping(\"\/login\")\n    public ResponseEntity&lt;AuthenticationResponse&gt; login(@Valid @RequestBody AuthenticationRequest authRequest) throws AuthenticationException {\n\n\n        Authentication authentication = this.authenticationManager\n                .authenticate(new UsernamePasswordAuthenticationToken(\n                                                                        authRequest.getUsername(),\n                                                                        authRequest.getPassword()\n                                                                      ));\n\n      \n\n        String accessToken = this.tokenProvider.createToken(authentication, \"access\");\n        String refreshToken = this.tokenProvider.createToken(authentication, \"refresh\");\n\n        HttpHeaders httpHeaders = new HttpHeaders();\n        httpHeaders.add(HttpHeaders.<em>AUTHORIZATION<\/em>, \"Bearer \" + accessToken);\n        AuthenticationResponse authenticationResponse = new AuthenticationResponse(accessToken,refreshToken);\n        return new ResponseEntity&lt;&gt;(authenticationResponse, httpHeaders, HttpStatus.<em>OK<\/em>);\n\n\n    }\n\n\n\n    @PostMapping(\"\/refresh\")\n    public ResponseEntity&lt;AuthenticationResponse&gt; refreshToken(@Valid @RequestBody RefreshTokenRequest refreshTokenRequest) {\n\n            if(!tokenProvider.validateToken(refreshTokenRequest.getRefreshToken()))\n                throw new AccessDeniedException(\"Access denied\");\n\n\n        String accessToken = this.tokenProvider.createToken(refreshTokenRequest.getRefreshToken(), \"access\");\n        String refreshToken = this.tokenProvider.createToken(refreshTokenRequest.getRefreshToken(), \"refresh\");\n\n        HttpHeaders httpHeaders = new HttpHeaders();\n        httpHeaders.add(HttpHeaders.<em>AUTHORIZATION<\/em>, \"Bearer \" + accessToken);\n        AuthenticationResponse authenticationResponse = new AuthenticationResponse(accessToken,refreshToken);\n        return new ResponseEntity&lt;&gt;(authenticationResponse, httpHeaders, HttpStatus.<em>OK<\/em>);\n\n    }\n\n    }\n<\/code><\/pre>\n\n\n\n<p>The <strong>\/login<\/strong> endpoint verifies credential, if the user exist and the password was correct it generates jwt token &amp; refresh token and send it to client. The <strong>\/referesh<\/strong> endpoint, accepts the refresh token, validate it, and re-generate jwt and its refresh token again and send it back to client.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Running The Project<\/strong><\/h2>\n\n\n\n<p>To run three services of the project, we should create a docker-compose.yml file and define services and configure their parameters :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"yaml\" class=\"language-yaml\">version: '2.1'\n\nservices:\n\n  redis:\n    image: 'bitnami\/redis:latest'\n    hostname: redis\n    environment:\n      - ALLOW_EMPTY_PASSWORD=yes\n    expose:\n      - \"6379\"\n  <em>#service 1: Authentication service\n  <\/em>authService:\n    image: auth-service\n    container_name: auth-service\n    hostname: auth-service\n    build :\n      context : .\/AuthServer\n      dockerfile: Dockerfile\n    expose:\n      - \"8080\"\n    environment:\n      jwt_secret : ${JWT_KEY}\n    restart: always\n\n  <em>#service 2: Product service\n  <\/em>productService:\n    image: product-service\n    container_name: product-service\n    hostname: product-service\n    build:\n      context: .\/productService\n      dockerfile: Dockerfile\n    expose:\n      - \"8084\"\n    environment:\n      jwt_secret : ${JWT_KEY}\n    restart: always\n  <em>#service 3: API Gateway\n  <\/em>apiGateway:\n    image: apigateway\n    container_name: apigateway\n    hostname: apigateway\n    build :\n      context : .\/ApiGateWay\n      dockerfile: Dockerfile\n    ports:\n      - '8010:8010'\n    environment:\n         jwt_secret : ${JWT_KEY}\n    restart: always\n    depends_on:\n      - authService\n      - productService\n      - redis\n\n<\/code><\/pre>\n\n\n\n<p>In the above docker compose file, we have defined three services + redis service. And as I said earlier, I stored jwt secret key in <strong>.env <\/strong>file as <strong>JWT_KEY <\/strong>beside <strong>docker-compose.yml <\/strong>file, and I read it in the <strong> docker-compose.yml<\/strong> to set  <strong>jwt_secret  <\/strong>environment variable.<\/p>\n\n\n\n<p>Excuting below command in project root will run our tiny microservice project:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">docker-compose up<\/code><\/pre>\n\n\n\n<p> I use postman to test the project, however you can use any other tools such as curl. I call localhost:8010\/auth\/login with the below payload to get the jwt token :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">{\n&nbsp;&nbsp;&nbsp;&nbsp;\"username\"&nbsp;:&nbsp;\"mosy\",\n&nbsp;&nbsp;&nbsp;&nbsp;\"password\"&nbsp;:&nbsp;\"1234\"\n}<\/code><\/pre>\n\n\n\n<p>The api gateway routes the request to Authentication service and the response would be a json message containing the jwt token and refresh token :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">{\n&nbsp;&nbsp;&nbsp;&nbsp;\"accessToken\":&nbsp;\"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJtb3N5Iiwicm9sZXMiOiJST0xFX1VTRVIiLCJpYXQiOjE2NzM2NDE3MzAsImV4cCI6MTY3MzY0NTMzMH0.Tve7jOR8vLO-riqY4g2rybb87mq2j_G8MhSg9WZFUzk\",\n&nbsp;&nbsp;&nbsp;&nbsp;\"refreshToken\":&nbsp;\"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJtb3N5Iiwicm9sZXMiOiJST0xFX1VTRVIiLCJpYXQiOjE2NzM2NDE3MzAsImV4cCI6MTY3MzY1OTczMH0._JuZBk48fZFKL36qqvIhuVIlJvD-D7KtRaD6whg8Ubc\"\n}<\/code><\/pre>\n\n\n\n<p>Now, having jwt token we can call other endpoints which need client to be authenticated. Here, I call localhost:8010\/api\/products\/user endpoint which only users with USER role can access it, as you can see below, we get respond with 200 status code.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/m-shaeri.ir\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"645\" src=\"https:\/\/m-shaeri.ir\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway-1024x645.jpg\" alt=\"securing and rate limiting spring cloud gateway\" class=\"wp-image-1207\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway-1024x645.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway-300x189.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway-768x484.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway.jpg 1355w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Spring Cloud Gateway routed the request and sent back the backend response to the client<\/figcaption><\/figure>\n\n\n\n<p>The interesting part of the work comes here, when you send request consecutively within a second, you will get <span class=\"has-inline-color has-vivid-red-color\">429 status Too Many Requests<\/span> error. Because, in our configuration we set  <span class=\"has-inline-color has-vivid-cyan-blue-color\"><strong>replenishRate<\/strong><\/span>  = 1, which means only one request will be proccessed within a second, <strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">burstCapacity<\/span><\/strong>  = 2 to accept maximum 2 request per second, and <strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">requestedTokens<\/span><\/strong> =2 which tells the rate limiter that calling these APIs cost 2 requests.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/m-shaeri.ir\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway_too_many_requests.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"648\" src=\"https:\/\/m-shaeri.ir\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway_too_many_requests-1024x648.jpg\" alt=\"\" class=\"wp-image-1208\" srcset=\"https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway_too_many_requests-1024x648.jpg 1024w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway_too_many_requests-300x190.jpg 300w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway_too_many_requests-768x486.jpg 768w, https:\/\/mshaeri.com\/blog\/wp-content\/uploads\/2023\/01\/rate_limiting_spring_cloud_gateway_too_many_requests.jpg 1359w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption>Api rate limiting sends <span class=\"has-inline-color has-vivid-red-color\">429 status Too Many Requests<\/span> error to client<\/figcaption><\/figure>\n\n\n\n<p>Obviuosly, we can put rate limiting parameters&#8217; values in in the .env file to prevent source code modification each time we need to set new rate limiting policies.<\/p>\n\n\n\n<p>Thanks for reading the post!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>API gateway is an infrastructure layer that places in front of application to route requests from the client side to the appropriate service. Spring Cloud &hellip; <\/p>\n","protected":false},"author":1,"featured_media":1228,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[178,141,144,1,69,94,177,35,179,143,70,176],"tags":[168,169,9,67,92,170,172,174,84,130,63,71,175],"_links":{"self":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts\/1166"}],"collection":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/comments?post=1166"}],"version-history":[{"count":0,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/posts\/1166\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/media\/1228"}],"wp:attachment":[{"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/media?parent=1166"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/categories?post=1166"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mshaeri.com\/blog\/wp-json\/wp\/v2\/tags?post=1166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}