diff --git a/README.md b/README.md index a7957e5e0..c103879eb 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-3.1.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.5-blue.svg)]() -[![JDK-8+](https://img.shields.io/badge/JDK-8+-green.svg)]() +[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() +[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼容原框架) diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 9f649edb0..145469d31 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -108,6 +108,9 @@ token: # security配置 security: + # 登出路径 + logout-url: /logout + # 匿名路径 anonymous: - /login - /register @@ -122,6 +125,8 @@ security: # actuator 监控配置 - /actuator - /actuator/** + # 用户放行 + permit-all: # 重复提交 repeat-submit: @@ -238,6 +243,11 @@ swagger: 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: @@ -261,7 +271,7 @@ thread-pool: # 线程池维护线程所允许的空闲时间 keepAliveSeconds: 300 # 线程池对拒绝任务(无线程可用)的处理策略 - # CALLER_RUNS_POLICY 等待 + # CALLER_RUNS_POLICY 调用方执行 # DISCARD_OLDEST_POLICY 放弃最旧的 # DISCARD_POLICY 丢弃 # ABORT_POLICY 中止 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java index 0c40f341c..5529bb336 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ThreadPoolRejectedPolicy.java @@ -15,7 +15,7 @@ import java.util.concurrent.ThreadPoolExecutor; @AllArgsConstructor public enum ThreadPoolRejectedPolicy { - CALLER_RUNS_POLICY("等待", ThreadPoolExecutor.CallerRunsPolicy.class), + CALLER_RUNS_POLICY("调用方执行", ThreadPoolExecutor.CallerRunsPolicy.class), DISCARD_OLDEST_POLICY("放弃最旧的", ThreadPoolExecutor.DiscardOldestPolicy.class), DISCARD_POLICY("丢弃", ThreadPoolExecutor.DiscardPolicy.class), ABORT_POLICY("中止", ThreadPoolExecutor.AbortPolicy.class); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java index dc3aea2c0..3ab304c0c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -35,6 +35,7 @@ public class FileUtils extends FileUtil .append(percentEncodedFileName); response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); } /** diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index 855be657c..bb8b7f231 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -21,7 +21,7 @@ import org.springframework.web.filter.CorsFilter; /** * spring security配置 - * + * * @author ruoyi */ @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @@ -32,7 +32,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter */ @Autowired private UserDetailsService userDetailsService; - + /** * 认证失败处理类 */ @@ -50,7 +50,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; - + /** * 跨域过滤器 */ @@ -109,11 +109,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter "/**/*.js" ).permitAll() .antMatchers(securityProperties.getAnonymous()).anonymous() + .antMatchers(securityProperties.getPermitAll()).permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); - httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + httpSecurity.logout().logoutUrl(securityProperties.getLogoutUrl()).logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java index 2c65ac69d..4da3b9949 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java +++ b/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.DocumentationType; 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 @@ import java.util.List; @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 securitySchemes() { - List apiKeyList = new ArrayList(); - 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 securityContexts() { - List securityContexts = new ArrayList<>(); - securityContexts.add( - SecurityContext.builder() - .securityReferences(defaultAuth()) - .operationSelector(o -> o.requestMappingPattern().matches("/.*")) - .build()); - return securityContexts; - } + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() { + List apiKeyList = new ArrayList(); + String header = tokenProperties.getHeader(); + apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue())); + return apiKeyList; + } - /** - * 默认的安全上引用 - */ - private List defaultAuth() { - AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); - AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; - authorizationScopes[0] = authorizationScope; - List securityReferences = new ArrayList<>(); - securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); - return securityReferences; - } + /** + * 安全上下文 + */ + private List securityContexts() { + List 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 defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List 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(); + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java index 33414cedb..c83ffccbe 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java @@ -14,9 +14,19 @@ import org.springframework.stereotype.Component; @ConfigurationProperties(prefix = "security") public class SecurityProperties { + /** + * 退出登录url + */ + private String logoutUrl; + /** * 匿名放行路径 */ private String[] anonymous; + /** + * 用户任意访问放行路径 + */ + private String[] permitAll; + } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java index ece75efef..9c4cd238c 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java @@ -5,6 +5,8 @@ import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import java.util.List; + /** * swagger 配置属性 * @@ -41,23 +43,46 @@ public class SwaggerProperties { */ private Contact contact; + /** + * 组配置 + */ + private List 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; + + } + } diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index ca693e33e..db44ceb28 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -573,7 +573,7 @@ export default { #end /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/${moduleName}/${businessName}/export', this.queryParams); + this.#[[$download]]#.excel('/${moduleName}/${businessName}/export', this.queryParams); } } }; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/PermissionService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/PermissionService.java index fe735b0c7..ce9af68dd 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/PermissionService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/PermissionService.java @@ -15,12 +15,15 @@ import java.util.Set; * @author ruoyi */ @Service("ss") -public class PermissionService -{ - /** 所有权限标识 */ +public class PermissionService { + /** + * 所有权限标识 + */ private static final String ALL_PERMISSION = "*:*:*"; - /** 管理员角色权限标识 */ + /** + * 管理员角色权限标识 + */ private static final String SUPER_ADMIN = "admin"; private static final String ROLE_DELIMETER = ","; @@ -33,15 +36,12 @@ public class PermissionService * @param permission 权限字符串 * @return 用户是否具备某权限 */ - public boolean hasPermi(String permission) - { - if (StringUtils.isEmpty(permission)) - { + public boolean hasPermi(String permission) { + if (StringUtils.isEmpty(permission)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) - { + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { return false; } return hasPermissions(loginUser.getPermissions(), permission); @@ -53,8 +53,7 @@ public class PermissionService * @param permission 权限字符串 * @return 用户是否不具备某权限 */ - public boolean lacksPermi(String permission) - { + public boolean lacksPermi(String permission) { return hasPermi(permission) != true; } @@ -64,22 +63,17 @@ public class PermissionService * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 * @return 用户是否具有以下任意一个权限 */ - public boolean hasAnyPermi(String permissions) - { - if (StringUtils.isEmpty(permissions)) - { + public boolean hasAnyPermi(String permissions) { + if (StringUtils.isEmpty(permissions)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) - { + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { return false; } Set authorities = loginUser.getPermissions(); - for (String permission : permissions.split(PERMISSION_DELIMETER)) - { - if (permission != null && hasPermissions(authorities, permission)) - { + for (String permission : permissions.split(PERMISSION_DELIMETER)) { + if (permission != null && hasPermissions(authorities, permission)) { return true; } } @@ -92,22 +86,17 @@ public class PermissionService * @param role 角色字符串 * @return 用户是否具备某角色 */ - public boolean hasRole(String role) - { - if (StringUtils.isEmpty(role)) - { + public boolean hasRole(String role) { + if (StringUtils.isEmpty(role)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) - { + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } - for (SysRole sysRole : loginUser.getUser().getRoles()) - { + for (SysRole sysRole : loginUser.getUser().getRoles()) { String roleKey = sysRole.getRoleKey(); - if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) - { + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) { return true; } } @@ -120,8 +109,7 @@ public class PermissionService * @param role 角色名称 * @return 用户是否不具备某角色 */ - public boolean lacksRole(String role) - { + public boolean lacksRole(String role) { return hasRole(role) != true; } @@ -131,21 +119,16 @@ public class PermissionService * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 * @return 用户是否具有以下任意一个角色 */ - public boolean hasAnyRoles(String roles) - { - if (StringUtils.isEmpty(roles)) - { + public boolean hasAnyRoles(String roles) { + if (StringUtils.isEmpty(roles)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) - { + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } - for (String role : roles.split(ROLE_DELIMETER)) - { - if (hasRole(role)) - { + for (String role : roles.split(ROLE_DELIMETER)) { + if (hasRole(role)) { return true; } } @@ -156,11 +139,10 @@ public class PermissionService * 判断是否包含权限 * * @param permissions 权限列表 - * @param permission 权限字符串 + * @param permission 权限字符串 * @return 用户是否具备某权限 */ - private boolean hasPermissions(Set permissions, String permission) - { + private boolean hasPermissions(Set permissions, String permission) { return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java index 45bbf0abe..0d2218ecb 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java @@ -18,74 +18,65 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * 登录校验方法 - * + * * @author ruoyi */ -@Component -public class SysLoginService -{ +@Service +public class SysLoginService { @Autowired private TokenService tokenService; @Resource private AuthenticationManager authenticationManager; - @Autowired + @Autowired private ISysUserService userService; - @Autowired - private ISysConfigService configService; + @Autowired + private ISysConfigService configService; - @Autowired - private LogininforService asyncService; + @Autowired + private LogininforService asyncService; /** * 登录验证 - * + * * @param username 用户名 * @param password 密码 - * @param code 验证码 - * @param uuid 唯一标识 + * @param code 验证码 + * @param uuid 唯一标识 * @return 结果 */ - public String login(String username, String password, String code, String uuid) - { - HttpServletRequest request = ServletUtils.getRequest(); - boolean captchaOnOff = configService.selectCaptchaOnOff(); + public String login(String username, String password, String code, String uuid) { + HttpServletRequest request = ServletUtils.getRequest(); + boolean captchaOnOff = configService.selectCaptchaOnOff(); // 验证码开关 - if (captchaOnOff) - { + if (captchaOnOff) { validateCaptcha(username, code, uuid, request); } // 用户验证 Authentication authentication = null; - try - { + try { // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password)); - } - catch (Exception e) - { - if (e instanceof BadCredentialsException) - { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); + } catch (Exception e) { + if (e instanceof BadCredentialsException) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request); throw new UserPasswordNotMatchException(); - } - else - { + } 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); + asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); recordLoginInfo(loginUser.getUserId()); // 生成token @@ -94,24 +85,24 @@ public class SysLoginService /** * 校验验证码 - * + * * @param username 用户名 - * @param code 验证码 - * @param uuid 唯一标识 + * @param code 验证码 + * @param uuid 唯一标识 * @return 结果 */ public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) { - String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; - String captcha = RedisUtils.getCacheObject(verifyKey); + String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; + String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); - if (captcha == null) { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); - throw new CaptchaExpireException(); - } - if (!code.equalsIgnoreCase(captcha)) { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request); - throw new CaptchaException(); - } + if (captcha == null) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request); + throw new CaptchaException(); + } } /** @@ -119,8 +110,7 @@ public class SysLoginService * * @param userId 用户ID */ - public void recordLoginInfo(Long userId) - { + public void recordLoginInfo(Long userId) { SysUser sysUser = new SysUser(); sysUser.setUserId(userId); sysUser.setLoginIp(ServletUtils.getClientIP()); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java index d8c657b5b..095b3b566 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java @@ -2,19 +2,19 @@ package com.ruoyi.system.service; import com.ruoyi.common.core.domain.entity.SysUser; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.Set; /** * 用户权限处理 - * + * * @author ruoyi */ -@Component -public class SysPermissionService -{ +@Service +public class SysPermissionService { + @Autowired private ISysRoleService roleService; @@ -23,20 +23,16 @@ public class SysPermissionService /** * 获取角色数据权限 - * + * * @param user 用户信息 * @return 角色权限信息 */ - public Set getRolePermission(SysUser user) - { + public Set getRolePermission(SysUser user) { Set roles = new HashSet(); // 管理员拥有所有权限 - if (user.isAdmin()) - { + if (user.isAdmin()) { roles.add("admin"); - } - else - { + } else { roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); } return roles; @@ -44,20 +40,16 @@ public class SysPermissionService /** * 获取菜单数据权限 - * + * * @param user 用户信息 * @return 菜单权限信息 */ - public Set getMenuPermission(SysUser user) - { + public Set getMenuPermission(SysUser user) { Set perms = new HashSet(); // 管理员拥有所有权限 - if (user.isAdmin()) - { + if (user.isAdmin()) { perms.add("*:*:*"); - } - else - { + } else { perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); } return perms; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java index 8171cb079..045c680c3 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java @@ -9,75 +9,59 @@ import com.ruoyi.common.exception.user.CaptchaException; import com.ruoyi.common.exception.user.CaptchaExpireException; import com.ruoyi.common.utils.*; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; /** * 注册校验方法 * * @author ruoyi */ -@Component -public class SysRegisterService -{ +@Service +public class SysRegisterService { + @Autowired private ISysUserService userService; @Autowired private ISysConfigService configService; - @Autowired - private LogininforService asyncService; + @Autowired + private LogininforService asyncService; /** * 注册 */ - public String register(RegisterBody registerBody) - { + public String register(RegisterBody registerBody) { String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); boolean captchaOnOff = configService.selectCaptchaOnOff(); // 验证码开关 - if (captchaOnOff) - { + if (captchaOnOff) { validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); } - if (StringUtils.isEmpty(username)) - { + if (StringUtils.isEmpty(username)) { msg = "用户名不能为空"; - } - else if (StringUtils.isEmpty(password)) - { + } else if (StringUtils.isEmpty(password)) { msg = "用户密码不能为空"; - } - else if (username.length() < UserConstants.USERNAME_MIN_LENGTH - || username.length() > UserConstants.USERNAME_MAX_LENGTH) - { + } else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) { msg = "账户长度必须在2到20个字符之间"; - } - else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH - || password.length() > UserConstants.PASSWORD_MAX_LENGTH) - { + } else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { msg = "密码长度必须在5到20个字符之间"; - } - else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(username))) - { + } else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(username))) { msg = "保存用户'" + username + "'失败,注册账号已存在"; - } - else - { + } else { SysUser sysUser = new SysUser(); sysUser.setUserName(username); sysUser.setNickName(username); sysUser.setPassword(SecurityUtils.encryptPassword(registerBody.getPassword())); boolean regFlag = userService.registerUser(sysUser); - if (!regFlag) - { + if (!regFlag) { msg = "注册失败,请联系系统管理人员"; - } - else - { - asyncService.recordLogininfor(username, Constants.REGISTER, + } else { + asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"), ServletUtils.getRequest()); } } @@ -88,21 +72,18 @@ public class SysRegisterService * 校验验证码 * * @param username 用户名 - * @param code 验证码 - * @param uuid 唯一标识 + * @param code 验证码 + * @param uuid 唯一标识 * @return 结果 */ - public void validateCaptcha(String username, String code, String uuid) - { + public void validateCaptcha(String username, String code, String uuid) { String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); - if (captcha == null) - { + if (captcha == null) { throw new CaptchaExpireException(); } - if (!code.equalsIgnoreCase(captcha)) - { + if (!code.equalsIgnoreCase(captcha)) { throw new CaptchaException(); } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java index 9803a6015..f54130a9a 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java @@ -15,7 +15,7 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; @@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit; * * @author Lion Li */ -@Component +@Service public class TokenServiceImpl implements TokenService { protected static final long MILLIS_SECOND = 1000; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java index 5d104f681..6b1c1e0f8 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java @@ -21,8 +21,7 @@ import org.springframework.stereotype.Service; */ @Slf4j @Service -public class UserDetailsServiceImpl implements UserDetailsService -{ +public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private ISysUserService userService; @@ -31,21 +30,15 @@ public class UserDetailsServiceImpl implements UserDetailsService private SysPermissionService permissionService; @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException - { + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user = userService.selectUserByUserName(username); - if (StringUtils.isNull(user)) - { + if (StringUtils.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new ServiceException("登录用户:" + username + " 不存在"); - } - else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) - { + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { log.info("登录用户:{} 已被删除.", username); throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); - } - else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) - { + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new ServiceException("对不起,您的账号:" + username + " 已停用"); } @@ -53,8 +46,7 @@ public class UserDetailsServiceImpl implements UserDetailsService return createLoginUser(user); } - public UserDetails createLoginUser(SysUser user) - { + public UserDetails createLoginUser(SysUser user) { return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); } } diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json index 58a8c790e..894f859a9 100644 --- a/ruoyi-ui/package.json +++ b/ruoyi-ui/package.json @@ -42,7 +42,7 @@ "core-js": "3.8.1", "echarts": "4.9.0", "element-ui": "2.15.5", - "file-saver": "2.0.4", + "file-saver": "2.0.5", "fuse.js": "6.4.3", "highlight.js": "9.18.5", "js-beautify": "1.13.0", diff --git a/ruoyi-ui/src/main.js b/ruoyi-ui/src/main.js index 6024266ab..c6306a65c 100644 --- a/ruoyi-ui/src/main.js +++ b/ruoyi-ui/src/main.js @@ -17,7 +17,6 @@ import './assets/icons' // icon import './permission' // permission control import { getDicts } from "@/api/system/dict/data"; import { getConfigKey } from "@/api/system/config"; -import { downLoadExcel } from "@/utils/download"; import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"; // 分页组件 import Pagination from "@/components/Pagination"; @@ -44,7 +43,6 @@ Vue.prototype.resetForm = resetForm Vue.prototype.addDateRange = addDateRange Vue.prototype.selectDictLabel = selectDictLabel Vue.prototype.selectDictLabels = selectDictLabels -Vue.prototype.downLoadExcel = downLoadExcel Vue.prototype.handleTree = handleTree // 全局组件挂载 diff --git a/ruoyi-ui/src/plugins/download.js b/ruoyi-ui/src/plugins/download.js new file mode 100644 index 000000000..248ed01c8 --- /dev/null +++ b/ruoyi-ui/src/plugins/download.js @@ -0,0 +1,71 @@ +import { saveAs } from 'file-saver' +import axios from 'axios' +import { getToken } from '@/utils/auth' + +const baseURL = process.env.VUE_APP_BASE_API + +export default { + excel(url, params) { + // get请求映射params参数 + if (params) { + let urlparams = url + '?'; + for (const propName of Object.keys(params)) { + const value = params[propName]; + var part = encodeURIComponent(propName) + "="; + if (value !== null && typeof(value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && typeof (value[key]) !== 'undefined') { + let params = propName + '[' + key + ']'; + let subPart = encodeURIComponent(params) + '='; + urlparams += subPart + encodeURIComponent(value[key]) + '&'; + } + } + } else { + urlparams += part + encodeURIComponent(value) + "&"; + } + } + } + urlparams = urlparams.slice(0, -1); + url = urlparams; + } + url = baseURL + url + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then(res => { + const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) + this.saveAs(blob, decodeURI(res.headers['download-filename'])) + }) + }, + oss(ossId, name) { + var url = baseURL + '/system/oss/download/' + ossId + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then(res => { + const blob = new Blob([res.data], { type: 'application/octet-stream' }) + this.saveAs(blob, name) + }) + }, + zip(url, name) { + var url = baseURL + url + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then(res => { + const blob = new Blob([res.data], { type: 'application/zip' }) + this.saveAs(blob, name) + }) + }, + saveAs(text, name, opts) { + saveAs(text, name, opts); + } +} + diff --git a/ruoyi-ui/src/plugins/index.js b/ruoyi-ui/src/plugins/index.js index 15d829b18..a138e6d6f 100644 --- a/ruoyi-ui/src/plugins/index.js +++ b/ruoyi-ui/src/plugins/index.js @@ -1,5 +1,6 @@ import cache from './cache' import modal from './modal' +import download from './download' export default { install(Vue) { @@ -7,5 +8,7 @@ export default { Vue.prototype.$cache = cache // 模态框对象 Vue.prototype.$modal = modal + // 下载文件 + Vue.prototype.$download = download } } diff --git a/ruoyi-ui/src/utils/download.js b/ruoyi-ui/src/utils/download.js deleted file mode 100644 index 52b07f713..000000000 --- a/ruoyi-ui/src/utils/download.js +++ /dev/null @@ -1,91 +0,0 @@ -import axios from 'axios' -import { getToken } from '@/utils/auth' - -const mimeMap = { - xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - zip: 'application/zip', - oss: 'application/octet-stream' -} - -const baseUrl = process.env.VUE_APP_BASE_API -export function downLoadZip(str, filename) { - var url = baseUrl + str - axios({ - method: 'get', - url: url, - responseType: 'blob', - headers: { 'Authorization': 'Bearer ' + getToken() } - }).then(res => { - resolveBlob(res, mimeMap.zip) - }) -} - -export function downLoadOss(ossId) { - var url = baseUrl + '/system/oss/download/' + ossId - axios({ - method: 'get', - url: url, - responseType: 'blob', - headers: { 'Authorization': 'Bearer ' + getToken() } - }).then(res => { - resolveBlob(res, mimeMap.oss) - }) -} - -export function downLoadExcel(url, params) { - // get请求映射params参数 - if (params) { - let urlparams = url + '?'; - for (const propName of Object.keys(params)) { - const value = params[propName]; - var part = encodeURIComponent(propName) + "="; - if (value !== null && typeof(value) !== "undefined") { - if (typeof value === 'object') { - for (const key of Object.keys(value)) { - if (value[key] !== null && typeof (value[key]) !== 'undefined') { - let params = propName + '[' + key + ']'; - let subPart = encodeURIComponent(params) + '='; - urlparams += subPart + encodeURIComponent(value[key]) + '&'; - } - } - } else { - urlparams += part + encodeURIComponent(value) + "&"; - } - } - } - urlparams = urlparams.slice(0, -1); - url = urlparams; - } - url = baseUrl + url - axios({ - method: 'get', - url: url, - responseType: 'blob', - headers: { 'Authorization': 'Bearer ' + getToken() } - }).then(res => { - resolveBlob(res, mimeMap.xlsx) - }) -} - -/** - * 解析blob响应内容并下载 - * @param {*} res blob响应内容 - * @param {String} mimeType MIME类型 - */ -export function resolveBlob(res, mimeType) { - const aLink = document.createElement('a') - var blob = new Blob([res.data], { type: mimeType }) - // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名; - var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*') - var contentDisposition = decodeURI(res.headers['content-disposition']) - var result = patt.exec(contentDisposition) - var fileName = result[1] - fileName = fileName.replace(/\"/g, '') - aLink.style.display = 'none' - aLink.href = URL.createObjectURL(blob) - aLink.setAttribute('download', decodeURI(fileName)) // 设置下载文件名称 - document.body.appendChild(aLink) - aLink.click() - URL.revokeObjectURL(aLink.href);//清除引用 - document.body.removeChild(aLink); -} diff --git a/ruoyi-ui/src/utils/ruoyi.js b/ruoyi-ui/src/utils/ruoyi.js index 10533e787..63bd379b7 100644 --- a/ruoyi-ui/src/utils/ruoyi.js +++ b/ruoyi-ui/src/utils/ruoyi.js @@ -3,8 +3,6 @@ * Copyright (c) 2019 ruoyi */ -const baseURL = process.env.VUE_APP_BASE_API - // 日期格式化 export function parseTime(time, pattern) { if (arguments.length === 0 || !time) { diff --git a/ruoyi-ui/src/views/demo/demo/index.vue b/ruoyi-ui/src/views/demo/demo/index.vue index 6f952ec4b..678ae218a 100644 --- a/ruoyi-ui/src/views/demo/demo/index.vue +++ b/ruoyi-ui/src/views/demo/demo/index.vue @@ -358,7 +358,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/demo/demo/export', this.queryParams); + this.$download.excel('/demo/demo/export', this.queryParams); } } }; diff --git a/ruoyi-ui/src/views/monitor/job/index.vue b/ruoyi-ui/src/views/monitor/job/index.vue index 33d416ea2..a97477b0c 100644 --- a/ruoyi-ui/src/views/monitor/job/index.vue +++ b/ruoyi-ui/src/views/monitor/job/index.vue @@ -510,7 +510,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/monitor/job/export', this.queryParams); + this.$download.excel('/monitor/job/export', this.queryParams); } } }; diff --git a/ruoyi-ui/src/views/monitor/job/log.vue b/ruoyi-ui/src/views/monitor/job/log.vue index 7f2e72e06..fca1af40e 100644 --- a/ruoyi-ui/src/views/monitor/job/log.vue +++ b/ruoyi-ui/src/views/monitor/job/log.vue @@ -293,7 +293,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/monitor/jobLog/export', this.queryParams); + this.$download.excel('/monitor/jobLog/export', this.queryParams); } } }; diff --git a/ruoyi-ui/src/views/monitor/logininfor/index.vue b/ruoyi-ui/src/views/monitor/logininfor/index.vue index 2b83181a4..5b113016d 100644 --- a/ruoyi-ui/src/views/monitor/logininfor/index.vue +++ b/ruoyi-ui/src/views/monitor/logininfor/index.vue @@ -216,7 +216,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/monitor/logininfor/export', this.queryParams); + this.$download.excel('/monitor/logininfor/export', this.queryParams); } } }; diff --git a/ruoyi-ui/src/views/monitor/operlog/index.vue b/ruoyi-ui/src/views/monitor/operlog/index.vue index fafaf6005..35fd2b812 100644 --- a/ruoyi-ui/src/views/monitor/operlog/index.vue +++ b/ruoyi-ui/src/views/monitor/operlog/index.vue @@ -303,7 +303,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/monitor/operlog/export', this.queryParams); + this.$download.excel('/monitor/operlog/export', this.queryParams); } } }; diff --git a/ruoyi-ui/src/views/system/config/index.vue b/ruoyi-ui/src/views/system/config/index.vue index c4029c328..b03791755 100644 --- a/ruoyi-ui/src/views/system/config/index.vue +++ b/ruoyi-ui/src/views/system/config/index.vue @@ -334,7 +334,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/system/config/export', this.queryParams); + this.$download.excel('/system/config/export', this.queryParams); }, /** 刷新缓存按钮操作 */ handleRefreshCache() { diff --git a/ruoyi-ui/src/views/system/dict/data.vue b/ruoyi-ui/src/views/system/dict/data.vue index 3f0b5e077..c7a90067e 100644 --- a/ruoyi-ui/src/views/system/dict/data.vue +++ b/ruoyi-ui/src/views/system/dict/data.vue @@ -380,7 +380,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/system/dict/data/export', this.queryParams); + this.$download.excel('/system/dict/data/export', this.queryParams); } } }; diff --git a/ruoyi-ui/src/views/system/dict/index.vue b/ruoyi-ui/src/views/system/dict/index.vue index c1c664936..6daa8679c 100644 --- a/ruoyi-ui/src/views/system/dict/index.vue +++ b/ruoyi-ui/src/views/system/dict/index.vue @@ -338,7 +338,7 @@ export default { }, /** 导出按钮操作 */ handleExport() { - this.downLoadExcel('/system/dict/type/export', this.queryParams); + this.$download.excel('/system/dict/type/export', this.queryParams); }, /** 刷新缓存按钮操作 */ handleRefreshCache() { diff --git a/ruoyi-ui/src/views/system/oss/index.vue b/ruoyi-ui/src/views/system/oss/index.vue index 5b6719197..fdfed1650 100644 --- a/ruoyi-ui/src/views/system/oss/index.vue +++ b/ruoyi-ui/src/views/system/oss/index.vue @@ -188,7 +188,6 @@