How to Build an OAuth2 Security Framework in Spring Cloud Microservices
This article explains the fundamentals of OAuth2, its four grant types and roles, and provides step‑by‑step Spring Boot and Spring Cloud configurations—including authorization and resource server setup, custom token filters, and Zuul gateway integration—to secure microservice APIs.
OAuth2 (Open Authorization) is a standard that enables third‑party applications to obtain limited access to user resources without exposing user credentials; OAuth2.0 is the successor of OAuth1.0 and defines four grant types.
OAuth2 Grant Types
Authorization Code : Two‑step flow that returns an access token and a refresh token; the access token has a limited lifespan and can be refreshed using the refresh token.
Implicit : Simplified flow that obtains the token directly from the authorization server; easier but less secure.
Password : Uses the resource owner's username and password to obtain tokens; suitable for trusted first‑party applications.
Client Credentials : No user involvement; the client authenticates directly with the authorization server, typically used for service‑to‑service communication.
The OAuth2 model involves four roles: third‑party application (client), resource owner (user), authorization server, and resource server.
System Integration Overview
Environment:
JDK 8
Spring Boot 2.1.6.RELEASE
Spring Cloud Greenwich.SR2
Spring Cloud Starter OAuth2
Key points for the authorization server configuration:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private DataSource dataSource;
@Autowired
private MyUserDetailsService myUserDetailsService;
@Bean
public RedisTemplateTokenStore tokenStore() {
return new RedisTemplateTokenStore(redisTemplate);
}
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
@Primary
@Bean
public AuthorizationServerTokenServices defaultTokenServices() {
CustomTokenServices tokenServices = new CustomTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(false);
tokenServices.setClientDetailsService(clientDetails());
tokenServices.setAccessTokenValiditySeconds(60 * 30);
tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24);
return tokenServices;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.userDetailsService(myUserDetailsService)
.tokenGranter(tokenGranter())
.authenticationManager(authenticationManager);
endpoints.tokenServices(defaultTokenServices());
}
}The resource server is marked with @EnableResourceServer and shares similar configuration:
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "oauth_server_service";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/oauth/token", "/oauth/check_token", "/oauth/exit", "/oauth/renewal", "/actuator/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic();
}
}A custom filter PermitAuthenticationFilter removes the Authorization header for whitelisted URLs, allowing unauthenticated access where needed.
@Component
public class PermitAuthenticationFilter extends OncePerRequestFilter {
@Value("${security.oauth2.resource.permit-all-urls:/actuator,/actuator/**}")
private String permitAllUrls;
public static final String H_AUTHORIZATION = "Authorization";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String[] urlsArray = permitAllUrls.split(",");
for (String permitUrl : urlsArray) {
if (permitUrl.equals("/**") || permitUrl.equals(request.getRequestURI())) {
request = new HttpServletRequestWrapper(request) {
@Override
public String getHeader(String name) {
if (H_AUTHORIZATION.equalsIgnoreCase(name)) {
return null;
}
return super.getHeader(name);
}
};
}
}
filterChain.doFilter(request, response);
}
}For Zuul gateway integration, the security configuration disables CSRF, permits a whitelist of endpoints, and ensures the Authorization header is not stripped by setting sensitiveHeaders to empty.
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {"/configuration/ui","/configuration/security","/doc.html","/actuator/**","/webjars/**","/swagger-ui.html","/**"};
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
.and().httpBasic();
}
}Key Zuul properties:
zuul:
add-proxy-headers: true
retryable: true
sensitiveHeaders:
routes:
basic-workflow: /workflow-service/**
basic-system: /userinfo-service/**
...
security:
oauth2:
client:
access-token-uri: http://localhost:8000/auth-service/oauth/token
user-authorization-uri: http://localhost:8000/auth-service/oauth/authorize
client-id: webapp
resource:
user-info-uri: http://localhost:8000/auth-service/user
prefer-token-info: falseArchitect's Alchemy Furnace
A comprehensive platform that combines Java development and architecture design, guaranteeing 100% original content. We explore the essence and philosophy of architecture and provide professional technical articles for aspiring architects.
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.
