Microservices Security¶
Overview¶
This guide covers security patterns, best practices, and implementation approaches for securing microservices architecture.
Prerequisites¶
- Basic understanding of microservices architecture
- Knowledge of Spring Security
- Familiarity with OAuth2 and JWT
- Understanding of SSL/TLS
Learning Objectives¶
- Understand microservices security patterns
- Learn OAuth2 implementation
- Master JWT authentication
- Implement service-to-service security
- Handle security at API Gateway level
Table of Contents¶
- Authentication and Authorization
- OAuth2 Implementation
- JWT Security
- Service-to-Service Security
- API Gateway Security
Authentication and Authorization¶
Spring Security Configuration¶
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
}
User Details Service¶
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + username));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
getAuthorities(user.getRoles()));
}
}
OAuth2 Implementation¶
Authorization Server Configuration¶
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("client-id")
.secret(passwordEncoder().encode("client-secret"))
.authorizedGrantTypes("password", "refresh_token")
.scopes("read", "write")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(86400);
}
}
Resource Server Configuration¶
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.oauth2ResourceServer()
.jwt();
}
}
JWT Security¶
JWT Token Provider¶
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpiration;
public String generateToken(Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(new Date().getTime() + jwtExpiration))
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
JWT Authentication Filter¶
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
String username = tokenProvider.getUsernameFromJWT(jwt);
UserDetails userDetails = userDetailsService
.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext()
.setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
}
Service-to-Service Security¶
Mutual TLS Configuration¶
@Configuration
public class MutualTlsConfig {
@Bean
public RestTemplate restTemplate() throws Exception {
SSLContext sslContext = SSLContextBuilder
.create()
.loadTrustMaterial(trustStore.getFile())
.loadKeyMaterial(
keyStore.getFile(),
keyStorePassword.toCharArray(),
keyPassword.toCharArray()
)
.build();
HttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(client);
return new RestTemplate(requestFactory);
}
}
Client Certificate Authentication¶
@Configuration
public class ClientCertificateConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(userDetailsService());
}
}
API Gateway Security¶
Gateway Security Configuration¶
@Configuration
public class GatewaySecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(
ServerHttpSecurity http) {
http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/public/**").permitAll()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
}
Rate Limiting¶
@Configuration
public class RateLimitingConfig {
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest()
.getHeaders()
.getFirst("X-User-Id"));
}
@Bean
public RateLimiter rateLimiter() {
return new RedisRateLimiter(10, 20);
}
}
Best Practices¶
- Use HTTPS everywhere
- Implement proper authentication and authorization
- Use secure session management
- Implement rate limiting
- Use secure communication between services
- Regular security audits
- Implement proper logging and monitoring
Common Pitfalls¶
- Insecure communication between services
- Weak authentication mechanisms
- Missing rate limiting
- Poor secret management
- Insufficient logging
- Missing security headers
Implementation Examples¶
Complete Security Configuration¶
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.and()
.addFilterBefore(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.headers()
.frameOptions().deny()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
}
}
Security Headers Configuration¶
@Configuration
public class SecurityHeadersConfig {
@Bean
public WebFilter securityHeadersFilter() {
return (exchange, chain) -> {
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("X-XSS-Protection", "1; mode=block");
response.getHeaders().add("X-Frame-Options", "DENY");
response.getHeaders().add("X-Content-Type-Options", "nosniff");
response.getHeaders().add("Strict-Transport-Security",
"max-age=31536000; includeSubDomains");
return chain.filter(exchange);
};
}
}
Resources for Further Learning¶
Practice Exercises¶
- Implement OAuth2 authentication
- Set up JWT-based authentication
- Configure mutual TLS
- Implement rate limiting
- Set up security headers