From cbd8032d93bf92a158f544318d2829cb3e2d7e3a Mon Sep 17 00:00:00 2001 From: gitee Date: Wed, 9 Jul 2025 15:52:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=20-=20=E5=8A=A0=E5=85=A5=20mybatis=20-=20=E5=8A=A0=E5=85=A5=20?= =?UTF-8?q?springSecurity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/encodings.xml | 7 + .idea/misc.xml | 1 - .idea/uiDesigner.xml | 124 +++++++++++++++++ pom.xml | 125 ++++++++++++++++- src/main/java/com/supervision/Main.java | 7 - .../com/supervision/PlatformApplication.java | 15 ++ .../config/ExceptionHandlerConfig.java | 63 +++++++++ .../config/MyMetaObjectHandler.java | 26 ++++ .../supervision/config/MybatisPlusConfig.java | 45 ++++++ .../supervision/config/SecurityConfig.java | 82 +++++++++++ .../constant/ResultStatusEnum.java | 55 ++++++++ src/main/java/com/supervision/dto/R.java | 131 ++++++++++++++++++ .../exception/BusinessException.java | 76 ++++++++++ .../filter/JwtAuthenticationFilter.java | 62 +++++++++ .../com/supervision/mapper/SysUserMapper.java | 18 +++ .../supervision/service/SysUserService.java | 16 +++ .../service/impl/SysUserServiceImpl.java | 30 ++++ .../service/impl/UserDetailsServiceImpl.java | 38 +++++ .../java/com/supervision/util/JwtUtils.java | 60 ++++++++ src/main/resources/application.yml | 24 ++++ src/main/resources/logback-spring.xml | 69 +++++++++ src/main/resources/mapper/SysUserMapper.xml | 24 ++++ 22 files changed, 1089 insertions(+), 9 deletions(-) create mode 100644 .idea/encodings.xml create mode 100644 .idea/uiDesigner.xml delete mode 100644 src/main/java/com/supervision/Main.java create mode 100644 src/main/java/com/supervision/PlatformApplication.java create mode 100644 src/main/java/com/supervision/config/ExceptionHandlerConfig.java create mode 100644 src/main/java/com/supervision/config/MyMetaObjectHandler.java create mode 100644 src/main/java/com/supervision/config/MybatisPlusConfig.java create mode 100644 src/main/java/com/supervision/config/SecurityConfig.java create mode 100644 src/main/java/com/supervision/constant/ResultStatusEnum.java create mode 100644 src/main/java/com/supervision/dto/R.java create mode 100644 src/main/java/com/supervision/exception/BusinessException.java create mode 100644 src/main/java/com/supervision/filter/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/supervision/mapper/SysUserMapper.java create mode 100644 src/main/java/com/supervision/service/SysUserService.java create mode 100644 src/main/java/com/supervision/service/impl/SysUserServiceImpl.java create mode 100644 src/main/java/com/supervision/service/impl/UserDetailsServiceImpl.java create mode 100644 src/main/java/com/supervision/util/JwtUtils.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/mapper/SysUserMapper.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 82dbec8..c3f3b0a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 805608c..bd1ebf9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,6 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.4.4 + + com.supervision ai-demo-platform @@ -12,6 +18,123 @@ 17 17 UTF-8 + 1.0.0-M7 - + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-aop + + + com.alibaba + druid-spring-boot-3-starter + 1.2.21 + + + com.baomidou + mybatis-plus-spring-boot3-starter + 3.5.7 + + + com.baomidou + mybatis-plus-boot-starter + 3.5.5 + + + org.postgresql + postgresql + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + cn.hutool + hutool-all + 5.8.26 + + + com.alibaba + fastjson + 1.2.83_noneautotype + + + com.fasterxml.jackson.core + jackson-core + 2.15.3 + + + com.fasterxml.jackson.core + jackson-databind + 2.15.3 + + + org.commonmark + commonmark + 0.21.0 + + + org.commonmark + commonmark-ext-gfm-tables + 0.21.0 + + + io.jsonwebtoken + jjwt-api + 0.12.5 + + + io.jsonwebtoken + jjwt-impl + 0.12.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.12.5 + runtime + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + + \ No newline at end of file diff --git a/src/main/java/com/supervision/Main.java b/src/main/java/com/supervision/Main.java deleted file mode 100644 index f7ee142..0000000 --- a/src/main/java/com/supervision/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.supervision; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world!"); - } -} \ No newline at end of file diff --git a/src/main/java/com/supervision/PlatformApplication.java b/src/main/java/com/supervision/PlatformApplication.java new file mode 100644 index 0000000..9731ae8 --- /dev/null +++ b/src/main/java/com/supervision/PlatformApplication.java @@ -0,0 +1,15 @@ +package com.supervision; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true) +@MapperScan(basePackages = {"com.supervision.**.mapper"}) +@SpringBootApplication +public class PlatformApplication { + public static void main(String[] args) { + SpringApplication.run(PlatformApplication.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/supervision/config/ExceptionHandlerConfig.java b/src/main/java/com/supervision/config/ExceptionHandlerConfig.java new file mode 100644 index 0000000..c3fe1ed --- /dev/null +++ b/src/main/java/com/supervision/config/ExceptionHandlerConfig.java @@ -0,0 +1,63 @@ +package com.supervision.config; + +import com.supervision.constant.ResultStatusEnum; +import com.supervision.dto.R; +import com.supervision.exception.BusinessException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; + +/** + * 统一异常处理器配置 + * + * @author wb + * @date 2022/3/10 13:24 + */ +@Slf4j +@Configuration +@RestControllerAdvice(annotations = RestController.class, basePackages = {"com.supervision.**.controller"}) +public class ExceptionHandlerConfig { + + /** + * 添加手动校验参数的异常处理 + * + * @param exception 参数验证异常 + * @return 通用返回值 + */ + @ExceptionHandler(IllegalArgumentException.class) + public R manualValidationExceptionResponse(IllegalArgumentException exception) { + log.error("=========手动校验参数异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========手动校验参数异常========="); + return R.fail(ResultStatusEnum.ILLEGAL_ARGUMENT.getCode(), exception.getMessage()); + } + + @ExceptionHandler(BusinessException.class) + public R businessExceptionResponse(BusinessException exception) { + log.error("=========运行异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========运行异常========="); + + return R.fail(511, exception.getMessage()); + } + + @ExceptionHandler(RuntimeException.class) + public R manualValidationExceptionResponse(RuntimeException exception) { + log.error("=========运行异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========运行异常========="); + + return R.fail(ResultStatusEnum.RUNTIME_EXCEPTION.getCode(), exception.getMessage()); + } + + @ExceptionHandler(MaxUploadSizeExceededException.class) + public R handleMaxSizeException(MaxUploadSizeExceededException exception) { + log.error("=========文件大小超出限制异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========文件大小超出限制异常========="); + return R.fail(ResultStatusEnum.EXCEED_FILE_SIZE.getCode(), exception.getMessage()); + } +} diff --git a/src/main/java/com/supervision/config/MyMetaObjectHandler.java b/src/main/java/com/supervision/config/MyMetaObjectHandler.java new file mode 100644 index 0000000..28ac192 --- /dev/null +++ b/src/main/java/com/supervision/config/MyMetaObjectHandler.java @@ -0,0 +1,26 @@ +package com.supervision.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; + +import java.time.LocalDateTime; + +/** + * @author Ray + */ +public class MyMetaObjectHandler implements MetaObjectHandler { + public MyMetaObjectHandler() { + } + + @Override + public void insertFill(MetaObject metaObject) { + this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); + this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); + } + + @Override + public void updateFill(MetaObject metaObject) { + this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); + } + +} \ No newline at end of file diff --git a/src/main/java/com/supervision/config/MybatisPlusConfig.java b/src/main/java/com/supervision/config/MybatisPlusConfig.java new file mode 100644 index 0000000..67a3759 --- /dev/null +++ b/src/main/java/com/supervision/config/MybatisPlusConfig.java @@ -0,0 +1,45 @@ +package com.supervision.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * MybatisPlus配置 + * + * @author qmy + * @version 1.0.0 2020/10/22 9:47 + * @since JDK1.8 + */ +@Configuration +public class MybatisPlusConfig { + + @Bean + public MyMetaObjectHandler myMetaObjectHandler() { + return new MyMetaObjectHandler(); + } + + /** + * 拦截器配置 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(this.paginationInterceptor()); + return interceptor; + } + + + private PaginationInnerInterceptor paginationInterceptor() { + PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(); + paginationInterceptor.setOverflow(false); + /** + * 注意! 此处要设置数据库类型. + */ + paginationInterceptor.setDbType(DbType.POSTGRE_SQL); + return paginationInterceptor; + } +} diff --git a/src/main/java/com/supervision/config/SecurityConfig.java b/src/main/java/com/supervision/config/SecurityConfig.java new file mode 100644 index 0000000..d0d4884 --- /dev/null +++ b/src/main/java/com/supervision/config/SecurityConfig.java @@ -0,0 +1,82 @@ +package com.supervision.config; + +import com.supervision.filter.JwtAuthenticationFilter; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.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 java.util.Base64; + + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final UserDetailsService userDetailsService; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) // 禁用CSRF + .authorizeHttpRequests(auth -> auth + .requestMatchers("/auth/**").permitAll() + .anyRequest().authenticated() + ) + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .authenticationProvider(authenticationProvider()) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + // 👇 正确禁用方式(推荐) + .formLogin(Customizer.withDefaults()) // 启用默认配置 + .httpBasic(Customizer.withDefaults()); // 启用默认配置 + return http.build(); + } + + + @Bean + public AuthenticationProvider authenticationProvider() { + // 使用DaoAuthenticationProvider,并注入自定义的UserDetailsService和PasswordEncoder + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); // 从数据库读取用户进行认证 + authProvider.setPasswordEncoder(passwordEncoder()); // 使用BCrypt密码器验证密码 + return authProvider; + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + // 从AuthenticationConfiguration中获取AuthenticationManager实例 + return config.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + // 使用BCryptPasswordEncoder作为密码加密器 + return new BCryptPasswordEncoder(); + } + + public static void main(String[] args) { + String s = Base64.getEncoder().encodeToString(Keys.secretKeyFor(SignatureAlgorithm.HS256).getEncoded()); + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + String encode = bCryptPasswordEncoder.encode("123456"); + System.out.println(encode); + } + +} diff --git a/src/main/java/com/supervision/constant/ResultStatusEnum.java b/src/main/java/com/supervision/constant/ResultStatusEnum.java new file mode 100644 index 0000000..91859f7 --- /dev/null +++ b/src/main/java/com/supervision/constant/ResultStatusEnum.java @@ -0,0 +1,55 @@ +package com.supervision.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 响应结果状态枚举类 + * @author qimaoyu + * @create 2019-07-14 10:22 + */ +@NoArgsConstructor +@AllArgsConstructor +public enum ResultStatusEnum { + + AUTHENTICATION_FAILED(320, "token失效,请重新登录!"), + NO_ACCESS_TO_THIS_INTERFACE(320, "无权访问此接口!"), + FAILED_TO_GENERATE_TOKEN(321, "生成token失败!"), + ACCOUNT_PASSWORD_INCORRECT(322, "账号或密码错误!"), + ACCOUNT_NOT_CREATE(323, "账号未创建!"), + HAS_BEEN_PULLED_BLACK(324, "已被删除或禁用,无法登录!"), + USERNAME_MAIL_IS_EXIST(341, "登录名称已经被注册!"), + USERNAME_IS_BLANK(342, "登录名称为空!"), + VERIFICATION_CODE_EXPIRED(350,"验证码已过期,请重新获取。"), + VERIFICATION_CODE_FAILURE(351,"验证码输入错误。"), + OPERATE_FAIL(360,"修改毕业生信息失败。"), + DATA_IS_EMPTY(370,"查询到的结果为空"), + SYSTEM_ABNORMAL(500, "系统繁忙,请稍后重试!"), + UPLOAD_EXCEPTION(501, "文件上传异常!"), + EXPORT_EXCEPTION(502, "文件导出异常!"), + INCORRECT_FILE_FORMAT(503, "文件格式不正确!"), + PARAMETER_CANNOT_BE_EMPTY(504, "参数不能为空,操作失败!"), + NO_TEMP_UPLOADFILEPATH(505,"未配置文件上传临时存储路径"), + USER_DOES_NOT_EXIST(507, "用户不存在,操作失败!"), + + ILLEGAL_ARGUMENT(508, "参数校验失败!"), + RUNTIME_EXCEPTION(509, "程序运行异常!"), + EXCEED_FILE_SIZE(510, "文件大小超出限制!"), + IMPORT_COMPANY_FORMAT_ERROR(521,"Excel表格格式错误!"), + IMPORT_COMPANY_FAIL(522,"部分数据导入失败"), + INSERT_FAIL(600,"新增失败"), + DuplicateKeyException(601,"该条信息已经存在,请勿重复添加"), + UPDATE_FAIL(700,"更新失败"), + DELETE_FAIL(800,"删除失败"), + YEAR_IS_CLOSE(1001,"该年度暂未开启"); + + @Getter + @Setter + private int code; + + @Getter + @Setter + private String message; +} diff --git a/src/main/java/com/supervision/dto/R.java b/src/main/java/com/supervision/dto/R.java new file mode 100644 index 0000000..29c3270 --- /dev/null +++ b/src/main/java/com/supervision/dto/R.java @@ -0,0 +1,131 @@ +package com.supervision.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.*; + +/** + * 响应信息主体 + * + * @author qimaoyu + */ +@Data +public class R implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String TOTAL_COUNT = "total"; + public static final String RESULT_LIST = "result"; + + /** + * 成功 + */ + public static final int SUCCESS = 200; + + /** + * 失败 + */ + public static final int FAIL = 500; + + private int code; + + private String msg; + + private T data; + + public static R ok() { + return restResult(null, SUCCESS, null); + } + + public static R judgeResult(Boolean bo, String successMessage, String failMessage) { + if (bo) { + return restResult(null, SUCCESS, successMessage); + } else { + return restResult(null, FAIL, failMessage); + } + } + + public static R okMsg(String msg) { + return restResult(null, SUCCESS, msg); + } + + public static R ok(T data) { + return restResult(data, SUCCESS, null); + } + + public static R ok(T data, String msg) { + return restResult(data, SUCCESS, msg); + } + + public static R fail() { + return restResult(null, FAIL, null); + } + + public static R fail(String msg) { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) { + return restResult(data, FAIL, null); + } + + public static R fail(int code, String msg) { + return restResult(null, code, msg); + } + + + + private static R restResult(T data, int code, String msg) { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + + public static Map buildDataMap(List list) { + Map dataMap = new HashMap<>(); + if (list == null) { + dataMap.put(TOTAL_COUNT, 0); + dataMap.put(RESULT_LIST, new ArrayList<>()); + } else { + dataMap.put(TOTAL_COUNT, list.size()); + dataMap.put(RESULT_LIST, list); + } + return dataMap; + } + + public static Map buildDataMap(List list, Long total) { + Map dataMap = new HashMap<>(); + dataMap.put(TOTAL_COUNT, total); + dataMap.put(RESULT_LIST, list); + return dataMap; + } + + public static Map buildDataMap(Set list) { + Map dataMap = new HashMap<>(); + if (list == null) { + dataMap.put(TOTAL_COUNT, 0); + dataMap.put(RESULT_LIST, new ArrayList<>()); + } else { + dataMap.put(TOTAL_COUNT, list.size()); + dataMap.put(RESULT_LIST, list); + } + return dataMap; + } + + public static Map buildDataMap(Object object) { + if (object == null) { + return null; + } + List resultList = new ArrayList<>(); + resultList.add(object); + Map dataMap = new HashMap<>(); + dataMap.put(TOTAL_COUNT, resultList.size()); + dataMap.put(RESULT_LIST, resultList); + return dataMap; + } + +} diff --git a/src/main/java/com/supervision/exception/BusinessException.java b/src/main/java/com/supervision/exception/BusinessException.java new file mode 100644 index 0000000..53d07fb --- /dev/null +++ b/src/main/java/com/supervision/exception/BusinessException.java @@ -0,0 +1,76 @@ +/* + * 文 件 名: CustomException + * 版 权: + * 描 述: <描述> + * 修 改 人: RedName + * 修改时间: 2022/8/5 + * 跟踪单号: <跟踪单号> + * 修改单号: <修改单号> + * 修改内容: <修改内容> + */ +package com.supervision.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; + +/** + * <功能详细描述> + * 自定义异常 + * + * @author ljt + * @version [版本号, 2022/8/5] + * @see [相关类/方法] + * @since [产品/模块版本] + */ +@Slf4j +public class BusinessException extends RuntimeException { + /** + * 异常编码 + */ + private final Integer code; + + /** + * 异常信息 + */ + private final String message; + + public BusinessException(Throwable cause) { + super(cause); + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = null; + + } + + public BusinessException(Throwable cause, String message) { + super(cause); + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = message; + + } + + public BusinessException(String message) { + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = message; + } + + public BusinessException(String message, Integer code) { + this.message = message; + this.code = code; + } + + public BusinessException(String message, Throwable e) { + super(message, e); + log.error(message, e); + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public Integer getCode() { + return code; + } +} diff --git a/src/main/java/com/supervision/filter/JwtAuthenticationFilter.java b/src/main/java/com/supervision/filter/JwtAuthenticationFilter.java new file mode 100644 index 0000000..f68ca84 --- /dev/null +++ b/src/main/java/com/supervision/filter/JwtAuthenticationFilter.java @@ -0,0 +1,62 @@ +package com.supervision.filter; + +import com.supervision.util.JwtUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +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; + + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtUtils jwtUtils; + private final UserDetailsService userDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws ServletException, IOException { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null && authHeader.startsWith("Bearer ")) { + // 提取JWT Token(去掉前缀"Bearer ") + String token = authHeader.substring(7); + String username; + try { + // 从JWT中解析用户名 + username = jwtUtils.getUsernameFromToken(token); + } catch (Exception e) { + // 如果JWT格式不正确或过期,直接放行(后续的过滤器会处理认证失败) + filterChain.doFilter(request, response); + return; + } + // 如果成功提取到用户名,并且当前没有已认证的用户 + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + // 根据用户名从数据库加载用户信息 + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + // 验证Token的有效性(是否未过期) + if (!jwtUtils.isTokenExpired(token)) { + // 将用户信息封装到Authentication对象中,标记为已认证 + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + // 将Authentication对象放入SecurityContext,表示当前请求已通过认证 + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + } + filterChain.doFilter(request, response); + } + +} diff --git a/src/main/java/com/supervision/mapper/SysUserMapper.java b/src/main/java/com/supervision/mapper/SysUserMapper.java new file mode 100644 index 0000000..cf0e4ad --- /dev/null +++ b/src/main/java/com/supervision/mapper/SysUserMapper.java @@ -0,0 +1,18 @@ +package com.supervision.mapper; + +import com.supervision.domain.SysUser; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【sys_user】的数据库操作Mapper +* @createDate 2025-07-09 15:19:45 +* @Entity com.supervision.domain.SysUser +*/ +public interface SysUserMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/supervision/service/SysUserService.java b/src/main/java/com/supervision/service/SysUserService.java new file mode 100644 index 0000000..95f2698 --- /dev/null +++ b/src/main/java/com/supervision/service/SysUserService.java @@ -0,0 +1,16 @@ +package com.supervision.service; + +import com.supervision.domain.SysUser; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【sys_user】的数据库操作Service +* @createDate 2025-07-09 15:19:45 +*/ +public interface SysUserService extends IService { + + + SysUser getByUsername(String username); + +} diff --git a/src/main/java/com/supervision/service/impl/SysUserServiceImpl.java b/src/main/java/com/supervision/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..1f2b886 --- /dev/null +++ b/src/main/java/com/supervision/service/impl/SysUserServiceImpl.java @@ -0,0 +1,30 @@ +package com.supervision.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.domain.SysUser; +import com.supervision.service.SysUserService; +import com.supervision.mapper.SysUserMapper; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【sys_user】的数据库操作Service实现 +* @createDate 2025-07-09 15:19:45 +*/ +@Service +public class SysUserServiceImpl extends ServiceImpl + implements SysUserService{ + + @Override + public SysUser getByUsername(String username) { + if (StrUtil.isEmpty(username)) { + return null; + } + return this.lambdaQuery().eq(SysUser::getUserName, username).one(); + } +} + + + + diff --git a/src/main/java/com/supervision/service/impl/UserDetailsServiceImpl.java b/src/main/java/com/supervision/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..55436fb --- /dev/null +++ b/src/main/java/com/supervision/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,38 @@ +package com.supervision.service.impl; + +import com.supervision.domain.SysUser; +import com.supervision.service.SysUserService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class UserDetailsServiceImpl implements UserDetailsService { + + private final SysUserService sysUserService; + + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + + + // 将查询到的用户信息组装成UserDetails对象 + // **扩展点**:如需加载用户角色权限,可在此处查询 sys_user_role 表关联的角色,并将角色加入 authorities 列表 + List authorities = Collections.emptyList(); + // 使用Spring Security提供的User对象作为UserDetails返回 + SysUser sysUser = sysUserService.getByUsername(username); + if (sysUser == null) { + throw new UsernameNotFoundException("用户不存在: " + username); + } + return new User(sysUser.getUserName(), sysUser.getPassword(), authorities); + } +} diff --git a/src/main/java/com/supervision/util/JwtUtils.java b/src/main/java/com/supervision/util/JwtUtils.java new file mode 100644 index 0000000..d3636aa --- /dev/null +++ b/src/main/java/com/supervision/util/JwtUtils.java @@ -0,0 +1,60 @@ +package com.supervision.util; + + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.Base64; +import java.util.Date; + +@Component +public class JwtUtils { + + @Value("${jwt.secret}") + private String Secret; + + @Value("${jwt.expiration}") + private long Expiration; // 1天 + + private SecretKey secretKey; + + @PostConstruct + public void initKey() { + this.secretKey = Keys.hmacShaKeyFor(Base64.getDecoder().decode(Secret)); + } + + // 生成Token + public String generateToken(UserDetails userDetails) { + return Jwts.builder() + .subject(userDetails.getUsername()) + .issuedAt(new Date()) + .expiration(new Date(System.currentTimeMillis() + Expiration)) + .signWith(secretKey, Jwts.SIG.HS256) + .compact(); + } + + // 从 Token 中解析用户名 + public String getUsernameFromToken(String token) { + Jws jws = Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token); + return jws.getPayload().getSubject(); + } + + // 判断是否过期 + public boolean isTokenExpired(String token) { + Jws jws = Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token); + return jws.getPayload().getExpiration().before(new Date()); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..babaa9e --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,24 @@ +server: + port: 9908 + servlet: + context-path: /ai-platform +spring: + application: + name: pdf-qa-server + datasource: + druid: + url: jdbc:postgresql://192.168.10.137:54321/ai-demo-platform + username: postgres + password: 123456 + driver-class-name: org.postgresql.Driver + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +jwt: + secret: "DlHaPUePiN6MyvpMpsMq/t6swzMHqtrRFd2YnofKz4k=" # JWT密钥 使用官方推荐方式生成 Base64.getEncoder().encodeToString(Keys.secretKeyFor(SignatureAlgorithm.HS256).getEncoded()); + expiration: 3600000 # 1小时:3600000 1天:86400000 \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..36f9143 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n + UTF-8 + + + + + + ${LOG_FILE} + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + UTF-8 + + + ${LOG_PATH}/application/application.%d{yyyy-MM-dd}.log + 30 + 100MB + + + + + + ${LOG_PATH}/error/error.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + UTF-8 + + + ${LOG_PATH}/error/error.%d{yyyy-MM-dd}.log + 30 + 100MB + + + ERROR + + + + + + + ${LOG_PATH}/AOP_LOG/AOP_LOG.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + UTF-8 + + + ${LOG_PATH}/AOP_LOG/AOP_LOG.%d{yyyy-MM-dd}.log + 30 + 100MB + + + + + + + + + + diff --git a/src/main/resources/mapper/SysUserMapper.xml b/src/main/resources/mapper/SysUserMapper.xml new file mode 100644 index 0000000..f843e1a --- /dev/null +++ b/src/main/resources/mapper/SysUserMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + id,app_id,user_name, + password,status,create_user_id, + create_time,update_time,update_user_id + +