Implementing OAuth 2.0 with Spring Security: A Practical Guide
•10 min read•Yaswanth Reddy Koduru
OAuth 2.0Spring SecurityJWTAuthenticationSecurity
Implementing OAuth 2.0 with Spring Security: A Practical Guide
Security isn't optional in enterprise systems. At WageNest and ChildEra, I implemented comprehensive OAuth 2.0 authentication with Spring Security. Here's what I learned.
Why OAuth 2.0?
Traditional session-based auth doesn't work well for:
- Microservices architectures
- Mobile applications
- Third-party integrations
- Stateless APIs
OAuth 2.0 with JWT tokens solves these problems.
Architecture Overview
Components:
- Authorization Server - Issues tokens
- Resource Servers - Your microservices
- Client Applications - React frontend, mobile apps
- Token Store - Redis for token validation
Implementation Steps
Step 1: Authorization Server Setup
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("payroll-web-app")
.secret(passwordEncoder().encode("secret"))
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("read", "write")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(86400);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
}
Step 2: JWT Token Configuration
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("your-secret-key"); // Use proper key management in production
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
Step 3: Resource Server Security
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/**").authenticated()
.and()
.csrf().disable();
}
}
Step 4: Method-Level Security
@RestController
@RequestMapping("/api/payroll")
public class PayrollController {
@PreAuthorize("hasRole('PAYROLL_ADMIN')")
@PostMapping("/process")
public ResponseEntity<PayrollResult> processPayroll(@RequestBody PayrollRequest request) {
// Only users with PAYROLL_ADMIN role can access
return ResponseEntity.ok(payrollService.process(request));
}
@PreAuthorize("hasPermission(#employeeId, 'Employee', 'READ')")
@GetMapping("/employee/{employeeId}")
public ResponseEntity<Employee> getEmployee(@PathVariable Long employeeId) {
// Custom permission evaluator checks if user can access this employee
return ResponseEntity.ok(employeeService.getEmployee(employeeId));
}
}
Role-Based Access Control (RBAC)
Implemented granular permissions:
Roles:
- SUPER_ADMIN - Full system access
- TENANT_ADMIN - Tenant-level admin
- PAYROLL_MANAGER - Can process payroll
- EMPLOYEE - Self-service access
Custom Permission Evaluator:
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication auth, Object targetId, Object targetType, Object permission) {
if (targetType.equals("Employee")) {
// Check if user can access this employee
UserDetails user = (UserDetails) auth.getPrincipal();
return employeeAccessService.canAccess(user, (Long) targetId);
}
return false;
}
}
Security Best Practices
1. Token Expiration
- Access tokens: 1 hour
- Refresh tokens: 24 hours
- Rotate refresh tokens on use
2. HTTPS Everywhere
All token exchanges over HTTPS only
3. Token Storage
- Never store tokens in localStorage
- Use httpOnly cookies or secure storage
- Clear tokens on logout
4. Rate Limiting
Prevent brute force attacks:
@Bean
public RateLimiter rateLimiter() {
return RateLimiter.create(10.0); // 10 requests per second
}
Results
After implementing OAuth 2.0:
- Security incidents: Reduced to zero
- Unauthorized access attempts: Blocked 100%
- Token-based auth: Seamless across mobile and web
- Audit compliance: Full request traceability
Common Pitfalls
- Exposing tokens in URLs - Use headers instead
- Long-lived tokens - Shorter is better
- Missing CSRF protection - Still needed for cookies
- Poor error messages - Don't leak security details
- Not validating redirect URIs - Security risk
Need help securing your Spring Boot microservices? Let's talk security architecture.