You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义OAUTH_CLIENT_DETAILS表实现Spring REST OAuth2认证

Implementing Custom OAUTH_CLIENT_DETAILS Table for Your Spring OAuth2 Setup

Hey there! Since you already have a solid working custom authentication setup with UserDetails and your existing database tables, let's break down how to build your custom OAUTH_CLIENT_DETAILS table and integrate it seamlessly with your OAuth2 authorization server.

1. Design the Custom OAUTH_CLIENT_DETAILS Table Structure

Based on your request to mirror your user table plus add token-related fields, here's a SQL schema that combines OAuth2 client requirements with your existing table pattern:

CREATE TABLE OAUTH_CLIENT_DETAILS (
    client_id VARCHAR(255) PRIMARY KEY COMMENT '类似user表的user_id,作为客户端唯一标识',
    name VARCHAR(255) NOT NULL COMMENT '客户端名称',
    email VARCHAR(255) UNIQUE COMMENT '客户端联系邮箱',
    password VARCHAR(255) NOT NULL COMMENT '客户端密钥,哈希存储,和user表的password处理逻辑一致',
    active BOOLEAN DEFAULT TRUE COMMENT '客户端是否启用,对应user表的active',
    access_token VARCHAR(512) COMMENT '新增的token字段,存储当前有效的access token',
    refresh_token VARCHAR(512) COMMENT '新增的refresh token字段',
    access_token_validity_seconds INT DEFAULT 3600 COMMENT 'access token有效期(秒)',
    refresh_token_validity_seconds INT DEFAULT 86400 COMMENT 'refresh token有效期(秒)',
    scope VARCHAR(255) DEFAULT 'read,write' COMMENT '客户端权限范围',
    authorized_grant_types VARCHAR(255) DEFAULT 'password,refresh_token' COMMENT '允许的授权类型',
    web_server_redirect_uri VARCHAR(255) COMMENT '授权回调地址(如果使用授权码模式)',
    authorities VARCHAR(255) COMMENT '客户端拥有的权限(和你的自定义角色权限体系对应)'
);

Note: I added standard OAuth2 client fields alongside your requested token columns to ensure full compatibility with Spring Security OAuth2. Feel free to adjust default values, field lengths, or remove non-required fields to match your application's needs.

2. Create the Client Details Entity Class

Next, build a JPA entity that maps to this table and implements Spring's ClientDetails interface (extending BaseClientDetails will save you from writing boilerplate code):

import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import jakarta.persistence.*;
import java.util.Set;
import java.util.Arrays;

@Entity
@Table(name = "OAUTH_CLIENT_DETAILS")
public class CustomClientDetails extends BaseClientDetails {

    @Id
    @Column(name = "client_id")
    private String clientId;

    @Column(name = "name")
    private String clientName;

    @Column(name = "email")
    private String email;

    @Column(name = "password")
    private String clientSecret;

    @Column(name = "active")
    private boolean enabled;

    @Column(name = "access_token")
    private String accessToken;

    @Column(name = "refresh_token")
    private String refreshToken;

    @Column(name = "access_token_validity_seconds")
    private Integer accessTokenValiditySeconds;

    @Column(name = "refresh_token_validity_seconds")
    private Integer refreshTokenValiditySeconds;

    @Column(name = "scope")
    private String scope;

    @Column(name = "authorized_grant_types")
    private String authorizedGrantTypes;

    // Override getters/setters to map to your entity fields
    @Override
    public String getClientId() {
        return this.clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    @Override
    public String getClientSecret() {
        return this.clientSecret;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    @Override
    public boolean isEnabled() {
        return this.enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public Set<String> getScope() {
        return Set.of(this.scope.split(","));
    }

    @Override
    public Set<String> getAuthorizedGrantTypes() {
        return Set.of(this.authorizedGrantTypes.split(","));
    }

    // Add getters and setters for all remaining fields (clientName, email, tokens, validity, etc.)
}

3. Implement ClientDetailsService with JPA Repository

First, create a JPA Repository for your CustomClientDetails entity:

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface CustomClientDetailsRepository extends JpaRepository<CustomClientDetails, String> {
    Optional<CustomClientDetails> findByClientId(String clientId);
}

Then build a custom ClientDetailsService implementation that uses this repository to fetch client data:

import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.stereotype.Service;

@Service
public class CustomClientDetailsService implements ClientDetailsService {

    private final CustomClientDetailsRepository clientDetailsRepository;

    public CustomClientDetailsService(CustomClientDetailsRepository clientDetailsRepository) {
        this.clientDetailsRepository = clientDetailsRepository;
    }

    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
        return clientDetailsRepository.findByClientId(clientId)
                .orElseThrow(() -> new ClientRegistrationException("Client not found with id: " + clientId));
    }
}

4. Configure Authorization Server to Use Custom Client Details

Update your authorization server configuration to use your custom ClientDetailsService instead of the default in-memory or JDBC client store:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final CustomClientDetailsService customClientDetailsService;
    private final PasswordEncoder passwordEncoder; // Reuse your existing password encoder from user auth

    public AuthorizationServerConfig(CustomClientDetailsService customClientDetailsService, PasswordEncoder passwordEncoder) {
        this.customClientDetailsService = customClientDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(customClientDetailsService)
                .passwordEncoder(passwordEncoder); // Ensure client secrets are decoded correctly
    }

    // Keep your existing configuration for token store, authorization endpoints, etc.
    // If you want to persist tokens to your custom table automatically, implement a custom TokenStore
}

5. Test the Setup

  1. Insert a test client into your OAUTH_CLIENT_DETAILS table using a hashed password (use the same password encoder you use for user passwords).
  2. Use a tool like Postman to send a token request (e.g., password grant type) using the client's client_id and client_secret.
  3. Verify the server returns a valid token, and check if the access_token and refresh_token fields in your table are populated (if you added token persistence logic).

Tip: If you want to automatically persist tokens to your custom table, you'll need to implement a custom TokenStore that saves tokens directly to your OAUTH_CLIENT_DETAILS table or a separate dedicated token table. The current setup uses Spring's default in-memory token store unless you configure otherwise.

内容的提问来源于stack exchange,提问作者Juan Carlos Rodriguez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:32:07