README.md
@@ -14,6 +14,8 @@ | åè½ä»ç» | ä½¿ç¨ææ¯ | ææ¡£å°å | ç¹æ§æ³¨æäºé¡¹ | |---|---|---|---| | å½åæ¡æ¶ | RuoYi-Vue-Plus | [RuoYi-Vue-Plusææ¡£](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | éåRuoYi-Vueå ¨æ¹ä½å级(ä¸å ¼å®¹åæ¡æ¶) | | satoken忝 | RuoYi-Vue-Plus-satoken | [satoken忝å°å](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 使ç¨satokenéææéé´æ(ä» ä¾å¦ä¹ 䏿¨èä¸ç产) | | åä½åæ¯ | RuoYi-Vue-Plus-fast | [fast忝å°å](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | åä½åºç¨ç»æ | | åæ¡æ¶ | RuoYi-Vue | [RuoYi-Vueå®ç½](http://ruoyi.vip/) | 宿忥éè¦çåè½ | | å端å¼åæ¡æ¶ | VueãElement UI | [Element UIå®ç½](https://element.eleme.cn/#/zh-CN) | | | å端å¼åæ¡æ¶ | SpringBoot | [SpringBootå®ç½](https://spring.io/projects/spring-boot/#learn) | | @@ -69,6 +71,7 @@ * 忥å级 RuoYi-Vue * GitHub å°å [RuoYi-Vue-Plus-github](https://github.com/JavaLionLi/RuoYi-Vue-Plus) * 忍¡å fast 忝 [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) * satoken 忝 [RuoYi-Vue-Plus-satoken](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) * ç¨æ·æ©å±é¡¹ç® [æ©å±é¡¹ç®å表](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4478302&doc_id=1469725) ## å ç¾¤ä¸æç® pom.xml
@@ -14,7 +14,7 @@ <properties> <ruoyi-vue-plus.version>3.1.0</ruoyi-vue-plus.version> <spring-boot.version>2.5.4</spring-boot.version> <spring-boot.version>2.5.5</spring-boot.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> @@ -26,14 +26,14 @@ <easyexcel.version>2.2.11</easyexcel.version> <velocity.version>1.7</velocity.version> <satoken.version>1.26.0</satoken.version> <mybatis-plus.version>3.4.3.3</mybatis-plus.version> <mybatis-plus.version>3.4.3.4</mybatis-plus.version> <p6spy.version>3.9.1</p6spy.version> <hutool.version>5.7.13</hutool.version> <feign.version>3.0.3</feign.version> <feign-okhttp.version>11.6</feign-okhttp.version> <okhttp.version>4.9.1</okhttp.version> <spring-boot-admin.version>2.5.1</spring-boot-admin.version> <redisson.version>3.16.2</redisson.version> <redisson.version>3.16.3</redisson.version> <lock4j.version>2.2.1</lock4j.version> <dynamic-ds.version>3.4.1</dynamic-ds.version> @@ -43,7 +43,7 @@ <!-- OSS é ç½® --> <qiniu.version>7.8.0</qiniu.version> <aliyun.oss.version>3.13.1</aliyun.oss.version> <qcloud.cos.version>5.6.51</qcloud.cos.version> <qcloud.cos.version>5.6.55</qcloud.cos.version> <minio.version>8.3.0</minio.version> <!-- docker é ç½® --> ruoyi-admin/src/main/resources/application.yml
@@ -122,6 +122,28 @@ # æ¯å¦è¾åºæä½æ¥å¿ is-log: true # securityé ç½® security: # ç»åºè·¯å¾ logout-url: /logout # å¿åè·¯å¾ anonymous: - /login - /register - /captchaImage # swagger ææ¡£é ç½® - /doc.html - /swagger-resources/** - /webjars/** - /*/api-docs # druid çæ§é ç½® - /druid/** # actuator çæ§é ç½® - /actuator - /actuator/** # ç¨æ·æ¾è¡ permit-all: # éå¤æäº¤ repeat-submit: # å ¨å±é´éæ¶é´(毫ç§) @@ -237,6 +259,11 @@ name: Lion Li email: crazylionli@163.com url: https://gitee.com/JavaLionLi/RuoYi-Vue-Plus groups: - name: æ¼ç¤ºæ¡ä¾ basePackage: com.ruoyi.demo - name: ç³»ç»æ¨¡å basePackage: com.ruoyi.admin # 鲿¢XSSæ»å» xss: ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
ÎļþÒÑɾ³ý ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
@@ -27,6 +27,9 @@ public static String getRealAddressByIP(String ip) { String address = UNKNOWN; if (StringUtils.isBlank(ip)){ return address; } // å ç½ä¸æ¥è¯¢ ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); if (NetUtil.isInnerIP(ip)) { ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java
ÎļþÒÑɾ³ý ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
@@ -1,11 +1,12 @@ package com.ruoyi.framework.config; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import com.ruoyi.common.properties.TokenProperties; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.config.properties.SwaggerProperties; import io.swagger.annotations.ApiOperation; import io.swagger.models.auth.In; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; @@ -15,6 +16,7 @@ import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; @@ -27,82 +29,92 @@ @EnableKnife4j public class SwaggerConfig { @Autowired private SwaggerProperties swaggerProperties; @Autowired private SwaggerProperties swaggerProperties; /** * å建API */ @Bean public Docket createRestApi() { return new Docket(DocumentationType.OAS_30) .enable(swaggerProperties.getEnabled()) // ç¨æ¥å建该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() /* 设置å®å ¨æ¨¡å¼ï¼swaggerå¯ä»¥è®¾ç½®è®¿é®token */ .securitySchemes(securitySchemes()) .securityContexts(securityContexts()) .pathMapping(swaggerProperties.getPathMapping()); } @Autowired private TokenProperties tokenProperties; /** * å®å ¨æ¨¡å¼ï¼è¿éæå®tokenéè¿Authorization头请æ±å¤´ä¼ é */ private List<SecurityScheme> securitySchemes() { List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); return apiKeyList; } /** * å建API */ @PostConstruct public void createRestApi() { for (SwaggerProperties.Groups group : swaggerProperties.getGroups()) { String basePackage = group.getBasePackage(); Docket docket = new Docket(DocumentationType.OAS_30) .enable(swaggerProperties.getEnabled()) // ç¨æ¥å建该APIçåºæ¬ä¿¡æ¯ï¼å±ç¤ºå¨ææ¡£ç页é¢ä¸ï¼èªå®ä¹å±ç¤ºçä¿¡æ¯ï¼ .apiInfo(apiInfo()) // 设置åªäºæ¥å£æ´é²ç»Swaggerå±ç¤º .select() // æ«æææææ³¨è§£çapiï¼ç¨è¿ç§æ¹å¼æ´çµæ´» //.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // æ«ææå®å ä¸çswagger注解 .apis(RequestHandlerSelectors.basePackage(basePackage)) // æ«æææ .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build() .groupName(group.getName()) // 设置å®å ¨æ¨¡å¼ï¼swaggerå¯ä»¥è®¾ç½®è®¿é®token .securitySchemes(securitySchemes()) .securityContexts(securityContexts()) .pathMapping(swaggerProperties.getPathMapping()); String beanName = StringUtils.substringAfterLast(basePackage, ".") + "Docket"; SpringUtils.registerBean(beanName, docket); } } /** * å®å ¨ä¸ä¸æ */ private List<SecurityContext> securityContexts() { List<SecurityContext> securityContexts = new ArrayList<>(); securityContexts.add( SecurityContext.builder() .securityReferences(defaultAuth()) .operationSelector(o -> o.requestMappingPattern().matches("/.*")) .build()); return securityContexts; } /** * å®å ¨æ¨¡å¼ï¼è¿éæå®tokenéè¿Authorization头请æ±å¤´ä¼ é */ private List<SecurityScheme> securitySchemes() { List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); String header = tokenProperties.getHeader(); apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue())); return apiKeyList; } /** * é»è®¤çå®å ¨ä¸å¼ç¨ */ private List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; List<SecurityReference> securityReferences = new ArrayList<>(); securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); return securityReferences; } /** * å®å ¨ä¸ä¸æ */ private List<SecurityContext> securityContexts() { List<SecurityContext> securityContexts = new ArrayList<>(); securityContexts.add( SecurityContext.builder() .securityReferences(defaultAuth()) .operationSelector(o -> o.requestMappingPattern().matches("/.*")) .build()); return securityContexts; } /** * æ·»å æè¦ä¿¡æ¯ */ private ApiInfo apiInfo() { // ç¨ApiInfoBuilderè¿è¡å®å¶ SwaggerProperties.Contact contact = swaggerProperties.getContact(); return new ApiInfoBuilder() // 设置æ é¢ .title(swaggerProperties.getTitle()) // æè¿° .description(swaggerProperties.getDescription()) // ä½è ä¿¡æ¯ .contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail())) // çæ¬ .version(swaggerProperties.getVersion()) .build(); } /** * é»è®¤çå®å ¨ä¸å¼ç¨ */ private List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; List<SecurityReference> securityReferences = new ArrayList<>(); securityReferences.add(new SecurityReference(tokenProperties.getHeader(), authorizationScopes)); return securityReferences; } /** * æ·»å æè¦ä¿¡æ¯ */ private ApiInfo apiInfo() { // ç¨ApiInfoBuilderè¿è¡å®å¶ SwaggerProperties.Contact contact = swaggerProperties.getContact(); return new ApiInfoBuilder() // 设置æ é¢ .title(swaggerProperties.getTitle()) // æè¿° .description(swaggerProperties.getDescription()) // ä½è ä¿¡æ¯ .contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail())) // çæ¬ .version(swaggerProperties.getVersion()) .build(); } } ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,32 @@ package com.ruoyi.framework.config.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * Security é ç½®å±æ§ * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "security") public class SecurityProperties { /** * éåºç»å½url */ private String logoutUrl; /** * å¿åæ¾è¡è·¯å¾ */ private String[] anonymous; /** * ç¨æ·ä»»æè®¿é®æ¾è¡è·¯å¾ */ private String[] permitAll; } ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java
@@ -5,6 +5,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.List; /** * swagger é ç½®å±æ§ * @@ -41,23 +43,46 @@ */ private Contact contact; /** * ç»é ç½® */ private List<Groups> groups; @Data @NoArgsConstructor public static class Contact{ public static class Contact { /** * è系人 **/ */ private String name; /** * è系人url **/ */ private String url; /** * è系人email **/ */ private String email; } @Data @NoArgsConstructor public static class Groups { /** * ç»å */ private String name; /** * åºç¡å è·¯å¾ */ private String basePackage; } } ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
@@ -557,7 +557,7 @@ /** ${subTable.functionName}å é¤æé®æä½ */ handleDelete${subClassName}() { if (this.checked${subClassName}.length == 0) { this.msgError("请å éæ©è¦å é¤ç${subTable.functionName}æ°æ®"); this.#[[$modal]]#.msgError("请å éæ©è¦å é¤ç${subTable.functionName}æ°æ®"); } else { const ${subclassName}List = this.${subclassName}List; const checked${subClassName} = this.checked${subClassName}; ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
@@ -77,9 +77,14 @@ asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); throw new UserPasswordNotMatchException(); } else { asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request); throw new ServiceException(e.getMessage()); } asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); recordLoginInfo(user); recordLoginInfo(user.getUserId()); // çætoken StpUtil.login(user.getUserId(), "PC"); return StpUtil.getTokenValue(); @@ -109,12 +114,15 @@ /** * è®°å½ç»å½ä¿¡æ¯ * * @param userId ç¨æ·ID */ public void recordLoginInfo(SysUser user) public void recordLoginInfo(Long userId) { user.setLoginIp(ServletUtils.getClientIP()); user.setLoginDate(DateUtils.getNowDate()); user.setUpdateBy(user.getUserName()); userService.updateUserProfile(user); SysUser sysUser = new SysUser(); sysUser.setUserId(userId); sysUser.setLoginIp(ServletUtils.getClientIP()); sysUser.setLoginDate(DateUtils.getNowDate()); userService.updateUserProfile(sysUser); } } ruoyi-ui/src/views/tool/gen/editTable.vue
@@ -4,8 +4,8 @@ <el-tab-pane label="åºæ¬ä¿¡æ¯" name="basic"> <basic-info-form ref="basicInfo" :info="info" /> </el-tab-pane> <el-tab-pane label="åæ®µä¿¡æ¯" name="cloum"> <el-table ref="dragTable" :data="cloumns" row-key="columnId" :max-height="tableHeight"> <el-tab-pane label="åæ®µä¿¡æ¯" name="columnInfo"> <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight"> <el-table-column label="åºå·" type="index" min-width="5%" class-name="allowDrag" /> <el-table-column label="åæ®µåå" @@ -141,13 +141,13 @@ data() { return { // éä¸é项å¡ç name activeName: "cloum", activeName: "columnInfo", // è¡¨æ ¼çé«åº¦ tableHeight: document.documentElement.scrollHeight - 245 + "px", // è¡¨ä¿¡æ¯ tables: [], // 表åä¿¡æ¯ cloumns: [], columns: [], // åå ¸ä¿¡æ¯ dictOptions: [], // èåä¿¡æ¯ @@ -161,7 +161,7 @@ if (tableId) { // è·å表详ç»ä¿¡æ¯ getGenTable(tableId).then(res => { this.cloumns = res.data.rows; this.columns = res.data.rows; this.info = res.data.info; this.tables = res.data.tables; }); @@ -184,7 +184,7 @@ const validateResult = res.every(item => !!item); if (validateResult) { const genTable = Object.assign({}, basicForm.model, genForm.model); genTable.columns = this.cloumns; genTable.columns = this.columns; genTable.params = { treeCode: genTable.treeCode, treeName: genTable.treeName, @@ -220,10 +220,10 @@ const sortable = Sortable.create(el, { handle: ".allowDrag", onEnd: evt => { const targetRow = this.cloumns.splice(evt.oldIndex, 1)[0]; this.cloumns.splice(evt.newIndex, 0, targetRow); for (let index in this.cloumns) { this.cloumns[index].sort = parseInt(index) + 1; const targetRow = this.columns.splice(evt.oldIndex, 1)[0]; this.columns.splice(evt.newIndex, 0, targetRow); for (let index in this.columns) { this.columns[index].sort = parseInt(index) + 1; } } });