diff --git a/README.md b/README.md index de233cb3..5f308c45 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # 项目简述 -项目基于 Spring Boot 2.7 、Spring Security 5.4 、 JWT 、 Redis 、 Mybatis-Plus 、 Knife4j 等技术栈搭建的前后端分离开源权限管理系统。 +项目基于 Spring Boot 3.0 、Spring Security 6.0 、 JWT 、 Redis 、 Mybatis-Plus 、 Knife4j 等技术栈搭建的前后端分离开源权限管理系统。 ## 项目特色 -- Spring Boot 2.7 + Vue3 前后端分离单体应用,适合快速开发; +- Spring Boot 3.0 + Vue3 前后端分离单体应用,适合快速开发; - Spring Security + JWT 认证鉴权方案; - 基于 RBAC 模型的权限设计,细粒度接口方法、按钮级别权限控制。 diff --git a/pom.xml b/pom.xml index 135810a7..ce3d03db 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.6 + 3.0.2 @@ -21,14 +21,13 @@ 17 17 - 1.18.24 5.7.21 8.0.19 1.2.4 - 3.5.2 + 3.5.3 - 3.0.2 + 4.0.0 2.7.6 @@ -49,7 +48,7 @@ org.projectlombok lombok - ${lombok.version} + true @@ -98,7 +97,7 @@ com.github.xiaoymin - knife4j-spring-boot-starter + knife4j-openapi3-jakarta-spring-boot-starter ${knife4j.version} @@ -160,20 +159,20 @@ - ${project.artifactId} org.springframework.boot spring-boot-maven-plugin - ${parent.version} - - - - repackage - - - + + + + org.projectlombok + lombok + + + + \ No newline at end of file diff --git a/src/main/java/com/youlai/system/common/base/BasePageQuery.java b/src/main/java/com/youlai/system/common/base/BasePageQuery.java index 620ed156..e9c1dac8 100644 --- a/src/main/java/com/youlai/system/common/base/BasePageQuery.java +++ b/src/main/java/com/youlai/system/common/base/BasePageQuery.java @@ -1,7 +1,7 @@ package com.youlai.system.common.base; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; + +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** @@ -11,12 +11,12 @@ import lombok.Data; * @date 2021/2/28 */ @Data -@ApiModel +@Schema public class BasePageQuery { - @ApiModelProperty(value = "页码", example = "1") + @Schema(description = "页码", example = "1") private int pageNum = 1; - @ApiModelProperty(value = "每页记录数", example = "10") + @Schema(description = "每页记录数", example = "10") private int pageSize = 10; } diff --git a/src/main/java/com/youlai/system/common/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/system/common/exception/GlobalExceptionHandler.java index 0d87d9c1..4669cec9 100644 --- a/src/main/java/com/youlai/system/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/youlai/system/common/exception/GlobalExceptionHandler.java @@ -18,9 +18,9 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; -import javax.servlet.ServletException; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; +import jakarta.servlet.ServletException; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; import java.sql.SQLSyntaxErrorException; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/com/youlai/system/common/util/ResponseUtils.java b/src/main/java/com/youlai/system/common/util/ResponseUtils.java index 458f7721..d969b1fe 100644 --- a/src/main/java/com/youlai/system/common/util/ResponseUtils.java +++ b/src/main/java/com/youlai/system/common/util/ResponseUtils.java @@ -3,10 +3,12 @@ package com.youlai.system.common.util; import cn.hutool.json.JSONUtil; import com.youlai.system.common.result.Result; import com.youlai.system.common.result.ResultCode; + import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import javax.servlet.http.HttpServletResponse; +// import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** diff --git a/src/main/java/com/youlai/system/config/SecurityConfig.java b/src/main/java/com/youlai/system/config/SecurityConfig.java index 7df9e20a..66108783 100644 --- a/src/main/java/com/youlai/system/config/SecurityConfig.java +++ b/src/main/java/com/youlai/system/config/SecurityConfig.java @@ -57,8 +57,10 @@ public class SecurityConfig { @Bean public WebSecurityCustomizer webSecurityCustomizer() { - return (web) -> web.ignoring() - .antMatchers("/api/v1/auth/login", "/webjars/**", "/doc.html", "/swagger-resources/**", "/v3/api-docs"); + return (web) -> web.ignoring().requestMatchers("/api/v1/auth/login", "/webjars/**", "/doc.html", + "/swagger-resources/**", + "/v3/api-docs/swagger-config", + "/v3/api-docs"); } @Bean diff --git a/src/main/java/com/youlai/system/config/SwaggerConfig.java b/src/main/java/com/youlai/system/config/SwaggerConfig.java index 33154141..2d35b6bd 100644 --- a/src/main/java/com/youlai/system/config/SwaggerConfig.java +++ b/src/main/java/com/youlai/system/config/SwaggerConfig.java @@ -1,82 +1,54 @@ package com.youlai.system.config; -import cn.hutool.core.collection.CollectionUtil; -import io.swagger.annotations.Api; +import cn.hutool.core.util.RandomUtil; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springdoc.core.customizers.GlobalOpenApiCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import springfox.documentation.spi.service.contexts.SecurityContext; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.*; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; + @Configuration -@EnableSwagger2 public class SwaggerConfig { - @Bean - public Docket createRestApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("权限服务") - //是否开启 (true 开启 false隐藏。生产环境建议隐藏) - //.enable(false) - .select() - //扫描的路径包,设置basePackage会将包下的所有被@Api标记类的所有方法作为api - .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) - //指定路径处理PathSelectors.any()代表所有的路径 - .paths(PathSelectors.any()) - .build().securityContexts(CollectionUtil.newArrayList(securityContext())) - .securitySchemes(CollectionUtil.newArrayList(apiKey())); - } - /** - * 配置基本信息 + * 根据@Tag 上的排序,写入x-order * - * @return + * @return the global open api customizer */ @Bean - public ApiInfo apiInfo() { - return new ApiInfoBuilder() - //设置文档标题(API名称) - .title("SpringBoot单体应用开发文档") - //文档描述 - .description("快速开发文档-接口说明") - //版本号 - .version("1.0.0") - //联系人 - .contact(new Contact("", "http://localhost", "")) - .build(); + public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() { + return openApi -> { + if (openApi.getTags() != null) { + openApi.getTags().forEach(tag -> { + Map map = new HashMap<>(); + map.put("x-order", RandomUtil.randomInt(0, 100)); + tag.setExtensions(map); + }); + } + if (openApi.getPaths() != null) { + openApi.addExtension("x-test123", "333"); + openApi.getPaths().addExtension("x-abb", RandomUtil.randomInt(1, 100)); + } + + }; } - private List securitySchemes() { - List apiKeyList= new ArrayList<>(); + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("XXX用户系统API") + .version("1.0") - apiKeyList.add(HttpAuthenticationScheme.JWT_BEARER_BUILDER.name("Authorization").build()); - return apiKeyList; + .description("Knife4j集成springdoc-openapi示例") + .termsOfService("http://doc.xiaominfo.com") + .license(new License().name("Apache 2.0") + .url("http://doc.xiaominfo.com"))); } - private ApiKey apiKey() { - return new ApiKey("Authorization", "Authorization", "header"); - } - - List defaultAuth() { - AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); - AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; - authorizationScopes[0] = authorizationScope; - return CollectionUtil.newArrayList(new SecurityReference("Authorization", authorizationScopes)); - } - - private SecurityContext securityContext() { - return SecurityContext.builder() - .securityReferences(defaultAuth()) - //.forPaths(PathSelectors.regex(".*?208.*$")) - .build(); - } } diff --git a/src/main/java/com/youlai/system/config/WebMvcConfig.java b/src/main/java/com/youlai/system/config/WebMvcConfig.java index 81f05a57..92911c96 100644 --- a/src/main/java/com/youlai/system/config/WebMvcConfig.java +++ b/src/main/java/com/youlai/system/config/WebMvcConfig.java @@ -15,9 +15,9 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.util.List; diff --git a/src/main/java/com/youlai/system/controller/AuthController.java b/src/main/java/com/youlai/system/controller/AuthController.java index 7e56505a..d1cbc950 100644 --- a/src/main/java/com/youlai/system/controller/AuthController.java +++ b/src/main/java/com/youlai/system/controller/AuthController.java @@ -4,8 +4,8 @@ package com.youlai.system.controller; import com.youlai.system.common.result.Result; import com.youlai.system.pojo.dto.TokenResult; import com.youlai.system.security.JwtTokenManager; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -13,7 +13,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; -@Api(tags = "认证管理") +@Tag(name = "认证管理") @RestController @RequestMapping("/api/v1/auth") @RequiredArgsConstructor @@ -21,7 +21,7 @@ public class AuthController { private final AuthenticationManager authenticationManager; private final JwtTokenManager jwtTokenManager; - @ApiOperation(value = "登录") + @Operation(summary = "登录") @PostMapping("/login") public Result login( @RequestParam String username, @@ -41,7 +41,7 @@ public class AuthController { return Result.success(tokenResult); } - @ApiOperation(value = "注销") + @Operation(summary = "注销") @DeleteMapping("/logout") public Result login() { SecurityContextHolder.clearContext(); diff --git a/src/main/java/com/youlai/system/controller/FileController.java b/src/main/java/com/youlai/system/controller/FileController.java index ea602b19..a58c34c3 100644 --- a/src/main/java/com/youlai/system/controller/FileController.java +++ b/src/main/java/com/youlai/system/controller/FileController.java @@ -3,15 +3,15 @@ package com.youlai.system.controller; import com.youlai.system.common.result.Result; import com.youlai.system.pojo.vo.file.FileInfo; import com.youlai.system.service.FileService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -@Api(tags = "文件接口") +@Tag(name = "文件接口") @RestController @RequestMapping("/api/v1/files") @RequiredArgsConstructor @@ -20,19 +20,19 @@ public class FileController { private final FileService fileService; @PostMapping - @ApiOperation(value = "文件上传") + @Operation(summary = "文件上传") public Result uploadFile( - @ApiParam("表单文件对象") @RequestParam(value = "file") MultipartFile file + @Parameter(name ="表单文件对象") @RequestParam(value = "file") MultipartFile file ) { FileInfo fileInfo = fileService.uploadFile(file); return Result.success(fileInfo); } @DeleteMapping - @ApiOperation(value = "文件删除") + @Operation(summary = "文件删除") @SneakyThrows public Result deleteFile( - @ApiParam("文件路径") @RequestParam String filePath + @Parameter(name ="文件路径") @RequestParam String filePath ) { boolean result = fileService.deleteFile(filePath); return Result.judge(result); diff --git a/src/main/java/com/youlai/system/controller/SysDeptController.java b/src/main/java/com/youlai/system/controller/SysDeptController.java index 7e88115d..18fa6de3 100644 --- a/src/main/java/com/youlai/system/controller/SysDeptController.java +++ b/src/main/java/com/youlai/system/controller/SysDeptController.java @@ -6,13 +6,14 @@ import com.youlai.system.pojo.form.DeptForm; import com.youlai.system.pojo.query.DeptQuery; import com.youlai.system.pojo.vo.dept.DeptVO; import com.youlai.system.service.SysDeptService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import java.util.List; /** @@ -21,7 +22,7 @@ import java.util.List; * @author haoxr * @date 2020/11/6 */ -@Api(tags = "部门接口") +@Tag(name = "部门接口") @RestController @RequestMapping("/api/v1/dept") @RequiredArgsConstructor @@ -29,30 +30,30 @@ public class SysDeptController { private final SysDeptService deptService; - @ApiOperation(value = "获取部门列表") + @Operation(summary = "获取部门列表") @GetMapping public Result> listDepartments(DeptQuery queryParams) { List list = deptService.listDepartments(queryParams); return Result.success(list); } - @ApiOperation(value = "获取部门下拉选项") + @Operation(summary = "获取部门下拉选项") @GetMapping("/options") public Result> listDeptOptions() { List