refactor: 目录结构优化

This commit is contained in:
ray
2024-08-30 08:18:53 +08:00
parent 7795c4d538
commit 95ef5dfd1f
215 changed files with 581 additions and 727 deletions

View File

@@ -0,0 +1,55 @@
package com.youlai.boot.config;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import com.youlai.boot.config.property.CaptchaProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.awt.*;
/**
* 验证码自动装配配置
*
* @author haoxr
* @since 2023/11/24
*/
@Configuration
public class CaptchaConfig {
@Autowired
private CaptchaProperties captchaProperties;
/**
* 验证码文字生成器
*
* @return CodeGenerator
*/
@Bean
public CodeGenerator codeGenerator() {
String codeType = captchaProperties.getCode().getType();
int codeLength = captchaProperties.getCode().getLength();
if ("math".equalsIgnoreCase(codeType)) {
return new MathGenerator(codeLength);
} else if ("random".equalsIgnoreCase(codeType)) {
return new RandomGenerator(codeLength);
} else {
throw new IllegalArgumentException("Invalid captcha generator type: " + codeType);
}
}
/**
* 验证码字体
*/
@Bean
public Font captchaFont() {
String fontName = captchaProperties.getFont().getName();
int fontSize = captchaProperties.getFont().getSize();
int fontWight = captchaProperties.getFont().getWeight();
return new Font(fontName, fontWight, fontSize);
}
}

View File

@@ -0,0 +1,42 @@
package com.youlai.boot.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Collections;
/**
* CORS 资源共享配置
*
* @author haoxr
* @since 2023/4/17
*/
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1.允许任何来源
corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
//2.允许任何请求头
corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
//3.允许任何方法
corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
//4.允许凭证
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
CorsFilter corsFilter = new CorsFilter(source);
FilterRegistrationBean<CorsFilter> filterRegistrationBean=new FilterRegistrationBean<>(corsFilter);
filterRegistrationBean.setOrder(-101); // 小于 SpringSecurity Filter的 Order(-100) 即可
return filterRegistrationBean;
}
}

View File

@@ -0,0 +1,51 @@
package com.youlai.boot.config;
import com.youlai.boot.config.property.MailProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import java.util.Properties;
/**
* MailConfig 配置类,用于手动配置和注入 JavaMailSender。
* 通过读取 MailProperties 类中配置的邮件相关属性来初始化 JavaMailSender。
* <p>
* 手动注入的原因是为了避免在使用 application-dev.yml 或其他非 application.yml 配置文件时,
* IDEA 提示无法找到 JavaMailSender 的 bean。
*
* @author Ray
* @since 2024/8/17
*/
@Configuration
@EnableConfigurationProperties(MailProperties.class)
public class MailConfig {
private final MailProperties mailProperties;
public MailConfig(MailProperties mailProperties) {
this.mailProperties = mailProperties;
}
/**
* 创建并配置 JavaMailSender bean。
*
* @return 配置好的 JavaMailSender 实例
*/
@Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(mailProperties.getHost());
mailSender.setPort(mailProperties.getPort());
mailSender.setUsername(mailProperties.getUsername());
mailSender.setPassword(mailProperties.getPassword());
Properties properties = mailSender.getJavaMailProperties();
properties.put("mail.smtp.auth", mailProperties.getProperties().getSmtp().isAuth());
properties.put("mail.smtp.starttls.enable", mailProperties.getProperties().getSmtp().getStarttls().isEnable());
return mailSender;
}
}

View File

@@ -0,0 +1,48 @@
package com.youlai.boot.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.youlai.system.plugin.mybatis.handler.MyDataPermissionHandler;
import com.youlai.system.plugin.mybatis.handler.MyMetaObjectHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* mybatis-plus 自动配置类
*
* @author haoxr
* @since 2022/7/2
*/
@Configuration
@EnableTransactionManagement
public class MybatisConfig {
/**
* 分页插件和数据权限插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//数据权限
interceptor.addInnerInterceptor(new DataPermissionInterceptor(new MyDataPermissionHandler()));
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
/**
* 自动填充数据库创建人、创建时间、更新人、更新时间
*/
@Bean
public GlobalConfig globalConfig() {
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setMetaObjectHandler(new MyMetaObjectHandler());
return globalConfig;
}
}

View File

@@ -0,0 +1,74 @@
package com.youlai.boot.config;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
/**
* Redis 缓存配置
*
* @author Ray
* @since 2023/12/4
*/
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@ConditionalOnProperty(name = "spring.cache.enabled") // xxl.job.enabled = true 才会自动装配
public class RedisCacheConfig {
/**
* 自定义 RedisCacheManager
* <p>
* 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer
*
* @param redisConnectionFactory {@link RedisConnectionFactory}
* @param cacheProperties {@link CacheProperties}
* @return {@link RedisCacheManager}
*/
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory, CacheProperties cacheProperties){
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration(cacheProperties))
.build();
}
/**
* 自定义 RedisCacheConfiguration
*
* @param cacheProperties {@link CacheProperties}
* @return {@link RedisCacheConfiguration}
*/
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()));
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
// 覆盖默认key双冒号 CacheKeyPrefix#prefixed
config = config.computePrefixWith(name -> name + ":");
return config;
}
}

View File

@@ -0,0 +1,42 @@
package com.youlai.boot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
/**
* Redis 配置
*
* @author Ray
* @since 2023/5/15
*/
@Configuration
public class RedisConfig {
/**
* 自定义 RedisTemplate
* <p>
* 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer
*
* @param redisConnectionFactory {@link RedisConnectionFactory}
* @return {@link RedisTemplate}
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.json());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setHashValueSerializer(RedisSerializer.json());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

View File

@@ -0,0 +1,110 @@
package com.youlai.boot.config;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.collection.CollectionUtil;
import com.youlai.boot.common.constant.SecurityConstants;
import com.youlai.boot.config.property.SecurityProperties;
import com.youlai.boot.framework.filter.RateLimiterFilter;
import com.youlai.boot.framework.security.exception.MyAccessDeniedHandler;
import com.youlai.boot.framework.security.exception.MyAuthenticationEntryPoint;
import com.youlai.boot.framework.security.filter.JwtValidationFilter;
import com.youlai.boot.framework.security.filter.CaptchaValidationFilter;
import com.youlai.boot.module.system.service.SysConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
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;
/**
* Spring Security 权限配置
*
* @author Ray
* @since 2023/2/17
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final MyAuthenticationEntryPoint authenticationEntryPoint;
private final MyAccessDeniedHandler accessDeniedHandler;
private final RedisTemplate<String, Object> redisTemplate;
private final CodeGenerator codeGenerator;
private final SecurityProperties securityProperties;
private final SysConfigService sysConfigService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(requestMatcherRegistry ->
requestMatcherRegistry.requestMatchers(SecurityConstants.LOGIN_PATH).permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
httpSecurityExceptionHandlingConfigurer
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
)
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(AbstractHttpConfigurer::disable)
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
;
// 限流过滤器
http.addFilterBefore(new RateLimiterFilter(redisTemplate, sysConfigService), UsernamePasswordAuthenticationFilter.class);
// 验证码校验过滤器
http.addFilterBefore(new CaptchaValidationFilter(redisTemplate, codeGenerator), UsernamePasswordAuthenticationFilter.class);
// JWT 校验过滤器
http.addFilterBefore(new JwtValidationFilter(redisTemplate,securityProperties.getJwt().getKey()), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
/**
* 不走过滤器链的放行配置
*/
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> {
if (CollectionUtil.isNotEmpty(securityProperties.getIgnoreUrls())) {
web.ignoring().requestMatchers(securityProperties.getIgnoreUrls().toArray(new String[0]));
}
};
}
/**
* 密码编码器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* AuthenticationManager 手动注入
*
* @param authenticationConfiguration 认证配置
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}

View File

@@ -0,0 +1,84 @@
package com.youlai.boot.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
/**
* Swagger 配置
* <p>
*
* @author Ray
* @see <a href="https://doc.xiaominfo.com/docs/quick-start">knife4j 快速开始</a>
* @since 2023/2/17
*/
@Configuration
@Slf4j
@RequiredArgsConstructor
public class SwaggerConfig {
private final Environment environment;
/**
* 接口信息
*/
@Bean
public OpenAPI openApi() {
String appVersion = environment.getProperty("project.version", "1.0.0");
return new OpenAPI()
.info(new Info()
.title("系统接口文档")
.version(appVersion)
)
// 配置全局鉴权参数-Authorize
.components(new Components()
.addSecuritySchemes(HttpHeaders.AUTHORIZATION,
new SecurityScheme()
.name(HttpHeaders.AUTHORIZATION)
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.scheme("Bearer")
.bearerFormat("JWT")
)
);
}
/**
* 全局自定义扩展
* <p>
* 在OpenAPI规范中Operation 是一个表示 API 端点Endpoint或操作的对象。
* 每个路径Path对象可以包含一个或多个 Operation 对象,用于描述与该路径相关联的不同 HTTP 方法(例如 GET、POST、PUT 等)。
*/
@Bean
public GlobalOpenApiCustomizer globalOpenApiCustomizer() {
return openApi -> {
// 全局添加鉴权参数
if (openApi.getPaths() != null) {
openApi.getPaths().forEach((s, pathItem) -> {
// 登录接口/验证码不需要添加鉴权参数
if (s.equals("/api/v1/auth/login") || s.equals("/api/v1/auth/captcha")) {
return;
}
// 接口添加鉴权参数
pathItem.readOperations()
.forEach(operation ->
operation.addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION))
);
});
}
};
}
}

View File

@@ -0,0 +1,69 @@
package com.youlai.boot.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.TimeZone;
/**
* WebMvc 自动装配配置
*
* @author Ray
* @since 2020/10/16
*/
@Configuration
@Slf4j
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
// 后台Long值传递给前端精度丢失问题JS最大精度整数是Math.pow(2,53)
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
}
@Bean
public Validator validator(final AutowireCapableBeanFactory autowireCapableBeanFactory) {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true) // failFast=true 不校验所有参数,只要出现校验失败情况直接返回,不再进行后续参数校验
.constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}

View File

@@ -0,0 +1,108 @@
package com.youlai.boot.config;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.boot.module.system.event.UserConnectionEvent;
import com.youlai.system.service.WebsocketService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* WebSocket 自动配置类
*
* @author haoxr
* @since 2.4.0
*/
// 启用WebSocket消息代理功能和配置STOMP协议实现实时双向通信和消息传递
@EnableWebSocketMessageBroker
@Configuration
@Slf4j
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private final ApplicationEventPublisher eventPublisher;
public WebSocketConfig(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
/**
* 注册一个端点,客户端通过这个端点进行连接
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
// 注册 /ws 的端点
.addEndpoint("/ws")
// 允许跨域
.setAllowedOriginPatterns("*");
}
/**
* 配置消息代理
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 客户端发送消息的请求前缀
registry.setApplicationDestinationPrefixes("/app");
// 客户端订阅消息的请求前缀topic一般用于广播推送queue用于点对点推送
registry.enableSimpleBroker("/topic", "/queue");
// 服务端通知客户端的前缀可以不设置默认为user
registry.setUserDestinationPrefix("/user");
}
/**
* 配置客户端入站通道拦截器
* <p>
* 添加 ChannelInterceptor 拦截器,用于在消息发送前,从请求头中获取 token 并解析出用户信息(username),用于点对点发送消息给指定用户
*
* @param registration 通道注册器
*/
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(@NotNull Message<?> message, @NotNull MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor != null) {
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String bearerToken = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION);
if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
bearerToken = bearerToken.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
String username = JWTUtil.parseToken(bearerToken).getPayloads().getStr(JWTPayload.SUBJECT);
if (StrUtil.isNotBlank(username)) {
accessor.setUser(() -> username);
eventPublisher.publishEvent(new UserConnectionEvent(this, username, true));
}
}
} else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) {
if (accessor.getUser() != null) {
String username = accessor.getUser().getName();
eventPublisher.publishEvent(new UserConnectionEvent(this, username, false));
}
}
}
return ChannelInterceptor.super.preSend(message, channel);
}
});
}
}

View File

@@ -0,0 +1,61 @@
package com.youlai.boot.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Configuration
@ConditionalOnProperty(name = "xxl.job.enabled") // xxl.job.enabled = true 才会自动装配
@Slf4j
public class XxlJobConfig {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
log.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}

View File

@@ -0,0 +1,50 @@
package com.youlai.boot.config.property;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* 阿里云短信配置
*
* @author Ray
* @since 2024/8/17
*/
@Configuration
@ConfigurationProperties(prefix = "sms.aliyun")
@Data
public class AliyunSmsProperties {
/**
* 阿里云账户的Access Key ID用于API请求认证
*/
private String accessKeyId;
/**
*阿里云账户的Access Key Secret用于API请求认证
*/
private String accessKeySecret;
/**
* 阿里云短信服务API的域名 eg: dysmsapi.aliyuncs.com
*/
private String domain;
/**
* 阿里云服务的区域ID如cn-shanghai
*/
private String regionId;
/**
* 短信签名,必须是已经在阿里云短信服务中注册并通过审核的
*/
private String signName;
/**
* 模板编码
*/
private Map<String, String> templateCodes;
}

View File

@@ -0,0 +1,92 @@
package com.youlai.boot.config.property;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 验证码 属性配置
*
* @author haoxr
* @since 2023/11/24
*/
@Component
@ConfigurationProperties(prefix = "captcha")
@Data
public class CaptchaProperties {
/**
* 验证码类型 circle-圆圈干扰验证码|gif-Gif验证码|line-干扰线验证码|shear-扭曲干扰验证码
*/
private String type;
/**
* 验证码图片宽度
*/
private int width;
/**
* 验证码图片高度
*/
private int height;
/**
* 干扰线数量
*/
private int interfereCount;
/**
* 文本透明度
*/
private Float textAlpha;
/**
* 验证码过期时间,单位:秒
*/
private Long expireSeconds;
/**
* 验证码字符配置
*/
private CodeProperties code;
/**
* 验证码字体
*/
private FontProperties font;
/**
* 验证码字符配置
*/
@Data
public static class CodeProperties {
/**
* 验证码字符类型 math-算术|random-随机字符串
*/
private String type;
/**
* 验证码字符长度type=算术时,表示运算位数(1:个位数 2:十位数)type=随机字符时,表示字符个数
*/
private int length;
}
/**
* 验证码字体配置
*/
@Data
public static class FontProperties {
/**
* 字体名称
*/
private String name;
/**
* 字体样式 0-普通|1-粗体|2-斜体
*/
private int weight;
/**
* 字体大小
*/
private int size;
}
}

View File

@@ -0,0 +1,82 @@
package com.youlai.boot.config.property;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.map.MapUtil;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* 代码生成配置属性
*
* @author Ray
* @since 2.11.0
*/
@Component
@ConfigurationProperties(prefix = "generator")
@Data
public class GeneratorProperties {
/**
* 默认配置
*/
private DefaultConfig defaultConfig ;
/**
* 模板配置
*/
private Map<String, TemplateConfig> templateConfigs = MapUtil.newHashMap(true);
/**
* 后端应用名
*/
private String backendAppName;
/**
* 前端应用名
*/
private String frontendAppName;
/**
* 下载文件名
*/
private String downloadFileName;
/**
* 排除数据表
*/
private List<String> excludeTables;
/**
* 模板配置
*/
@Data
public static class TemplateConfig {
private String templatePath;
private String subpackageName;
/**
* 文件扩展名,如 .java
*/
private String extension = FileNameUtil.EXT_JAVA;
}
/**
* 默认配置
*/
@Data
public static class DefaultConfig {
private String author;
}
}

View File

@@ -0,0 +1,89 @@
package com.youlai.boot.config.property;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 邮件配置类,用于接收和存储邮件相关的配置属性。
*
* @author Ray
* @since 2024/8/17
*/
@ConfigurationProperties(prefix = "spring.mail")
@Data
public class MailProperties {
/**
* 邮件服务器主机名或 IP 地址。
* 例如smtp.example.com
*/
private String host;
/**
* 邮件服务器端口号。
* 例如587
*/
private int port;
/**
* 用于连接邮件服务器的用户名。
* 例如your_email@example.com
*/
private String username;
/**
* 用于连接邮件服务器的密码。
* 该密码应安全存储,不应在代码中硬编码。
*/
private String password;
/**
* 邮件发送者地址。
*/
private String from;
/**
* 邮件服务器的其他属性配置。
* 这些配置通常用于进一步定制邮件发送行为。
*/
private Properties properties = new Properties();
/**
* 内部类,用于封装邮件服务器的详细配置。
* 包含 SMTP 相关的配置选项。
*/
@Data
public static class Properties {
/**
* SMTP 配置选项类。
* 包含认证、加密等与 SMTP 协议相关的配置。
*/
private Smtp smtp = new Smtp();
@Data
public static class Smtp {
/**
* 是否启用 SMTP 认证。
* 如果为 `true`,则需要提供有效的用户名和密码进行认证。
*/
private boolean auth;
/**
* STARTTLS 加密配置选项。
*/
private StartTls starttls = new StartTls();
@Data
public static class StartTls {
/**
* 是否启用 STARTTLS 加密。
* 如果为 `true`,在发送邮件时将启用 STARTTLS 协议进行加密传输。
*/
private boolean enable;
}
}
}
}

View File

@@ -0,0 +1,44 @@
package com.youlai.boot.config.property;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* @author haoxr
* @since 2024/4/18
*/
@Data
@ConfigurationProperties(prefix = "security")
public class SecurityProperties {
/**
* 白名单 URL 集合
*/
private List<String> ignoreUrls;
/**
* JWT 配置
*/
private JwtProperty jwt;
/**
* JWT 配置
*/
@Data
public static class JwtProperty {
/**
* JWT 密钥
*/
private String key;
/**
* JWT 过期时间
*/
private Long ttl;
}
}