Spring Security OAuth2 Authorization Code Implementation
OAuth Cliant Configuration
The oauth_client_details table stores cleint configurations with these key fields:
client_id: Unique client identifier
client_secret: Encrypted client password
authorized_grant_types: Supported grant types (comma-separated)
scope: Client permission scope
web_server_redirect_uri: Authorization callback URL
autoapprove: Automatic authorization flag
Default Token Ednpoints
/oauth/authorize: Authorization code endpoint
/oauth/token: Token issuance endpoint
/oauth/check_token: Token validation endpoint
/oauth/token_key: Public key endpoint (JWT)
Authorization Code Flow
- Obtain authorization code: GET /oauth/authorize?client_id=CLIENT&response_type=code
- User authentication and consent
- Redirect with code parameter
- Exchange code for token: POST /oauth/token with Basic auth and parameters: ```
grant_type=authorization_code
code=RECEIVED_CODE
Security Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.userDetailsService(userService);
}
@Bean
@Override
public AuthenticationManager authManager() throws Exception {
return super.authenticationManagerBean();
}
}
Authorization Server
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Bean
public ClientDetailsService clientService() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) {
clients.withClientDetails(clientService());
}
@Autowired
private AuthenticationManager authManager;
@Autowired
private TokenStore tokenStorage;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authManager)
.tokenStore(tokenStorage)
.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource));
}
}
Token Storage
@Configuration
public class TokenConfiguration {
@Autowired
private DataSource dataSource;
@Bean
public TokenStore tokenStorage() {
return new JdbcTokenStore(dataSource);
}
}
Password Handling
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
User Details Service
@Service
public class CustomUserService implements UserDetailsService {
@Autowired
private UserClient userClient;
@Override
public UserDetails loadUserByUsername(String username) {
User user = userClient.findByUsername(username);
List<GrantedAuthority> permissions = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getCode()))
.collect(Collectors.toList());
return new AuthUser(
user.getId(),
user.getUsername(),
user.getPassword(),
permissions
);
}
}
User Details Implementation
public class AuthUser implements UserDetails {
private String userId;
private String username;
private String password;
private List<GrantedAuthority> authorities;
public AuthUser(String userId, String username, String password,
List<GrantedAuthority> authorities) {
this.userId = userId;
this.username = username;
this.password = password;
this.authorities = authorities;
}
// Implement UserDetails methods
}
Resource Server Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
@Autowired
private TokenStore tokenStorage;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId("system-resources")
.tokenStore(tokenStorage);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated();
}
}