Java 如何在Spring中使用JDBC for ClientDetailsServiceConfigurer添加客户端?

Java 如何在Spring中使用JDBC for ClientDetailsServiceConfigurer添加客户端?,java,mysql,spring-security-oauth2,Java,Mysql,Spring Security Oauth2,我记忆中的东西工作如下: @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp") .authorizedGrantTypes("password", "refresh_token")

我记忆中的东西工作如下:

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.inMemory()
               .withClient("clientapp")
               .authorizedGrantTypes("password", "refresh_token")
               .authorities("USER")
               .scopes("read", "write")
               .resourceIds(RESOURCE_ID)
               .secret("123456");
}
clients.jdbc(dataSource).withClient("clientapp")
           .authorizedGrantTypes("password", "refresh_token")
           .authorities("USER")
           .scopes("read", "write")
           .resourceIds(RESOURCE_ID)
           .secret("123456").and().build();
我想使用JDBC实现。为此,我创建了以下表(使用MySQL):

我是否需要在MySQL表中手动添加客户机

我试过这个:

clients.jdbc(dataSource).withClient("clientapp")
               .authorizedGrantTypes("password", "refresh_token")
               .authorities("USER")
               .scopes("read", "write")
               .resourceIds(RESOURCE_ID)
               .secret("123456");

希望Spring能在好的表格中插入正确的内容,但它似乎没有这样做。为什么您可以在
jdbc()之后进一步链接?

请暂停以下步骤:

  • 启动服务器后,将此schema.sql放入SpringBoot将检测到的资源文件夹中。如果不使用SpringBoot,无需担心,只需从任何Mysql应用程序客户端(phpmyadmin、HeidiSQL、Navicat..)导入此脚本即可

    如果存在oauth\u客户端详细信息,则删除表;
    创建表oauth\u客户端\u详细信息(
    客户端id VARCHAR(255)主键,
    资源ID VARCHAR(255),
    客户_secret VARCHAR(255),
    范围VARCHAR(255),
    授权授权类型VARCHAR(255),
    web服务器重定向uri VARCHAR(255),
    瓦查尔(255),
    访问\u令牌\u有效性整数,
    刷新\u令牌\u有效性整数,
    附加信息VARCHAR(4096),
    自动批准VARCHAR(255)
    );
    如果存在oauth_客户端_令牌,则删除表;
    创建表oauth_客户端_令牌(
    令牌id VARCHAR(255),
    令牌长VARBINARY,
    身份验证\u id VARCHAR(255)主键,
    用户名VARCHAR(255),
    客户id VARCHAR(255)
    );
    如果存在oauth\u访问\u令牌,则删除表;
    创建表oauth\u访问\u令牌(
    令牌id VARCHAR(255),
    令牌长VARBINARY,
    身份验证\u id VARCHAR(255)主键,
    用户名VARCHAR(255),
    客户id VARCHAR(255),
    身份验证长VARBINARY,
    刷新令牌VARCHAR(255)
    );
    如果存在oauth\u刷新\u令牌,则删除表;
    创建表oauth\u刷新\u令牌(
    令牌id VARCHAR(255),
    令牌长VARBINARY,
    身份验证长VARBINARY
    );
    如果存在oauth_代码,则删除表;
    创建表oauth_代码(
    代码VARCHAR(255),身份验证长VARBINARY
    );
    如果存在oauth_批准,则删除表格;
    创建表oauth\u批准(
    userId VARCHAR(255),
    clientId VARCHAR(255),
    范围VARCHAR(255),
    状态VARCHAR(10),
    到期时间戳,
    lastModifiedAt时间戳
    );
    如果存在ClientDetails,则删除表;
    创建表ClientDetails(
    appId VARCHAR(255)主键,
    resourceIds VARCHAR(255),
    appSecret VARCHAR(255),
    范围VARCHAR(255),
    grantTypes VARCHAR(255),
    重定向URL VARCHAR(255),
    瓦查尔(255),
    访问\u令牌\u有效性整数,
    刷新\u令牌\u有效性整数,
    附加信息VARCHAR(4096),
    自动批准示波器VARCHAR(255)
    );
    
  • 将数据源、authenticationManager、UserDetails服务注入到Othorization服务器中

    
    @自动连线
    私有MyUserDetails服务UserDetails服务;
    @注入
    私人AuthenticationManager AuthenticationManager;
    @自动连线
    私有数据源
    
  • 您将需要创建这两个bean

    @Bean
    公共JdbcTokenStore tokenStore(){
    返回新的JdbcTokenStore(数据源);
    }
    @豆子
    受保护的AuthorizationCodeServices AuthorizationCodeServices(){
    返回新的JdbcAuthorizationCodeServices(数据源);
    }
    请不要忘记AuthorizationServer类顶部的@Configuration

  • 配置要在mysql数据库中创建的客户端应用程序:
    clients.jdbc(dataSource).withClient(“clientapp”)
    .authorizedGrantTypes(“密码”、“刷新令牌”)
    .当局(“用户”)
    .作用域(“读”、“写”)
    .ResourceID(资源\u ID)
    .机密(“123456”)
    你已经这么做了

  • 最重要的事情(我想您已经忘记了…)是:要使用AuthorizationServerEndpointsConfigure配置您的端点:

    endpoints.userDetailsService(userDetailsService).authorizationCodeServices(authorizationCodeServices()).authenticationManager(this.authenticationManager).tokenStore(tokenStore()).approvalStoreDisabled();
    
  • 就这样,伙计,现在应该可以了;)

    请随意要求更多。。。我很乐意帮忙


    我已经从推特给你发了一条消息

    @AndroidLover的答案很好,但可以简化。除非需要jdbc令牌存储,否则不需要创建oauth\u访问\u令牌、oauth\u刷新\u令牌等表

    由于您只需要一个jdbc客户机详细信息服务,因此只需执行以下操作:
    一,。创建客户端详细信息表oauth_client_details,例如:

    drop table if exists oauth_client_details;
        create table oauth_client_details (
        client_id VARCHAR(255) PRIMARY KEY,
        resource_ids VARCHAR(255),
        client_secret VARCHAR(255),
        scope VARCHAR(255),
        authorized_grant_types VARCHAR(255),
        web_server_redirect_uri VARCHAR(255),
        authorities VARCHAR(255),
        access_token_validity INTEGER,
        refresh_token_validity INTEGER,
        additional_information VARCHAR(4096),
        autoapprove VARCHAR(255)
        );
    
    二,。创建一个实现UserDetail接口的用户模型,例如(我使用的是SpringJPA,在本例中,您可以使用mybatis、jdbc等等):

    四,。最后,配置您的身份验证服务器:

    @Configuration
    @EnableAuthorizationServer
    public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Autowired
    @Qualifier("dataSource")
    DataSource dataSource;
    
    @Autowired
    @Qualifier("userDetailsService")
    private UserDetailsService userDetailsService;
    
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer configurer) {
        configurer
                .authenticationManager(authenticationManager)                
                .approvalStoreDisabled()
                .userDetailsService(userDetailsService);
    }
    
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
    {
        clients
                .jdbc(dataSource)
                .inMemory().withClient("my-trusted-
    client").secret("secret").accessTokenValiditySeconds(3600)
                .scopes("read", "write").authorizedGrantTypes("password", 
    "refresh_token").resourceIds("resource");
    }
    }
    

    这个问题已经很老了,但是没有一个回答回答了发问者原来的问题。我在熟悉spring的oauth2实现时遇到了同样的问题,我想知道为什么
    ClientDetailsServiceConfigurer
    没有持久化通过
    JdbcClientDetailsServiceBuilder
    以编程方式添加的客户机(通过调用
    jdbc(数据源)实例化)
    方法),尽管网络上的所有教程都显示了类似的示例,如Wim发布的示例
    @Service("userDetailsService")
    public class UserService implements UserDetailsService {
    
    @Autowired
    UserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String userName) throws 
    UsernameNotFoundException {
        return userRepository.findByUsername(userName);
    }
    }
    
    @Configuration
    @EnableAuthorizationServer
    public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Autowired
    @Qualifier("dataSource")
    DataSource dataSource;
    
    @Autowired
    @Qualifier("userDetailsService")
    private UserDetailsService userDetailsService;
    
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer configurer) {
        configurer
                .authenticationManager(authenticationManager)                
                .approvalStoreDisabled()
                .userDetailsService(userDetailsService);
    }
    
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
    {
        clients
                .jdbc(dataSource)
                .inMemory().withClient("my-trusted-
    client").secret("secret").accessTokenValiditySeconds(3600)
                .scopes("read", "write").authorizedGrantTypes("password", 
    "refresh_token").resourceIds("resource");
    }
    }
    
    clients.jdbc(dataSource).withClient("clientapp")
               .authorizedGrantTypes("password", "refresh_token")
               .authorities("USER")
               .scopes("read", "write")
               .resourceIds(RESOURCE_ID)
               .secret("123456").and().build();
    
    @Override
    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
    
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
    
        if(!jdbcClientDetailsService.listClientDetails().isEmpty() ) {          
        jdbcClientDetailsService.removeClientDetails(CLIEN_ID);     
        }
    
        if(jdbcClientDetailsService.listClientDetails().isEmpty() ) {
            configurer.jdbc(dataSource).withClient(CLIEN_ID).secret(encoder.encode(CLIENT_SECRET))
            .authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT)
            .scopes(SCOPE_READ, SCOPE_WRITE, TRUST).accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
            .refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS).and().build();                
        }       
        configurer.jdbc(dataSource).build().loadClientByClientId(CLIEN_ID); 
    }
    
     if(!jdbcClientDetailsService.listClientDetails().isEmpty() ) { 
    
        jdbcClientDetailsService.removeClientDetails(CLIEN_ID);
    
        }
    
    if(jdbcClientDetailsService.listClientDetails().isEmpty() ) {
            configurer.jdbc(dataSource).withClient(CLIEN_ID).secret(encoder.encode(CLIENT_SECRET))
            .authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT)
            .scopes(SCOPE_READ, SCOPE_WRITE, TRUST).accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
            .refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS).and().build();
    
        } 
    
    configurer.jdbc(dataSource).build().loadClientByClientId(CLIEN_ID);
    
    @Bean
    public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
        //...setting dataSource and databasePopulator
    }
    private DatabasePopulator databasePopulator() {
        //...adding your schema script
    }
    @Bean
    public DataSource dataSource() {
        //...setting driverclassname, url, etc
    }
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //...
        clients.jdbc(this.dataSource()).withClient("example").(...).build()
    }
    
    create table IF NOT EXISTS   oauth_client_details
    (
        client_id               VARCHAR(256) PRIMARY KEY,
        resource_ids            VARCHAR(256),
        client_secret           VARCHAR(256),
        scope                   VARCHAR(256),
        authorized_grant_types  VARCHAR(256),
        web_server_redirect_uri VARCHAR(256),
        authorities             VARCHAR(256),
        access_token_validity   INTEGER,
        refresh_token_validity  INTEGER,
        additional_information  VARCHAR(4096),
        autoapprove             VARCHAR(256)
    );
    
    create table IF NOT EXISTS  oauth_client_token
    (
        token_id          VARCHAR(256),
        token             bytea,
        authentication_id VARCHAR(256) PRIMARY KEY,
        user_name         VARCHAR(256),
        client_id         VARCHAR(256)
    );
    
    create table IF NOT EXISTS  oauth_access_token
    (
        token_id          VARCHAR(256),
        token             bytea,
        authentication_id VARCHAR(256) PRIMARY KEY,
        user_name         VARCHAR(256),
        client_id         VARCHAR(256),
        authentication    bytea,
        refresh_token     VARCHAR(256)
    );
    
    create table IF NOT EXISTS  oauth_refresh_token
    (
        token_id       VARCHAR(256),
        token          bytea,
        authentication bytea
    );
    
    create table IF NOT EXISTS  oauth_code
    (
        code           VARCHAR(256),
        authentication bytea
    );
    
    create table IF NOT EXISTS  oauth_approvals
    (
        userId         VARCHAR(256),
        clientId       VARCHAR(256),
        scope          VARCHAR(256),
        status         VARCHAR(10),
        expiresAt      TIMESTAMP,
        lastModifiedAt TIMESTAMP
    );
    
    
    -- customized oauth_client_details table
    create table IF NOT EXISTS  ClientDetails
    (
        appId                  VARCHAR(256) PRIMARY KEY,
        resourceIds            VARCHAR(256),
        appSecret              VARCHAR(256),
        scope                  VARCHAR(256),
        grantTypes             VARCHAR(256),
        redirectUrl            VARCHAR(256),
        authorities            VARCHAR(256),
        access_token_validity  INTEGER,
        refresh_token_validity INTEGER,
        additionalInformation  VARCHAR(4096),
        autoApproveScopes      VARCHAR(256)
    );
    
    @Configuration
    @EnableAuthorizationServer
    class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private BCryptPasswordEncoder passwordEncoder;
    
        @Autowired
        @Qualifier("dataSource")
        DataSource dataSource;
    
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        public TokenStore tokenStore() {
            return new JdbcTokenStore(dataSource);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security
                    .tokenKeyAccess("permitAll()")
                    .checkTokenAccess("isAuthenticated()")
                    .allowFormAuthenticationForClients();
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.jdbc(dataSource);
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
            authorizationServerEndpointsConfigurer.tokenStore(tokenStore());
        }
    }