First Commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import quant.rich.emoney.interfaces.ConfigInfo;
|
||||
import quant.rich.emoney.interfaces.IConfig;
|
||||
|
||||
/**
|
||||
* 实现自动化注册 Config
|
||||
*/
|
||||
@Slf4j
|
||||
@DependsOn("configService")
|
||||
public class ConfigAutoRegistrar implements BeanDefinitionRegistryPostProcessor {
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
|
||||
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
|
||||
scanner.addIncludeFilter(new AssignableTypeFilter(IConfig.class));
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(ConfigInfo.class));
|
||||
|
||||
scanner.findCandidateComponents("quant.rich.emoney.entity.config").forEach(beanDefinition -> {
|
||||
String className = beanDefinition.getBeanClassName();
|
||||
try {
|
||||
// 确保其 field 规则与 configService 内 field 生成规则一致,即:
|
||||
// 如果 @ConfigInfo 指定了 field 的,使用该 field + "Config"
|
||||
// 作为 beanName,否则使用首字母小写的 simpleClassName 作为
|
||||
// beanName,且 simpleClassName 无论如何必须以 Config 作为结尾。
|
||||
Class<?> clazz = Class.forName(className);
|
||||
String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase()
|
||||
+ clazz.getSimpleName().substring(1);
|
||||
|
||||
if (!IConfig.class.isAssignableFrom(clazz)) {
|
||||
log.warn("Config {} does not implement IConfig, ignore", beanName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!beanName.endsWith("Config")) {
|
||||
log.warn("Config {}'s simple class name does not end with \"Config\", ignore", beanName);
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigInfo info = clazz.getAnnotation(ConfigInfo.class);
|
||||
if (info == null) {
|
||||
log.warn("Config {} does not have @ConfigInfo annotation, ignore", clazz.getName());
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isNotBlank(info.field())) {
|
||||
beanName = info.field() + "Config";
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder factoryBean = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(ConfigServiceFactoryBean.class)
|
||||
.addConstructorArgValue(clazz);
|
||||
// 注意此处注册 factoryBean 不意味着 FactoryBean.getObject() 方法立即被执行,
|
||||
// Spring 管理的 Bean 默认在其被使用时才创建,所以如果 getObject() 调用一些方
|
||||
// 法,这些方法会在初次使用 Bean 时才被创建。如果这些方法对于启动过程很重要,
|
||||
// 需要在对应 Config(Bean) 上加上 @Bean 和 @Lazy(false) 注解,确保一旦准备好
|
||||
// 相应的 Bean 就会被创建。
|
||||
registry.registerBeanDefinition(beanName, factoryBean.getBeanDefinition());
|
||||
log.info("Add config {} to bean register", beanName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Failed to load class: " + className, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import quant.rich.emoney.interfaces.IConfig;
|
||||
import quant.rich.emoney.service.ConfigService;
|
||||
|
||||
/**
|
||||
* 实现配置项自动载入
|
||||
* @param <T>
|
||||
*/
|
||||
@Slf4j
|
||||
public class ConfigServiceFactoryBean<T extends IConfig<T>> implements FactoryBean<T>, BeanNameAware {
|
||||
|
||||
private final Class<T> targetClass;
|
||||
|
||||
private String beanName;
|
||||
|
||||
@Autowired
|
||||
private AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
public ConfigServiceFactoryBean(Class<T> targetClass) {
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
ConstructionGuard.enter(targetClass);
|
||||
boolean success = true;
|
||||
try {
|
||||
T bean = configService.getConfig(targetClass);
|
||||
beanFactory.autowireBean(bean);
|
||||
beanFactory.initializeBean(bean, beanName);
|
||||
configService.saveOrUpdate(bean);
|
||||
return bean;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Fail to load config: " + targetClass.getName(), e);
|
||||
success = false;
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
ConstructionGuard.exit(targetClass);
|
||||
if (success) {
|
||||
log.debug("getObject() for {} success", targetClass.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ConstructionGuard {
|
||||
private static final ThreadLocal<Set<Class<?>>> constructing = ThreadLocal.withInitial(HashSet::new);
|
||||
|
||||
public static boolean isConstructing(Class<?> clazz) {
|
||||
return constructing.get().contains(clazz);
|
||||
}
|
||||
|
||||
public static void enter(Class<?> clazz) {
|
||||
log.debug("Enter construction for {}", clazz.toString());
|
||||
if (isConstructing(clazz)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Class ")
|
||||
.append(clazz.getName())
|
||||
.append(" is being constructed but is seems like circular involving.");
|
||||
String msg = sb.toString();
|
||||
log.error(msg);
|
||||
throw new BeanCreationException(msg);
|
||||
}
|
||||
constructing.get().add(clazz);
|
||||
}
|
||||
|
||||
public static void exit(Class<?> clazz) {
|
||||
constructing.get().remove(clazz);
|
||||
log.debug("Exit construction for {}", clazz.toString());
|
||||
}
|
||||
}
|
||||
25
src/main/java/quant/rich/emoney/config/DataSourceConfig.java
Normal file
25
src/main/java/quant/rich/emoney/config/DataSourceConfig.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class DataSourceConfig {
|
||||
|
||||
@Bean(name = "postgreDataSource")
|
||||
@ConfigurationProperties(prefix = "spring.datasource.postgre")
|
||||
public DataSource postgreDataSource() {
|
||||
return DataSourceBuilder.create().build();
|
||||
}
|
||||
|
||||
@Bean(name = "sqliteDataSource")
|
||||
@ConfigurationProperties(prefix = "spring.datasource.sqlite")
|
||||
public DataSource sqliteDataSource() {
|
||||
DataSource dataSource = DataSourceBuilder.create().build();
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
38
src/main/java/quant/rich/emoney/config/EmoneyAutoConfig.java
Normal file
38
src/main/java/quant/rich/emoney/config/EmoneyAutoConfig.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
|
||||
import quant.rich.emoney.interceptor.BaseInterceptor;
|
||||
import quant.rich.emoney.service.ConfigService;
|
||||
|
||||
/**
|
||||
* Emoney-Auto 配置项
|
||||
*
|
||||
* @author Doghole
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@Import(ConfigAutoRegistrar.class)
|
||||
public class EmoneyAutoConfig implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
BaseInterceptor baseInterceptor;
|
||||
|
||||
@Autowired
|
||||
ConfigService configService;
|
||||
|
||||
/**
|
||||
* 简单鉴权/配置项注入拦截器
|
||||
*
|
||||
* @param registry
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(baseInterceptor).addPathPatterns("/**");
|
||||
}
|
||||
}
|
||||
31
src/main/java/quant/rich/emoney/config/KaptchaConfig.java
Normal file
31
src/main/java/quant/rich/emoney/config/KaptchaConfig.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||
import com.google.code.kaptcha.util.Config;
|
||||
|
||||
@Configuration
|
||||
public class KaptchaConfig {
|
||||
|
||||
@Bean(name = "kaptchaProperties")
|
||||
@ConfigurationProperties
|
||||
Properties getKaptchaProperties() {
|
||||
return new Properties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@DependsOn("kaptchaProperties")
|
||||
DefaultKaptcha getDefaultKaptcha(@Autowired Properties kaptchaProperties) {
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Config config = new Config(kaptchaProperties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
|
||||
return defaultKaptcha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
||||
|
||||
@Configuration
|
||||
@MapperScan(basePackages = "quant.rich.emoney.mapper.postgre", sqlSessionTemplateRef = "postgreSqlSessionTemplate")
|
||||
public class PostgreMybatisConfig {
|
||||
|
||||
@Bean("postgreSqlSessionFactory")
|
||||
public SqlSessionFactory postgreSqlSessionFactory(
|
||||
@Qualifier("postgreDataSource") DataSource dataSource) throws Exception {
|
||||
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
|
||||
factory.setDataSource(dataSource);
|
||||
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
factory.setPlugins(interceptor);
|
||||
|
||||
return factory.getObject();
|
||||
|
||||
}
|
||||
|
||||
@Bean("postgreSqlSessionTemplate")
|
||||
public SqlSessionTemplate sqliteSqlSessionTemplate(@Qualifier("postgreSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
|
||||
return new SqlSessionTemplate(sqlSessionFactory);
|
||||
}
|
||||
|
||||
@Bean("postgreTransactionManager")
|
||||
public DataSourceTransactionManager postgreTransacetionManager(@Qualifier("postgreDataSource") DataSource dataSource) {
|
||||
return new DataSourceTransactionManager(dataSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
import org.reflections.scanners.Scanners;
|
||||
import org.reflections.util.ConfigurationBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Reflections configuration, support autowired
|
||||
*
|
||||
* @author Bakwat
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class ReflectionsConfig {
|
||||
|
||||
@Bean("reflections")
|
||||
Reflections reflections() {
|
||||
return new Reflections(new ConfigurationBuilder()
|
||||
.addScanners(Scanners.MethodsAnnotated, Scanners.SubTypes, Scanners.TypesAnnotated)
|
||||
.forPackages("quant.rich.emoney"));
|
||||
}
|
||||
|
||||
}
|
||||
71
src/main/java/quant/rich/emoney/config/SecurityConfig.java
Normal file
71
src/main/java/quant/rich/emoney/config/SecurityConfig.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
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.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import quant.rich.emoney.service.AuthService;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
private final AuthService userDetailsService;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.headers(headers -> headers.cacheControl(cache -> cache.disable()))
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/admin/*/login").permitAll()
|
||||
.requestMatchers("/admin/*/static/**").permitAll()
|
||||
.requestMatchers("/public/**").permitAll()
|
||||
.requestMatchers("/captcha/**").permitAll()
|
||||
.requestMatchers("/api/**").permitAll()
|
||||
.requestMatchers("/img/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(form -> form // 开启表单登录,并指定登录页
|
||||
.loginPage("/admin/v1/login") // 指定登录页
|
||||
.loginProcessingUrl("/admin/v1/doLogin") // 处理登录请求的 URL
|
||||
.defaultSuccessUrl("/admin/v1/", false) // 登录成功后默认跳转
|
||||
.permitAll())
|
||||
.logout(logout -> logout
|
||||
.logoutUrl("/admin/v1/logout")
|
||||
.logoutSuccessUrl("/admin/v1/login")
|
||||
.invalidateHttpSession(true)
|
||||
.permitAll())
|
||||
.sessionManagement(session -> session
|
||||
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
||||
);
|
||||
;
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(@Autowired PasswordEncoder passwordEncoder) {
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
return new ProviderManager(provider);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return NoOpPasswordEncoder.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package quant.rich.emoney.config;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
||||
|
||||
@Configuration
|
||||
@MapperScan(basePackages = "quant.rich.emoney.mapper.sqlite", sqlSessionTemplateRef = "sqliteSqlSessionTemplate")
|
||||
public class SqliteMybatisConfig {
|
||||
|
||||
public static final String SQLITE_TRANSACTION_MANAGER = "sqliteTransactionManager";
|
||||
|
||||
@Bean("sqliteSqlSessionFactory")
|
||||
public SqlSessionFactory sqliteSqlSessionFactory(
|
||||
@Qualifier("sqliteDataSource") DataSource dataSource) throws Exception {
|
||||
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
|
||||
factory.setDataSource(dataSource);
|
||||
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQLITE));
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
factory.setPlugins(interceptor);
|
||||
|
||||
return factory.getObject();
|
||||
|
||||
}
|
||||
|
||||
@Bean("sqliteSqlSessionTemplate")
|
||||
public SqlSessionTemplate sqliteSqlSessionTemplate(@Qualifier("sqliteSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
|
||||
return new SqlSessionTemplate(sqlSessionFactory);
|
||||
}
|
||||
|
||||
@Bean("sqliteTransactionManager")
|
||||
public DataSourceTransactionManager postgreTransacetionManager(@Qualifier("sqliteDataSource") DataSource dataSource) {
|
||||
return new DataSourceTransactionManager(dataSource);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user