Implementing a Spring Cloud Gateway with Rate Limiting, BCrypt Encryption, and JWT Authentication
This guide demonstrates how to build a Spring Cloud Gateway for microservices, covering system setup, CORS handling, rate‑limiting with the token‑bucket algorithm, password hashing with BCrypt, and secure JWT‑based authentication, complete with Maven dependencies, configuration files, and filter implementations.
Objectives
Master the construction of a microservice gateway, implement gateway rate limiting, use BCrypt for password encryption and verification, understand encryption algorithms, and apply JWT for microservice authentication.
1. Microservice Gateway Overview
Direct client calls to multiple microservices cause complexity, cross‑origin issues, duplicated authentication, and difficulty in refactoring. A gateway solves these problems by acting as a middle layer that handles security, performance, and monitoring.
Typical architecture:
Advantages include security (services hidden behind the gateway), easy monitoring, centralized authentication, reduced client‑service interactions, and unified authorization.
1.1 Gateway Technologies
nginx – high‑performance HTTP and reverse proxy server.
Zuul – Netflix JVM‑based router and load balancer.
Spring Cloud Gateway – Spring‑based gateway with circuit breaker, path rewrite, and better performance than Zuul.
1.2 Building the Gateway Service
Create a Maven module changgou_gateway_system with the following dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>Define the main class:
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}Add application.yml with routes, CORS configuration, and server settings:
spring:
application:
name: sysgateway
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
routes:
- id: goods
uri: lb://goods
predicates:
- Path=/goods/**
filters:
- StripPrefix=1
- id: system
uri: lb://system
predicates:
- Path=/system/**
filters:
- StripPrefix=1
server:
port: 9101
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka
instance:
prefer-ip-address: true1.3 CORS Configuration
Extend application.yml with the globalcors section shown above to allow all origins and HTTP methods.
1.4 Gateway Filters
Two example filters demonstrate request logging and IP/URL inspection.
@Component
public class IpFilter implements GlobalFilter, Ordered {
@Override
public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("经过第1个过滤器IpFilter");
InetSocketAddress remote = exchange.getRequest().getRemoteAddress();
System.out.println("ip:" + remote.getHostName());
return chain.filter(exchange);
}
@Override
public int getOrder() { return 1; }
}
@Component
public class UrlFilter implements GlobalFilter, Ordered {
@Override
public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("经过第2个过滤器UrlFilter");
String url = exchange.getRequest().getURI().getPath();
System.out.println("url:" + url);
return chain.filter(exchange);
}
@Override
public int getOrder() { return 2; }
}2. Gateway Rate Limiting
Implement per‑IP rate limiting (1 request per second) using Spring Cloud Gateway's built‑in RequestRateLimiter backed by Redis.
2.1 Token Bucket Algorithm
The token bucket controls request flow by adding tokens at a fixed rate, limiting the bucket size, and requiring a token for each request.
2.2 Configuration
Add Redis dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>Define a KeyResolver bean to use the client IP as the rate‑limit key:
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}Update application.yml to include the RequestRateLimiter filter:
filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@ipKeyResolver}"
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1When the limit is exceeded, the gateway returns HTTP 429.
3. BCrypt Password Encryption
BCrypt provides a strong, salted hash for storing passwords.
3.1 Quick Start
String gensalt = BCrypt.gensalt();
String hash = BCrypt.hashpw("123456", gensalt);
System.out.println(hash);
boolean match = BCrypt.checkpw("123456", hash);
System.out.println(match);3.2 Integrating into Admin Service
Encrypt passwords before persisting:
public void add(Admin admin) {
String password = BCrypt.hashpw(admin.getPassword(), BCrypt.gensalt());
admin.setPassword(password);
adminMapper.insert(admin);
}Validate login credentials:
public boolean login(Admin admin) {
Admin stored = adminMapper.selectOneByLoginName(admin.getLoginName());
if (stored == null) return false;
return BCrypt.checkpw(admin.getPassword(), stored.getPassword());
}4. Encryption Algorithms Overview
Brief comparison of reversible (symmetric, asymmetric) and irreversible (hash) algorithms, including AES, RSA, MD5, SHA, and Base64 encoding.
5. JWT for Microservice Authentication
JWT enables stateless, token‑based authentication across microservices.
5.1 Token Structure
Header (algorithm, type), Payload (claims such as iss, sub, exp, etc.), and Signature (header + payload signed with a secret).
5.2 Creating JWT with JJWT
Add JJWT dependency:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>Utility class to generate tokens:
public class JwtUtil {
public static final Long JWT_TTL = 3600000L; // 1 hour
public static final String JWT_KEY = "itcast";
public static String createJWT(String id, String subject, Long ttlMillis) {
SignatureAlgorithm alg = SignatureAlgorithm.HS256;
long now = System.currentTimeMillis();
Date exp = new Date(now + (ttlMillis != null ? ttlMillis : JWT_TTL));
SecretKey key = generalKey();
return Jwts.builder()
.setId(id)
.setSubject(subject)
.setIssuer("admin")
.setIssuedAt(new Date(now))
.setExpiration(exp)
.signWith(alg, key)
.compact();
}
private static SecretKey generalKey() {
byte[] encoded = Base64.getDecoder().decode(JWT_KEY);
return new SecretKeySpec(encoded, 0, encoded.length, "AES");
}
}Login endpoint returns a token on successful authentication:
@PostMapping("/login")
public Result login(@RequestBody Admin admin) {
if (adminService.login(admin)) {
Map
info = new HashMap<>();
info.put("username", admin.getLoginName());
String token = JwtUtil.createJWT(UUID.randomUUID().toString(), admin.getLoginName(), null);
info.put("token", token);
return new Result(true, StatusCode.OK, "登录成功", info);
}
return new Result(false, StatusCode.LOGINERROR, "用户名或密码错误");
}5.3 Gateway JWT Validation Filter
Add JJWT dependency to the gateway and create a utility for parsing tokens:
public class JwtUtil {
public static final String JWT_KEY = "itcast";
public static SecretKey generalKey() {
byte[] encoded = Base64.getDecoder().decode(JWT_KEY);
return new SecretKeySpec(encoded, 0, encoded.length, "AES");
}
public static Claims parseJWT(String jwt) throws Exception {
return Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(jwt).getBody();
}
}Global filter that checks the token header, allows the login path, and returns HTTP 401 for missing or invalid tokens:
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
private static final String AUTHORIZE_TOKEN = "token";
@Override
public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
if (request.getURI().getPath().contains("/admin/login")) {
return chain.filter(exchange);
}
String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN);
if (StringUtils.isEmpty(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
try {
JwtUtil.parseJWT(token);
} catch (Exception e) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() { return 0; }
}Testing with Postman shows that requests without a token receive 401, while requests with a valid JWT return the expected data.
Source: blog.csdn.net/qq_36079912/article/details/104831199
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.