内存中具有两个数据库H2的Spring Security

问题描述 投票:0回答:2

我正在尝试访问我的控制台 H2 localhost:8080/h2-console,但它不起作用。

我正在使用 Spring Boot (V 3.0.2)

当我尝试访问第二个 h2 数据源或尝试访问控制台路径时,问题就出现了,因为它无法决定要使用什么 H2CONSOLE.PROPERTIES。

我的安全配置是:

包 com.queueup.security;

    import com.queueup.security.filters.JwtAuthenticationFilter;
    import com.queueup.security.filters.JwtAuthorizationFilter;
    import com.queueup.security.jwt.JwtUtils;
    import com.queueup.security.service.UserDetailsServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toH2Console;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    
        @Autowired
        JwtUtils jwtUtils;
    
        @Autowired
        UserDetailsServiceImpl userDetailsService;
    
        @Autowired
        JwtAuthorizationFilter authorizationFilter;
    
        @Bean
        SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, AuthenticationManager authenticationManager) throws Exception {
    
            JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(jwtUtils);
            jwtAuthenticationFilter.setAuthenticationManager(authenticationManager);
            jwtAuthenticationFilter.setFilterProcessesUrl("/login");
    
            return httpSecurity
                    .csrf(AbstractHttpConfigurer::disable)
                    .headers().frameOptions().sameOrigin()
                    .and()
                    .authorizeHttpRequests(auth -> {
                        auth.requestMatchers(toH2Console()).permitAll();
                        auth.requestMatchers("/hello").permitAll();
                        auth.requestMatchers("/createUser").permitAll();
                        auth.requestMatchers("/h2-console/**").permitAll();
                        auth.requestMatchers("/api/path/**").hasAnyRole("ADMIN", "USER");
                        auth.requestMatchers("/api/path1/**").hasRole("USER");
                        auth.anyRequest().authenticated();
                    })
                    .sessionManagement(session -> {
                        session.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                    })
                    .addFilter(jwtAuthenticationFilter)
                    .addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class)
                    .build();
        }
    
    //    @Bean
    //    UserDetailsService userDetailsService(){
    //        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    //        manager.createUser(User.withUsername("santiago")
    //                .password("1234")
    //                .roles()
    //                .build());
    //
    //        return manager;
    //    }
    
        @Bean
        PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
       }
    
        @Bean
        AuthenticationManager authenticationManager(HttpSecurity httpSecurity, PasswordEncoder passwordEncoder) throws Exception {
            return httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                    .userDetailsService(userDetailsService)
                    .passwordEncoder(passwordEncoder)
                    .and().build();
        }
    }`

我的数据源配置是:

package com.queueup;

import com.queueup.config.DataSourcesConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.HashMap;


@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.queueup.repositories.enqueuers",
        entityManagerFactoryRef = "enqueueUpEntityManager",
        transactionManagerRef = "enqueueupTransactionManager")
public class PersistenceEnqueueupConfiguration {

    private static final String URL = "jdbc:h2:mem:enqueueupdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS ENQUEUEUP";

    public PersistenceEnqueueupConfiguration(){
        super();
    }

    @Bean
    public DataSource enqueueUpDataSource() {

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(DataSourcesConfig.H2DRIVER);
        dataSource.setUrl(URL);
        dataSource.setUsername("sa");
        dataSource.setPassword("");
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean enqueueUpEntityManager() {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(enqueueUpDataSource());
        em.setPackagesToScan(
                "com.queueup.entities");

        HibernateJpaVendorAdapter vendorAdapter
                = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
               "create-drop");
        properties.put("hibernate.dialect",
                DataSourcesConfig.H2DIALECT);
        properties.put("dataSourceClassName",
                DataSourcesConfig.H2DRIVER);
        properties.put("h2.console.enabled",
                false);
        properties.put("hibernate.cache.use_second_level_cache", false);
        properties.put("hibernate.cache.use_query_cache", false);
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    public PlatformTransactionManager enqueueupTransactionManager() {

        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                enqueueUpEntityManager().getObject());
        return transactionManager;
    }
}

package com.queueup;

import com.queueup.config.DataSourcesConfig;
import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;

import static org.springframework.boot.autoconfigure.security.servlet.PathRequest.toH2Console;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.queueup.repositories.security",
        entityManagerFactoryRef = "userEntityManager",
        transactionManagerRef = "userTransactionManager")
public class PersistenceUsersConfiguration {

    private static final String URL = "jdbc:h2:mem:userdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS USERS";

    public PersistenceUsersConfiguration(){
        super();
    }

    @Primary
    @Bean
    public DataSource userDataSource() {

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(DataSourcesConfig.H2DRIVER);
        dataSource.setUrl(URL);
        dataSource.setUsername("sa");
        dataSource.setPassword("");

        return dataSource;
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean userEntityManager() {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(userDataSource());
        em.setPackagesToScan(
                "com.queueup.security.models");

        HibernateJpaVendorAdapter vendorAdapter
                = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
                "create-drop");
        properties.put("hibernate.dialect",
                DataSourcesConfig.H2DIALECT);
        properties.put("dataSourceClassName",
                DataSourcesConfig.H2DRIVER);
        properties.put("h2.console.enabled",
                true);
        properties.put("h2.console.path", toH2Console());
        properties.put("hibernate.cache.use_second_level_cache", false);
        properties.put("hibernate.cache.use_query_cache", false);
        em.setJpaPropertyMap(properties);

        return em;
    }


    @Bean
    @Primary
    public PlatformTransactionManager userTransactionManager() {

        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                userEntityManager().getObject());

        return transactionManager;
    }

}

如果我尝试访问控制台,我会得到 403 禁止,当我尝试执行第二个数据源(以任何顺序)时,我会得到

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.h2.H2ConsoleProperties' 

但是我如果删除

auth.requestMatchers(toH2Console()).permitAll();

这条线可以工作,但我可以访问控制台。问题是因为 spring 不知道使用什么实例。

我定义了一个bean来尝试解决它,但是,如果我没有收到错误,我可以访问控制台。

import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class H2ConsolePropConfig {

    private final H2ConsoleProperties h2ConsoleProperties;

    public H2ConsolePropConfig(){
        h2ConsoleProperties = new H2ConsoleProperties();

        h2ConsoleProperties.setEnabled(true);
        h2ConsoleProperties.setPath("/h2-console");
        h2ConsoleProperties.getSettings().setWebAdminPassword("");
        h2ConsoleProperties.getSettings().setWebAllowOthers(true);
        h2ConsoleProperties.getSettings().setTrace(true);
    }

    @Bean
    @Primary
    public H2ConsoleProperties getH2ConsoleProperties() {
        return h2ConsoleProperties;
    }
} 

也许如果我知道如何将它注入到我的主要数据源中,我就可以让它继续运行。

如果您能帮助我,我将不胜感激。

提前致谢。 问候

java spring-boot spring-security spring-data-jpa h2
2个回答
0
投票

让我们看看问题是什么:

  • 默认情况下 spring 查询默认/主数据库

我们如何解决它:

  • 我们需要动态更改默认数据库

  • 假设您有 2 个内存数据库,其中一个用于学生,一个用于会员

步骤:

  • 定义下面的枚举
public enum AuthServerDatabase {
    CLIENT_DB_STUDENT, CLIENT_DB_MEMBER
}

  • 定义下面的类来设置和获取默认数据库上下文
public class AuthServerDatabaseContextHolder {
    private static ThreadLocal<AuthServerDatabase> CONTEXT = new ThreadLocal<>();

    public static void set(AuthServerDatabase clientDatabase) {
        Assert.notNull(clientDatabase, "clientDatabase cannot be null");
        CONTEXT.set(clientDatabase);
    }

    public static AuthServerDatabase getClientDatabase() {
        return CONTEXT.get();
    }

    public static void clear() {
        CONTEXT.remove();
    }

}

  • 覆盖下面的方法

public class AuthServerSourceRouter extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return AuthServerDatabaseContextHolder.getClientDatabase();
    }
}

  • 在下面写下数据库配置

@Configuration
public class DBConfig {

    public DataSource studentDataSource()
    {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.url(" your db connection ur");
        dataSourceBuilder.username("your db username");
        dataSourceBuilder.password("your db password");
        return dataSourceBuilder.build();
    }
    public DataSource membersDataSource()
    {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.url(" your db connection ur");
        dataSourceBuilder.username("your db username");
        dataSourceBuilder.password("your db password");
        return dataSourceBuilder.build();
    }


    @Bean
    public DataSource clientDatasource()
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        DataSource studentDB = studentDataSource();
        DataSource membersDB = membersDataSource();
        targetDataSources.put(AuthServerDatabase.CLIENT_DB_STUDENT, studentDB);
        targetDataSources.put(AuthServerDatabase.CLIENT_DB_MEMBER, membersDB);

        AuthServerSourceRouter clientRoutingDatasource = new AuthServerSourceRouter();
        clientRoutingDatasource.setTargetDataSources(targetDataSources);
        clientRoutingDatasource.setDefaultTargetDataSource(studentDB);
        return clientRoutingDatasource;
    }
}
 

  • 现在即时设置您的数据库选择并清除,即设置为默认/主要

AuthServerDatabaseContextHolder.set(AuthServerDatabase.CLIENT_DB_MEMBER);

//与会员数据库关联的查询或业务逻辑

AuthServerDatabaseContextHolder.clear();


-1
投票

首先,感谢您抽出时间。

我在你的 dbconfig 中做了一些修改来保存我的 befores beans 和我的 DBCONFIG 类


@Configuration
@AllArgsConstructor
public class DBConfig {

    private final PersistenceUsersConfiguration persistenceUsersConfiguration;
    private final PersistenceEnqueueupConfiguration persistenceEnqueueupConfiguration;

    @Bean
    public DataSource clientDataSource(){
        Map<Object, Object> targetDataSources = new HashMap<>();
        DataSource userDB = persistenceUsersConfiguration.userDataSource();
        DataSource enqueueUpDB = persistenceEnqueueupConfiguration.enqueueUpDataSource();
        targetDataSources.put(AuthServerDataBase.CLIENT_DB_USERS, userDB);
        targetDataSources.put(AuthServerDataBase.CLIENT_DB_ENQUEUEUP, enqueueUpDB);

        AuthServerSourceRouter clientRoutingDatasource = new AuthServerSourceRouter();
        clientRoutingDatasource.setTargetDataSources(targetDataSources);
        clientRoutingDatasource.setDefaultTargetDataSource(userDB);
        return clientRoutingDatasource;
    }

和我的前配置数据源bean,我退出了@Configuration并使用了@Components。使用 debug 初始化应用程序,数据源是正确的,但是当我尝试访问 localhost:8080/h2 时,我得到了相同的 500 错误。

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.h2.H2ConsoleProperties' available
© www.soinside.com 2019 - 2024. All rights reserved.