From fce631eb3cb6f7777a9788a16b677786b283a0bd Mon Sep 17 00:00:00 2001 From: cs <2268380485@qq.com> Date: Tue, 29 Apr 2025 18:29:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=E6=B7=BB=E5=8A=A0annotation?= =?UTF-8?q?,aspect,config=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hfbu/recruit/annotation/SwitchMethod.java | 24 +++++ .../edu/hfbu/recruit/aspect/MethodSwitch.java | 78 ++++++++++++++++ .../hfbu/recruit/config/JwtRequestFilter.java | 80 +++++++++++++++++ .../edu/hfbu/recruit/config/Security.java | 89 +++++++++++++++++++ .../edu/hfbu/recruit/config/WebConfig.java | 41 +++++++++ .../config/jwt/AuthenticationRequest.java | 30 +++++++ .../config/jwt/AuthenticationResponse.java | 21 +++++ 7 files changed, 363 insertions(+) create mode 100644 src/main/java/edu/hfbu/recruit/annotation/SwitchMethod.java create mode 100644 src/main/java/edu/hfbu/recruit/aspect/MethodSwitch.java create mode 100644 src/main/java/edu/hfbu/recruit/config/JwtRequestFilter.java create mode 100644 src/main/java/edu/hfbu/recruit/config/Security.java create mode 100644 src/main/java/edu/hfbu/recruit/config/WebConfig.java create mode 100644 src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationRequest.java create mode 100644 src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationResponse.java diff --git a/src/main/java/edu/hfbu/recruit/annotation/SwitchMethod.java b/src/main/java/edu/hfbu/recruit/annotation/SwitchMethod.java new file mode 100644 index 0000000..c969647 --- /dev/null +++ b/src/main/java/edu/hfbu/recruit/annotation/SwitchMethod.java @@ -0,0 +1,24 @@ +package edu.ahbvc.recruit.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 该注解可以用于方法上, + * 表示该方法可以被用于切换功能启用或禁用的方法 + * 该注解的value属性用于指定功能类型的字符串值 + * 例如:@SwitchMethod("register") + * @author c215 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +// 该注解可以用于方法上, +// 表示该方法可以被用于切换功能启用或禁用的方法 +// 该注解的value属性用于指定功能类型的字符串值 +// 例如:@SwitchMethod("register") +public @interface SwitchMethod { + // 用于指定功能类型的字符串值 + String value() default ""; +} diff --git a/src/main/java/edu/hfbu/recruit/aspect/MethodSwitch.java b/src/main/java/edu/hfbu/recruit/aspect/MethodSwitch.java new file mode 100644 index 0000000..e898fe7 --- /dev/null +++ b/src/main/java/edu/hfbu/recruit/aspect/MethodSwitch.java @@ -0,0 +1,78 @@ +package edu.ahbvc.recruit.aspect; + +import edu.ahbvc.recruit.annotation.SwitchMethod; +import edu.ahbvc.recruit.model.ApiResponseData; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author c215 + */ +@Aspect +@Component +public class MethodSwitch { + // 使用常量定义功能类型 + // 注册 + public static final String REGISTER = "register"; + // 提交简历 + public static final String SUBMIT = "submit"; + // 尝试重新提交 + public static final String TRY_SUBMIT = "trySubmit"; + // 放弃投递 + public static final String ABANDON = "abandon"; + + // 使用带有默认值的线程安全的Map存储开关状态 + private static final Map METHOD_SWITCHES = new ConcurrentHashMap<>(); + + // 静态代码块,在类加载时执行,用于初始化开关状态 + static { + // 默认情况下,所有功能都启用 + METHOD_SWITCHES.put(REGISTER, true); + METHOD_SWITCHES.put(SUBMIT, true); + METHOD_SWITCHES.put(TRY_SUBMIT, true); + METHOD_SWITCHES.put(ABANDON, true); + } + + // 统一设置方法 + // 通过此方法可以动态地设置功能的开关状态 + public static void setSwitch(String methodType, boolean enabled) { + // 检查方法类型是否存在 + if (!METHOD_SWITCHES.containsKey(methodType)) { + throw new IllegalArgumentException("Invalid method type: " + methodType); + } + // 设置功能的开关状态 + METHOD_SWITCHES.put(methodType, enabled); + } + + // 统一获取方法 + // 通过此方法可以获取功能的开关状态 + public static boolean isEnabled(String methodType) { + // 检查方法类型是否存在 + if (!METHOD_SWITCHES.containsKey(methodType)) { + throw new IllegalArgumentException("Invalid method type: " + methodType); + } + // 返回功能的开关状态 + return METHOD_SWITCHES.getOrDefault(methodType, false); + } + + // 统一切面处理 + @Around("@annotation(switchMethod)") + public Object controlMethod(ProceedingJoinPoint joinPoint, SwitchMethod switchMethod) throws Throwable { + // 获取方法的功能类型 + String methodType = switchMethod.value(); + // 检查功能是否启用 + if (!METHOD_SWITCHES.getOrDefault(methodType, false)) { + // 如果功能未启用,则返回错误信息 + return new ApiResponseData<>("500", null, "此功能已关闭"); + } + // 如果功能已启用,则执行原方法 + return joinPoint.proceed(); + } +} + + diff --git a/src/main/java/edu/hfbu/recruit/config/JwtRequestFilter.java b/src/main/java/edu/hfbu/recruit/config/JwtRequestFilter.java new file mode 100644 index 0000000..2ef0eb3 --- /dev/null +++ b/src/main/java/edu/hfbu/recruit/config/JwtRequestFilter.java @@ -0,0 +1,80 @@ +package edu.ahbvc.recruit.config; + +import edu.ahbvc.recruit.util.JwtUtil; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * JWT请求过滤器 + * 用于处理JWT认证请求 + * 继承自OncePerRequestFilter, 确保每个请求只被过滤一次 + * 实现了doFilterInternal方法, 用于处理请求的认证逻辑 + * 从请求头中获取JWT令牌, 并验证令牌的有效性 + * 如果令牌有效, 则将用户信息设置到SecurityContextHolder中, 以便后续的认证和授权操作 + * 如果令牌无效, 则不做任何操作 + * 因为JWT相关代码暂未更新, 所以此类用不到 + * 更新了之后代码量更少了,所以屎山了, 但是功能是有的, 所以就先这样了, 以后再优化 + * @author c215 + */ + +// * JWT请求过滤器 +// * 用于处理JWT认证请求 +// * 继承自OncePerRequestFilter, 确保每个请求只被过滤一次 +// * 实现了doFilterInternal方法, 用于处理请求的认证逻辑 +// * 从请求头中获取JWT令牌, 并验证令牌的有效性 +// * 如果令牌有效, 则将用户信息设置到SecurityContextHolder中, 以便后续的认证和授权操作 +// * 如果令牌无效, 则不做任何操作 +// * 因为JWT相关代码暂未更新, 所以此类用不到 +// * 更新了之后代码量更少了,所以屎山了, 但是功能是有的, 所以就先这样了, 以后再优化 + +@Component +public class JwtRequestFilter { +// extends OncePerRequestFilter +// @Autowired +// private UserDetailsService userDetailsService; +// +// @Autowired +// private JwtUtil jwtUtil; +// +// @Override +// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) +// throws ServletException, IOException { +// +// final String authorizationHeader = request.getHeader("Authorization"); +// +// String username = null; +// String jwt = null; +// +// if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { +// jwt = authorizationHeader.substring(7); +// username = jwtUtil.extractUsername(jwt); +// } +// +// if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { +// +// UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); +// +// if (jwtUtil.validateToken(jwt, userDetails)) { +// +// UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( +// userDetails, null, userDetails.getAuthorities()); +// usernamePasswordAuthenticationToken +// .setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); +// SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); +// } +// } +// chain.doFilter(request, response); +// } +} \ No newline at end of file diff --git a/src/main/java/edu/hfbu/recruit/config/Security.java b/src/main/java/edu/hfbu/recruit/config/Security.java new file mode 100644 index 0000000..67fe3bb --- /dev/null +++ b/src/main/java/edu/hfbu/recruit/config/Security.java @@ -0,0 +1,89 @@ +package edu.ahbvc.recruit.config; + + +import edu.ahbvc.recruit.mapper.UserInter; +import edu.ahbvc.recruit.model.token.CustomUserDetailsService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.CachingUserDetailsService; +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.core.userdetails.UserDetailsService; +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 org.springframework.web.cors.CorsConfigurationSource; + +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import static org.springframework.security.config.Customizer.withDefaults; +import java.util.Arrays; +import java.util.Collections; + +/** + * Spring Security配置类 + * @author c215 + */ +// @Configuration 表示这是一个配置类 +@Configuration +// @EnableWebSecurity 表示启用Spring Security的Web安全支持 +@EnableWebSecurity +public class Security { + // SecurityFilterChain 用于配置安全过滤器链 + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .cors(withDefaults()) + .authorizeHttpRequests((requests)->requests + .requestMatchers("/","/login","/favicon.ico","/**").permitAll() + .anyRequest().authenticated() + + ) + .formLogin((form)-> form + .loginPage("/login.html") + .permitAll() + ); + return http.build(); + } + + + // CORS 配置源 用于配置跨域请求的CORS配置 + // 正式上线后需要修改为允许指定的域名和端口 + @Bean + CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + // 允许所有本地地址和端口 + configuration.setAllowedOriginPatterns(Arrays.asList( + "http://localhost:*", + "http://127.0.0.1:*" + )); + // 允许所有请求方法 + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + // 允许所有请求头 + configuration.setAllowedHeaders(Collections.singletonList("*")); + // 允许携带凭证 + configuration.setAllowCredentials(true); + + // 注册CORS配置源 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + // 注册CORS配置源 + source.registerCorsConfiguration("/**", configuration); + return source; + } + + // UserDetailsService 用于加载用户信息 + // 因为JWT相关代码暂未更新, 所以此方法用不到 + @Bean + public UserDetailsService userDetailsService(UserInter userRepository){ + return new CustomUserDetailsService(userRepository); + } + + @Bean + public PasswordEncoder passwordEncoder(){ + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/edu/hfbu/recruit/config/WebConfig.java b/src/main/java/edu/hfbu/recruit/config/WebConfig.java new file mode 100644 index 0000000..2946e10 --- /dev/null +++ b/src/main/java/edu/hfbu/recruit/config/WebConfig.java @@ -0,0 +1,41 @@ +package edu.ahbvc.recruit.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Web配置 + * @author c215 + */ +@Configuration +public class WebConfig implements WebMvcConfigurer { + // 配置跨域请求的CORS配置 + // 正式上线后需要修改为允许指定的域名和端口 + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + // 允许所有本地地址和端口 + .allowedOrigins("http://localhost:3333") + // 允许的请求方法 + .allowedMethods("GET", "POST", "PUT", "DELETE") + // 允许的请求头 + .allowedHeaders("*") + // 允许凭证 + .allowCredentials(true) + // 预检请求的缓存时间 + .maxAge(3600); + } + + // 配置静态资源的处理 + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 配置静态资源的处理 + // 允许访问的路径 + registry.addResourceHandler("/**") + // 允许访问的路径 + .addResourceLocations("classpath:/static/"); + } +} + diff --git a/src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationRequest.java b/src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationRequest.java new file mode 100644 index 0000000..fa0bcd3 --- /dev/null +++ b/src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationRequest.java @@ -0,0 +1,30 @@ +package edu.ahbvc.recruit.config.jwt; + +/** + * 认证请求 + * 在JWT认证中,通常会使用一个认证请求对象来封装用户提交的认证信息, + * 例如用户名和密码。这个对象通常包含了用户提供的认证信息, + * 并在认证过程中被传递给认证服务进行验证。 + * 因JWT相关代码暂未更新,所以此类用不到 + * @author c215 + */ +public class AuthenticationRequest { + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationResponse.java b/src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationResponse.java new file mode 100644 index 0000000..a891e88 --- /dev/null +++ b/src/main/java/edu/hfbu/recruit/config/jwt/AuthenticationResponse.java @@ -0,0 +1,21 @@ +package edu.ahbvc.recruit.config.jwt; + +/** + * 认证响应 + * 在JWT认证中,通常会使用一个认证响应对象来封装认证成功后的响应信息, + * 例如生成的JWT令牌。这个对象通常包含了认证成功后的相关信息, + * 并在认证成功后被返回给客户端。 + * 因JWT相关代码暂未更新,所以此类用不到 + * @author c215 + */ +public class AuthenticationResponse { + private final String jwt; + + public AuthenticationResponse(String jwt) { + this.jwt = jwt; + } + + public String getJwt() { + return jwt; + } +} \ No newline at end of file