You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何扩展OAuth2 Principal?双场景OAuth2应用的身份集成需求

Got it, let's walk through how to build your OAuth 2.0 application with both required flows and create that unified Principal object to merge Keycloak and local user data. Here's a step-by-step breakdown tailored to your needs:

1. Setting Up the Two OAuth 2.0 Flows

Client Credentials Flow for Microservice Access

This flow is straightforward since it's for service-to-service communication (no user context involved):

  • Register your application as a client in Keycloak, enabling the client_credentials grant type.
  • In your app, configure an OAuth2 client that uses your client ID and secret to request access tokens from Keycloak's token endpoint.
  • Use this token to authenticate requests to your backend microservices—most frameworks (like Spring Security) handle token validation automatically once configured.

Authorization Code Flow with Keycloak User Authentication

For user-facing authentication, follow these steps (aligned with Keycloak's standard patterns):

  1. Keycloak Client Setup: Create a client in Keycloak, set the correct redirect URI for your app, and enable the authorization_code grant type. Make sure to turn on "Standard Flow Enabled" in the client settings.
  2. App Configuration: In your application, configure OAuth2 login to point to Keycloak's authorization, token, and user info endpoints. When users access protected resources, they'll be redirected to Keycloak's login page automatically.
  3. Token Handling: After successful login, Keycloak returns an ID token and access token. The ID token contains basic user attributes like preferred_username (your login name) and email—these are the bits you'll pull into your Principal.
2. Building a Unified Principal Object

The goal here is to merge Keycloak's user attributes with your local user table data into a single, usable Principal. Here's how to do it:

Step 1: Extract Keycloak User Data Post-Authentication

Once a user logs in via Keycloak, your framework's authentication object (like Spring Security's OAuth2User) will hold the attributes from Keycloak. For example, you can pull the login name with:

String username = oAuth2User.getAttribute("preferred_username");

Other common attributes include sub (unique user ID in Keycloak) and email.

Step 2: Fetch Local User Data

Use the Keycloak-derived identifier (like the username or sub value) to query your local user table. Retrieve any additional data you store there—such as internal user IDs, custom roles, or profile details.

Step 3: Create a Custom Principal Class

Define a class that implements the Principal interface (or integrates with your framework's user type, like OAuth2User) to hold both sets of data. Example:

public class CustomUserPrincipal implements Principal {
    // Keycloak-derived fields
    private String keycloakUsername;
    private String email;
    private String keycloakUserId; // The 'sub' attribute
    
    // Local user table fields
    private Long localUserId;
    private String localUserRole;
    private String customProfileField;

    // Constructor, getters, and setters
    @Override
    public String getName() {
        return keycloakUsername; // Or use localUserId if that's your primary identifier
    }
}

Step 4: Populate the Custom Principal

Use a custom user service (like Spring Security's OAuth2UserService) to build and attach your CustomUserPrincipal to the authentication context. Example implementation:

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
    private final LocalUserRepository localUserRepo;

    public CustomOAuth2UserService(LocalUserRepository localUserRepo) {
        this.localUserRepo = localUserRepo;
    }

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        // Get Keycloak's user data
        OAuth2User keycloakUser = super.loadUser(userRequest);
        String username = keycloakUser.getAttribute("preferred_username");
        String keycloakUserId = keycloakUser.getAttribute("sub");

        // Fetch local user data
        LocalUser localUser = localUserRepo.findByKeycloakUserId(keycloakUserId)
                .orElseThrow(() -> new UsernameNotFoundException("User not found in local database"));

        // Build and return the custom principal
        return new CustomUserPrincipal(
                username,
                keycloakUser.getAttribute("email"),
                keycloakUserId,
                localUser.getId(),
                localUser.getRole(),
                localUser.getCustomProfileField()
        );
    }
}

Then, configure your app to use this custom service so that every authenticated user's context uses your unified Principal.

3. Key Things to Keep in Mind
  • Identity Matching: Pick a consistent, unique identifier (like Keycloak's sub field) to link Keycloak users to your local table—avoid using usernames if they can change.
  • Error Handling: Decide what to do if a Keycloak user doesn't exist in your local table (e.g., auto-provision a new local user, or redirect to a setup page).
  • Security: Don't store sensitive data in the Principal unless necessary, and ensure all local database queries are secure (avoid SQL injection risks).
  • Token Validation: Let your framework handle token validation (Spring Security does this out of the box with Keycloak) to ensure only valid tokens are accepted.

内容的提问来源于stack exchange,提问作者Michael Cornel

火山引擎 最新活动