diff --git a/pom.xml b/pom.xml index 78eb9e4cf..158cba652 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,7 @@ 2.13.0 4.1.2 2.3 + 2.5.0 @@ -142,6 +143,13 @@ ${oshi.version} + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + commons-io diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index e2a5dbcd4..53df70040 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -30,6 +30,12 @@ true + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + com.mysql diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java index 14abba1c2..b56a09766 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -1,183 +1,175 @@ -//package com.ruoyi.web.controller.tool; -// -//import java.util.ArrayList; -//import java.util.LinkedHashMap; -//import java.util.List; -//import java.util.Map; -//import org.springframework.web.bind.annotation.DeleteMapping; -//import org.springframework.web.bind.annotation.GetMapping; -//import org.springframework.web.bind.annotation.PathVariable; -//import org.springframework.web.bind.annotation.PostMapping; -//import org.springframework.web.bind.annotation.PutMapping; -//import org.springframework.web.bind.annotation.RequestBody; -//import org.springframework.web.bind.annotation.RequestMapping; -//import org.springframework.web.bind.annotation.RestController; -//import com.ruoyi.common.core.controller.BaseController; -//import com.ruoyi.common.core.domain.R; -//import com.ruoyi.common.utils.StringUtils; -//import io.swagger.annotations.Api; -//import io.swagger.annotations.ApiImplicitParam; -//import io.swagger.annotations.ApiImplicitParams; -//import io.swagger.annotations.ApiModel; -//import io.swagger.annotations.ApiModelProperty; -//import io.swagger.annotations.ApiOperation; -// -///** -// * swagger 用户测试方法 -// * -// * @author ruoyi -// */ -//@Api("用户信息管理") -//@RestController -//@RequestMapping("/test/user") -//public class TestController extends BaseController -//{ -// private final static Map users = new LinkedHashMap(); -// { -// users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); -// users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); -// } -// -// @ApiOperation("获取用户列表") -// @GetMapping("/list") -// public R> userList() -// { -// List userList = new ArrayList(users.values()); -// return R.ok(userList); -// } -// -// @ApiOperation("获取用户详细") -// @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) -// @GetMapping("/{userId}") -// public R getUser(@PathVariable Integer userId) -// { -// if (!users.isEmpty() && users.containsKey(userId)) -// { -// return R.ok(users.get(userId)); -// } -// else -// { -// return R.fail("用户不存在"); -// } -// } -// -// @ApiOperation("新增用户") -// @ApiImplicitParams({ -// @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), -// @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), -// @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), -// @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class) -// }) -// @PostMapping("/save") -// public R save(UserEntity user) -// { -// if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) -// { -// return R.fail("用户ID不能为空"); -// } -// users.put(user.getUserId(), user); -// return R.ok(); -// } -// -// @ApiOperation("更新用户") -// @PutMapping("/update") -// public R update(@RequestBody UserEntity user) -// { -// if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) -// { -// return R.fail("用户ID不能为空"); -// } -// if (users.isEmpty() || !users.containsKey(user.getUserId())) -// { -// return R.fail("用户不存在"); -// } -// users.remove(user.getUserId()); -// users.put(user.getUserId(), user); -// return R.ok(); -// } -// -// @ApiOperation("删除用户信息") -// @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) -// @DeleteMapping("/{userId}") -// public R delete(@PathVariable Integer userId) -// { -// if (!users.isEmpty() && users.containsKey(userId)) -// { -// users.remove(userId); -// return R.ok(); -// } -// else -// { -// return R.fail("用户不存在"); -// } -// } -//} -// -//@ApiModel(value = "UserEntity", description = "用户实体") -//class UserEntity -//{ -// @ApiModelProperty("用户ID") -// private Integer userId; -// -// @ApiModelProperty("用户名称") -// private String username; -// -// @ApiModelProperty("用户密码") -// private String password; -// -// @ApiModelProperty("用户手机") -// private String mobile; -// -// public UserEntity() -// { -// -// } -// -// public UserEntity(Integer userId, String username, String password, String mobile) -// { -// this.userId = userId; -// this.username = username; -// this.password = password; -// this.mobile = mobile; -// } -// -// public Integer getUserId() -// { -// return userId; -// } -// -// public void setUserId(Integer userId) -// { -// this.userId = userId; -// } -// -// 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; -// } -// -// public String getMobile() -// { -// return mobile; -// } -// -// public void setMobile(String mobile) -// { -// this.mobile = mobile; -// } -//} +package com.ruoyi.web.controller.tool; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +@Tag(name = "用户信息管理") +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map users = new LinkedHashMap(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @Operation(summary = "获取用户列表") + @GetMapping("/list") + public R> userList() + { + List userList = new ArrayList(users.values()); + return R.ok(userList); + } + + @Operation(summary = "获取用户详细") + @GetMapping("/{userId}") + public R getUser(@PathVariable(name = "userId") + Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + @Operation(summary = "新增用户") + @PostMapping("/save") + public R save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + @Operation(summary = "更新用户") + @PutMapping("/update") + public R update(@RequestBody + UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + @Operation(summary = "删除用户信息") + @DeleteMapping("/{userId}") + public R delete(@PathVariable(name = "userId") + Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +@Schema(description = "用户实体") +class UserEntity +{ + @Schema(title = "用户ID") + private Integer userId; + + @Schema(title = "用户名称") + private String username; + + @Schema(title = "用户密码") + private String password; + + @Schema(title = "用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + 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; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java index 7874a2967..8d2de2f7a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -1,67 +1,65 @@ -//package com.ruoyi.web.core.config; -// -//import org.springframework.beans.factory.annotation.Value; -//import org.springframework.context.annotation.Bean; -//import org.springframework.context.annotation.Configuration; -//import com.ruoyi.common.config.RuoYiConfig; -//import io.swagger.annotations.ApiOperation; -//import springfox.documentation.builders.ApiInfoBuilder; -//import springfox.documentation.builders.PathSelectors; -//import springfox.documentation.builders.RequestHandlerSelectors; -//import springfox.documentation.service.ApiInfo; -//import springfox.documentation.service.Contact; -//import springfox.documentation.spi.DocumentationType; -//import springfox.documentation.spring.web.plugins.Docket; -// -///** -// * Swagger2的接口配置 -// * -// * @author ruoyi -// */ -//@Configuration -//public class SwaggerConfig -//{ -// /** 是否开启swagger */ -// @Value("${swagger.enabled}") -// private boolean enabled; -// -// /** -// * 创建API -// */ -// @Bean -// public Docket createRestApi() -// { -// return new Docket(DocumentationType.OAS_30) -// // 是否启用Swagger -// .enable(enabled) -// // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) -// .apiInfo(apiInfo()) -// // 设置哪些接口暴露给Swagger展示 -// .select() -// // 扫描所有有注解的api,用这种方式更灵活 -// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) -// // 扫描指定包中的swagger注解 -// //.apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) -// // 扫描所有 .apis(RequestHandlerSelectors.any()) -// .paths(PathSelectors.any()) -// .build(); -// } -// -// /** -// * 添加摘要信息 -// */ -// private ApiInfo apiInfo() -// { -// // 用ApiInfoBuilder进行定制 -// return new ApiInfoBuilder() -// // 设置标题 -// .title("标题:若依管理系统_接口文档") -// // 描述 -// .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") -// // 作者信息 -// .contact(new Contact(RuoYiConfig.getName(), null, null)) -// // 版本 -// .version("版本号:" + RuoYiConfig.getVersion()) -// .build(); -// } -//} +package com.ruoyi.web.core.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.config.RuoYiConfig; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 自定义的 OpenAPI 对象 + */ + @Bean + public OpenAPI customOpenApi() + { + return new OpenAPI().components(new Components() + // 设置认证的请求头 + .addSecuritySchemes("apikey", securityScheme())) + .addSecurityItem(new SecurityRequirement().addList("apikey")) + .info(getApiInfo()); + } + + @Bean + public SecurityScheme securityScheme() + { + return new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .name("Authorization") + .in(SecurityScheme.In.HEADER) + .scheme("Bearer"); + } + + /** + * 添加摘要信息 + */ + @SuppressWarnings("static-access") + public Info getApiInfo() + { + return new Info() + // 设置标题 + .title("标题:若依管理系统_接口文档") + // 描述 + .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") + // 作者信息 + .contact(new Contact().name(ruoyiConfig.getName())) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()); + } +} diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index dd7fc6e30..e9a01b1d7 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -127,6 +127,20 @@ shiro: # 是否开启记住我 enabled: true +# Springdoc配置 +springdoc: + api-docs: + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + tags-sorter: alpha + group-configs: + - group: 'default' + display-name: '测试模块' + paths-to-match: '/**' + packages-to-scan: com.ruoyi.web.controller.tool + # 防止XSS攻击 xss: # 过滤开关 diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index c8cc13ea8..819d049df 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -42,7 +42,7 @@ servlet-api - jakarta.servlet + javax.servlet diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java index 76afb7211..b957e84fd 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java @@ -4,7 +4,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.shiro.cache.ehcache.EhCacheManager; @@ -25,7 +24,6 @@ import com.ruoyi.common.constant.Constants; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.security.CipherUtils; import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.config.properties.PermitAllUrlProperties; import com.ruoyi.framework.shiro.realm.UserRealm; import com.ruoyi.framework.shiro.session.OnlineSessionDAO; import com.ruoyi.framework.shiro.session.OnlineSessionFactory; @@ -290,12 +288,6 @@ public class ShiroConfig filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/ruoyi/**", "anon"); filterChainDefinitionMap.put("/captcha/captchaImage**", "anon"); - // 匿名访问不鉴权注解列表 - List permitAllUrl = SpringUtils.getBean(PermitAllUrlProperties.class).getUrls(); - if (StringUtils.isNotEmpty(permitAllUrl)) - { - permitAllUrl.forEach(url -> filterChainDefinitionMap.put(url, "anon")); - } // 退出 logout地址,shiro去清除session filterChainDefinitionMap.put("/logout", "logout"); // 不需要拦截的访问 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java deleted file mode 100644 index 068310246..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.ruoyi.framework.config.properties; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import org.springframework.aop.framework.Advised; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import com.ruoyi.common.annotation.Anonymous; - -/** - * 设置Anonymous注解允许匿名访问的url - * - * @author ruoyi - */ -@Configuration -public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware -{ - private List urls = new ArrayList<>(); - - private ApplicationContext applicationContext; - - @Override - public void afterPropertiesSet() throws Exception - { - Map controllers = applicationContext.getBeansWithAnnotation(Controller.class); - for (Object bean : controllers.values()) - { - if (!(bean instanceof Advised)) - { - continue; - } - Class beanClass = ((Advised) bean).getTargetSource().getTarget().getClass(); - RequestMapping base = beanClass.getAnnotation(RequestMapping.class); - String[] baseUrl = {}; - if (Objects.nonNull(base)) - { - baseUrl = base.value(); - } - Method[] methods = beanClass.getDeclaredMethods(); - for (Method method : methods) - { - if (method.isAnnotationPresent(Anonymous.class) && method.isAnnotationPresent(RequestMapping.class)) - { - RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); - String[] uri = requestMapping.value(); - urls.addAll(rebuildUrl(baseUrl, uri)); - } - else if (method.isAnnotationPresent(Anonymous.class) && method.isAnnotationPresent(GetMapping.class)) - { - GetMapping requestMapping = method.getAnnotation(GetMapping.class); - String[] uri = requestMapping.value(); - urls.addAll(rebuildUrl(baseUrl, uri)); - } - else if (method.isAnnotationPresent(Anonymous.class) && method.isAnnotationPresent(PostMapping.class)) - { - PostMapping requestMapping = method.getAnnotation(PostMapping.class); - String[] uri = requestMapping.value(); - urls.addAll(rebuildUrl(baseUrl, uri)); - } - else if (method.isAnnotationPresent(Anonymous.class) && method.isAnnotationPresent(PutMapping.class)) - { - PutMapping requestMapping = method.getAnnotation(PutMapping.class); - String[] uri = requestMapping.value(); - urls.addAll(rebuildUrl(baseUrl, uri)); - } - else if (method.isAnnotationPresent(Anonymous.class) && method.isAnnotationPresent(DeleteMapping.class)) - { - DeleteMapping requestMapping = method.getAnnotation(DeleteMapping.class); - String[] uri = requestMapping.value(); - urls.addAll(rebuildUrl(baseUrl, uri)); - } - } - - } - } - - private List rebuildUrl(String[] bases, String[] uris) - { - List urls = new ArrayList<>(); - for (String base : bases) - { - for (String uri : uris) - { - urls.add(prefix(base) + prefix(uri)); - } - } - return urls; - } - - private String prefix(String seg) - { - return seg.startsWith("/") ? seg : "/" + seg; - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException - { - this.applicationContext = context; - } - - public List getUrls() - { - return urls; - } - - public void setUrls(List urls) - { - this.urls = urls; - } -}