refactor: 目录结构优化
This commit is contained in:
55
src/main/java/com/youlai/boot/config/CaptchaConfig.java
Normal file
55
src/main/java/com/youlai/boot/config/CaptchaConfig.java
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
42
src/main/java/com/youlai/boot/config/CorsConfig.java
Normal file
42
src/main/java/com/youlai/boot/config/CorsConfig.java
Normal 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;
|
||||
}
|
||||
}
|
||||
51
src/main/java/com/youlai/boot/config/MailConfig.java
Normal file
51
src/main/java/com/youlai/boot/config/MailConfig.java
Normal 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;
|
||||
}
|
||||
}
|
||||
48
src/main/java/com/youlai/boot/config/MybatisConfig.java
Normal file
48
src/main/java/com/youlai/boot/config/MybatisConfig.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
74
src/main/java/com/youlai/boot/config/RedisCacheConfig.java
Normal file
74
src/main/java/com/youlai/boot/config/RedisCacheConfig.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
42
src/main/java/com/youlai/boot/config/RedisConfig.java
Normal file
42
src/main/java/com/youlai/boot/config/RedisConfig.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
110
src/main/java/com/youlai/boot/config/SecurityConfig.java
Normal file
110
src/main/java/com/youlai/boot/config/SecurityConfig.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
84
src/main/java/com/youlai/boot/config/SwaggerConfig.java
Normal file
84
src/main/java/com/youlai/boot/config/SwaggerConfig.java
Normal 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))
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
69
src/main/java/com/youlai/boot/config/WebMvcConfig.java
Normal file
69
src/main/java/com/youlai/boot/config/WebMvcConfig.java
Normal 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();
|
||||
}
|
||||
}
|
||||
108
src/main/java/com/youlai/boot/config/WebSocketConfig.java
Normal file
108
src/main/java/com/youlai/boot/config/WebSocketConfig.java
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
61
src/main/java/com/youlai/boot/config/XxlJobConfig.java
Normal file
61
src/main/java/com/youlai/boot/config/XxlJobConfig.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user