Commit bbe71ec2c893abc4e8a4a983f48e416f9e3e94db
1 parent
aa38c0f9
完成部分权限的认证操作的迁移
Showing
39 changed files
with
370 additions
and
394 deletions
http-client.env.json
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java
| ... | ... | @@ -9,17 +9,15 @@ import com.ruoyi.framework.web.domain.Server; |
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | 11 | * 服务器监控 |
| 12 | - * | |
| 12 | + * | |
| 13 | 13 | * @author ruoyi |
| 14 | 14 | */ |
| 15 | 15 | @RestController |
| 16 | 16 | @RequestMapping("/monitor/server") |
| 17 | -public class ServerController | |
| 18 | -{ | |
| 17 | +public class ServerController { | |
| 19 | 18 | @PreAuthorize("@ss.hasPermi('monitor:server:list')") |
| 20 | 19 | @GetMapping() |
| 21 | - public AjaxResult getInfo() throws Exception | |
| 22 | - { | |
| 20 | + public AjaxResult getInfo() throws Exception { | |
| 23 | 21 | Server server = new Server(); |
| 24 | 22 | server.copyTo(); |
| 25 | 23 | return AjaxResult.success(server); | ... | ... |
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/SwaggerController.java renamed to ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SwaggerController.java
| 1 | -package com.ruoyi.web.controller.tool; | |
| 2 | - | |
| 3 | -import org.springframework.security.access.prepost.PreAuthorize; | |
| 4 | -import org.springframework.stereotype.Controller; | |
| 5 | -import org.springframework.web.bind.annotation.GetMapping; | |
| 6 | -import org.springframework.web.bind.annotation.RequestMapping; | |
| 7 | -import com.ruoyi.common.core.controller.BaseController; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * swagger 接口 | |
| 11 | - * | |
| 12 | - * @author ruoyi | |
| 13 | - */ | |
| 14 | -@Controller | |
| 15 | -@RequestMapping("/tool/swagger") | |
| 16 | -public class SwaggerController extends BaseController | |
| 17 | -{ | |
| 18 | - @PreAuthorize("@ss.hasPermi('tool:swagger:view')") | |
| 19 | - @GetMapping() | |
| 20 | - public String index() | |
| 21 | - { | |
| 22 | - return redirect("/swagger-ui.html"); | |
| 23 | - } | |
| 24 | -} | |
| 1 | +package com.ruoyi.web.controller.tool; | |
| 2 | + | |
| 3 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 4 | +import org.springframework.stereotype.Controller; | |
| 5 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 6 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 7 | +import com.ruoyi.common.core.controller.BaseController; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * swagger 接口 | |
| 11 | + * | |
| 12 | + * @author ruoyi | |
| 13 | + */ | |
| 14 | +@Controller | |
| 15 | +@RequestMapping("/tool/swagger") | |
| 16 | +public class SwaggerController extends BaseController | |
| 17 | +{ | |
| 18 | + @PreAuthorize("@ss.hasPermi('tool:swagger:view')") | |
| 19 | + @GetMapping() | |
| 20 | + public String index() | |
| 21 | + { | |
| 22 | + return redirect("/swagger-ui.html"); | |
| 23 | + } | |
| 24 | +} | ... | ... |
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnline.java
| ... | ... | @@ -5,31 +5,45 @@ package com.ruoyi.system.domain; |
| 5 | 5 | * |
| 6 | 6 | * @author ruoyi |
| 7 | 7 | */ |
| 8 | -public class SysUserOnline | |
| 9 | -{ | |
| 10 | - /** 会话编号 */ | |
| 8 | +public class SysUserOnline { | |
| 9 | + /** | |
| 10 | + * 会话编号 | |
| 11 | + */ | |
| 11 | 12 | private String tokenId; |
| 12 | 13 | |
| 13 | - /** 部门名称 */ | |
| 14 | + /** | |
| 15 | + * 部门名称 | |
| 16 | + */ | |
| 14 | 17 | private String deptName; |
| 15 | 18 | |
| 16 | - /** 用户名称 */ | |
| 19 | + /** | |
| 20 | + * 用户名称 | |
| 21 | + */ | |
| 17 | 22 | private String userName; |
| 18 | 23 | |
| 19 | - /** 登录IP地址 */ | |
| 24 | + /** | |
| 25 | + * 登录IP地址 | |
| 26 | + */ | |
| 20 | 27 | private String ipaddr; |
| 21 | 28 | |
| 22 | - /** 登录地址 */ | |
| 29 | + /** | |
| 30 | + * 登录地址 | |
| 31 | + */ | |
| 23 | 32 | private String loginLocation; |
| 24 | 33 | |
| 25 | - /** 浏览器类型 */ | |
| 34 | + /** | |
| 35 | + * 浏览器类型 | |
| 36 | + */ | |
| 26 | 37 | private String browser; |
| 27 | 38 | |
| 28 | - /** 操作系统 */ | |
| 39 | + /** | |
| 40 | + * 操作系统 | |
| 41 | + */ | |
| 29 | 42 | private String os; |
| 30 | 43 | |
| 31 | - /** 登录时间 */ | |
| 44 | + /** | |
| 45 | + * 登录时间 | |
| 46 | + */ | |
| 32 | 47 | private Long loginTime; |
| 33 | 48 | |
| 34 | - | |
| 35 | 49 | } | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
| ... | ... | @@ -8,14 +8,13 @@ import java.lang.annotation.Target; |
| 8 | 8 | |
| 9 | 9 | /** |
| 10 | 10 | * 数据权限过滤注解 |
| 11 | - * | |
| 11 | + * | |
| 12 | 12 | * @author ruoyi |
| 13 | 13 | */ |
| 14 | 14 | @Target(ElementType.METHOD) |
| 15 | 15 | @Retention(RetentionPolicy.RUNTIME) |
| 16 | 16 | @Documented |
| 17 | -public @interface DataScope | |
| 18 | -{ | |
| 17 | +public @interface DataScope { | |
| 19 | 18 | /** |
| 20 | 19 | * 部门表的别名 |
| 21 | 20 | */ | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java
| ... | ... | @@ -6,21 +6,21 @@ import java.lang.annotation.Inherited; |
| 6 | 6 | import java.lang.annotation.Retention; |
| 7 | 7 | import java.lang.annotation.RetentionPolicy; |
| 8 | 8 | import java.lang.annotation.Target; |
| 9 | + | |
| 9 | 10 | import com.ruoyi.common.enums.DataSourceType; |
| 10 | 11 | |
| 11 | 12 | /** |
| 12 | 13 | * 自定义多数据源切换注解 |
| 13 | - * | |
| 14 | + * <p> | |
| 14 | 15 | * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 |
| 15 | 16 | * |
| 16 | 17 | * @author ruoyi |
| 17 | 18 | */ |
| 18 | -@Target({ ElementType.METHOD, ElementType.TYPE }) | |
| 19 | +@Target({ElementType.METHOD, ElementType.TYPE}) | |
| 19 | 20 | @Retention(RetentionPolicy.RUNTIME) |
| 20 | 21 | @Documented |
| 21 | 22 | @Inherited |
| 22 | -public @interface DataSource | |
| 23 | -{ | |
| 23 | +public @interface DataSource { | |
| 24 | 24 | /** |
| 25 | 25 | * 切换数据源名称 |
| 26 | 26 | */ | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java
| ... | ... | @@ -9,15 +9,13 @@ import java.lang.annotation.Target; |
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | 11 | * 自定义注解防止表单重复提交 |
| 12 | - * | |
| 13 | - * @author ruoyi | |
| 14 | 12 | * |
| 13 | + * @author ruoyi | |
| 15 | 14 | */ |
| 16 | 15 | @Inherited |
| 17 | 16 | @Target(ElementType.METHOD) |
| 18 | 17 | @Retention(RetentionPolicy.RUNTIME) |
| 19 | 18 | @Documented |
| 20 | -public @interface RepeatSubmit | |
| 21 | -{ | |
| 19 | +public @interface RepeatSubmit { | |
| 22 | 20 | |
| 23 | 21 | } | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java
| ... | ... | @@ -2,7 +2,7 @@ package com.ruoyi.common.constant; |
| 2 | 2 | |
| 3 | 3 | /** |
| 4 | 4 | * 代码生成通用常量 |
| 5 | - * | |
| 5 | + * | |
| 6 | 6 | * @author ruoyi |
| 7 | 7 | */ |
| 8 | 8 | public class GenConstants |
| ... | ... | @@ -13,6 +13,7 @@ public class GenConstants |
| 13 | 13 | /** 树表(增删改查) */ |
| 14 | 14 | public static final String TPL_TREE = "tree"; |
| 15 | 15 | |
| 16 | + | |
| 16 | 17 | /** 树编码字段 */ |
| 17 | 18 | public static final String TREE_CODE = "treeCode"; |
| 18 | 19 | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
| ... | ... | @@ -8,45 +8,38 @@ import javax.servlet.ServletException; |
| 8 | 8 | import javax.servlet.ServletRequest; |
| 9 | 9 | import javax.servlet.ServletResponse; |
| 10 | 10 | import javax.servlet.http.HttpServletRequest; |
| 11 | + | |
| 11 | 12 | import org.springframework.http.MediaType; |
| 12 | 13 | import com.ruoyi.common.utils.StringUtils; |
| 13 | 14 | |
| 14 | 15 | /** |
| 15 | 16 | * Repeatable 过滤器 |
| 16 | - * | |
| 17 | + * | |
| 17 | 18 | * @author ruoyi |
| 18 | 19 | */ |
| 19 | -public class RepeatableFilter implements Filter | |
| 20 | -{ | |
| 20 | +public class RepeatableFilter implements Filter { | |
| 21 | 21 | @Override |
| 22 | - public void init(FilterConfig filterConfig) throws ServletException | |
| 23 | - { | |
| 22 | + public void init(FilterConfig filterConfig) throws ServletException { | |
| 24 | 23 | |
| 25 | 24 | } |
| 26 | 25 | |
| 27 | 26 | @Override |
| 28 | 27 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
| 29 | - throws IOException, ServletException | |
| 30 | - { | |
| 28 | + throws IOException, ServletException { | |
| 31 | 29 | ServletRequest requestWrapper = null; |
| 32 | 30 | if (request instanceof HttpServletRequest |
| 33 | - && StringUtils.equalsAnyIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) | |
| 34 | - { | |
| 31 | + && StringUtils.equalsAnyIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { | |
| 35 | 32 | requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); |
| 36 | 33 | } |
| 37 | - if (null == requestWrapper) | |
| 38 | - { | |
| 34 | + if (null == requestWrapper) { | |
| 39 | 35 | chain.doFilter(request, response); |
| 40 | - } | |
| 41 | - else | |
| 42 | - { | |
| 36 | + } else { | |
| 43 | 37 | chain.doFilter(requestWrapper, response); |
| 44 | 38 | } |
| 45 | 39 | } |
| 46 | 40 | |
| 47 | 41 | @Override |
| 48 | - public void destroy() | |
| 49 | - { | |
| 42 | + public void destroy() { | |
| 50 | 43 | |
| 51 | 44 | } |
| 52 | 45 | } | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
| ... | ... | @@ -9,19 +9,18 @@ import javax.servlet.ServletInputStream; |
| 9 | 9 | import javax.servlet.ServletResponse; |
| 10 | 10 | import javax.servlet.http.HttpServletRequest; |
| 11 | 11 | import javax.servlet.http.HttpServletRequestWrapper; |
| 12 | + | |
| 12 | 13 | import com.ruoyi.common.utils.http.HttpHelper; |
| 13 | 14 | |
| 14 | 15 | /** |
| 15 | 16 | * 构建可重复读取inputStream的request |
| 16 | - * | |
| 17 | + * | |
| 17 | 18 | * @author ruoyi |
| 18 | 19 | */ |
| 19 | -public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper | |
| 20 | -{ | |
| 20 | +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { | |
| 21 | 21 | private final byte[] body; |
| 22 | 22 | |
| 23 | - public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException | |
| 24 | - { | |
| 23 | + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException { | |
| 25 | 24 | super(request); |
| 26 | 25 | request.setCharacterEncoding("UTF-8"); |
| 27 | 26 | response.setCharacterEncoding("UTF-8"); |
| ... | ... | @@ -30,41 +29,34 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper |
| 30 | 29 | } |
| 31 | 30 | |
| 32 | 31 | @Override |
| 33 | - public BufferedReader getReader() throws IOException | |
| 34 | - { | |
| 32 | + public BufferedReader getReader() throws IOException { | |
| 35 | 33 | return new BufferedReader(new InputStreamReader(getInputStream())); |
| 36 | 34 | } |
| 37 | 35 | |
| 38 | 36 | @Override |
| 39 | - public ServletInputStream getInputStream() throws IOException | |
| 40 | - { | |
| 37 | + public ServletInputStream getInputStream() throws IOException { | |
| 41 | 38 | |
| 42 | 39 | final ByteArrayInputStream bais = new ByteArrayInputStream(body); |
| 43 | 40 | |
| 44 | - return new ServletInputStream() | |
| 45 | - { | |
| 41 | + return new ServletInputStream() { | |
| 46 | 42 | |
| 47 | 43 | @Override |
| 48 | - public int read() throws IOException | |
| 49 | - { | |
| 44 | + public int read() throws IOException { | |
| 50 | 45 | return bais.read(); |
| 51 | 46 | } |
| 52 | 47 | |
| 53 | 48 | @Override |
| 54 | - public boolean isFinished() | |
| 55 | - { | |
| 49 | + public boolean isFinished() { | |
| 56 | 50 | return false; |
| 57 | 51 | } |
| 58 | 52 | |
| 59 | 53 | @Override |
| 60 | - public boolean isReady() | |
| 61 | - { | |
| 54 | + public boolean isReady() { | |
| 62 | 55 | return false; |
| 63 | 56 | } |
| 64 | 57 | |
| 65 | 58 | @Override |
| 66 | - public void setReadListener(ReadListener readListener) | |
| 67 | - { | |
| 59 | + public void setReadListener(ReadListener readListener) { | |
| 68 | 60 | |
| 69 | 61 | } |
| 70 | 62 | }; | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java
| ... | ... | @@ -55,46 +55,6 @@ public class Server { |
| 55 | 55 | */ |
| 56 | 56 | private List<SysFile> sysFiles = new LinkedList<SysFile>(); |
| 57 | 57 | |
| 58 | - public Cpu getCpu() { | |
| 59 | - return cpu; | |
| 60 | - } | |
| 61 | - | |
| 62 | - public void setCpu(Cpu cpu) { | |
| 63 | - this.cpu = cpu; | |
| 64 | - } | |
| 65 | - | |
| 66 | - public Mem getMem() { | |
| 67 | - return mem; | |
| 68 | - } | |
| 69 | - | |
| 70 | - public void setMem(Mem mem) { | |
| 71 | - this.mem = mem; | |
| 72 | - } | |
| 73 | - | |
| 74 | - public Jvm getJvm() { | |
| 75 | - return jvm; | |
| 76 | - } | |
| 77 | - | |
| 78 | - public void setJvm(Jvm jvm) { | |
| 79 | - this.jvm = jvm; | |
| 80 | - } | |
| 81 | - | |
| 82 | - public Sys getSys() { | |
| 83 | - return sys; | |
| 84 | - } | |
| 85 | - | |
| 86 | - public void setSys(Sys sys) { | |
| 87 | - this.sys = sys; | |
| 88 | - } | |
| 89 | - | |
| 90 | - public List<SysFile> getSysFiles() { | |
| 91 | - return sysFiles; | |
| 92 | - } | |
| 93 | - | |
| 94 | - public void setSysFiles(List<SysFile> sysFiles) { | |
| 95 | - this.sysFiles = sysFiles; | |
| 96 | - } | |
| 97 | - | |
| 98 | 58 | public void copyTo() throws Exception { |
| 99 | 59 | SystemInfo si = new SystemInfo(); |
| 100 | 60 | HardwareAbstractionLayer hal = si.getHardware(); | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java
| ... | ... | @@ -38,22 +38,10 @@ public class Cpu { |
| 38 | 38 | */ |
| 39 | 39 | private double free; |
| 40 | 40 | |
| 41 | - public int getCpuNum() { | |
| 42 | - return cpuNum; | |
| 43 | - } | |
| 44 | - | |
| 45 | - public void setCpuNum(int cpuNum) { | |
| 46 | - this.cpuNum = cpuNum; | |
| 47 | - } | |
| 48 | - | |
| 49 | 41 | public double getTotal() { |
| 50 | 42 | return Arith.round(Arith.mul(total, 100), 2); |
| 51 | 43 | } |
| 52 | 44 | |
| 53 | - public void setTotal(double total) { | |
| 54 | - this.total = total; | |
| 55 | - } | |
| 56 | - | |
| 57 | 45 | public double getSys() { |
| 58 | 46 | return Arith.round(Arith.mul(sys / total, 100), 2); |
| 59 | 47 | } | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java
| ... | ... | @@ -40,18 +40,10 @@ public class Jvm { |
| 40 | 40 | return Arith.div(total, (1024 * 1024), 2); |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | - public void setTotal(double total) { | |
| 44 | - this.total = total; | |
| 45 | - } | |
| 46 | - | |
| 47 | 43 | public double getMax() { |
| 48 | 44 | return Arith.div(max, (1024 * 1024), 2); |
| 49 | 45 | } |
| 50 | 46 | |
| 51 | - public void setMax(double max) { | |
| 52 | - this.max = max; | |
| 53 | - } | |
| 54 | - | |
| 55 | 47 | public double getFree() { |
| 56 | 48 | return Arith.div(free, (1024 * 1024), 2); |
| 57 | 49 | } |
| ... | ... | @@ -75,22 +67,6 @@ public class Jvm { |
| 75 | 67 | return ManagementFactory.getRuntimeMXBean().getVmName(); |
| 76 | 68 | } |
| 77 | 69 | |
| 78 | - public String getVersion() { | |
| 79 | - return version; | |
| 80 | - } | |
| 81 | - | |
| 82 | - public void setVersion(String version) { | |
| 83 | - this.version = version; | |
| 84 | - } | |
| 85 | - | |
| 86 | - public String getHome() { | |
| 87 | - return home; | |
| 88 | - } | |
| 89 | - | |
| 90 | - public void setHome(String home) { | |
| 91 | - this.home = home; | |
| 92 | - } | |
| 93 | - | |
| 94 | 70 | /** |
| 95 | 71 | * JDK启动时间 |
| 96 | 72 | */ | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java
| ... | ... | @@ -27,27 +27,16 @@ public class Mem { |
| 27 | 27 | return Arith.div(total, (1024 * 1024 * 1024), 2); |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | - public void setTotal(long total) { | |
| 31 | - this.total = total; | |
| 32 | - } | |
| 33 | - | |
| 34 | 30 | public double getUsed() { |
| 35 | 31 | return Arith.div(used, (1024 * 1024 * 1024), 2); |
| 36 | 32 | } |
| 37 | 33 | |
| 38 | - public void setUsed(long used) { | |
| 39 | - this.used = used; | |
| 40 | - } | |
| 41 | - | |
| 42 | 34 | public double getFree() { |
| 43 | 35 | return Arith.div(free, (1024 * 1024 * 1024), 2); |
| 44 | 36 | } |
| 45 | 37 | |
| 46 | - public void setFree(long free) { | |
| 47 | - this.free = free; | |
| 48 | - } | |
| 49 | - | |
| 50 | 38 | public double getUsage() { |
| 51 | 39 | return Arith.mul(Arith.div(used, total, 4), 100); |
| 52 | 40 | } |
| 41 | + | |
| 53 | 42 | } | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java
| ... | ... | @@ -31,43 +31,4 @@ public class Sys { |
| 31 | 31 | */ |
| 32 | 32 | private String osArch; |
| 33 | 33 | |
| 34 | - public String getComputerName() { | |
| 35 | - return computerName; | |
| 36 | - } | |
| 37 | - | |
| 38 | - public void setComputerName(String computerName) { | |
| 39 | - this.computerName = computerName; | |
| 40 | - } | |
| 41 | - | |
| 42 | - public String getComputerIp() { | |
| 43 | - return computerIp; | |
| 44 | - } | |
| 45 | - | |
| 46 | - public void setComputerIp(String computerIp) { | |
| 47 | - this.computerIp = computerIp; | |
| 48 | - } | |
| 49 | - | |
| 50 | - public String getUserDir() { | |
| 51 | - return userDir; | |
| 52 | - } | |
| 53 | - | |
| 54 | - public void setUserDir(String userDir) { | |
| 55 | - this.userDir = userDir; | |
| 56 | - } | |
| 57 | - | |
| 58 | - public String getOsName() { | |
| 59 | - return osName; | |
| 60 | - } | |
| 61 | - | |
| 62 | - public void setOsName(String osName) { | |
| 63 | - this.osName = osName; | |
| 64 | - } | |
| 65 | - | |
| 66 | - public String getOsArch() { | |
| 67 | - return osArch; | |
| 68 | - } | |
| 69 | - | |
| 70 | - public void setOsArch(String osArch) { | |
| 71 | - this.osArch = osArch; | |
| 72 | - } | |
| 73 | 34 | } | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java
| ... | ... | @@ -41,59 +41,4 @@ public class SysFile { |
| 41 | 41 | */ |
| 42 | 42 | private double usage; |
| 43 | 43 | |
| 44 | - public String getDirName() { | |
| 45 | - return dirName; | |
| 46 | - } | |
| 47 | - | |
| 48 | - public void setDirName(String dirName) { | |
| 49 | - this.dirName = dirName; | |
| 50 | - } | |
| 51 | - | |
| 52 | - public String getSysTypeName() { | |
| 53 | - return sysTypeName; | |
| 54 | - } | |
| 55 | - | |
| 56 | - public void setSysTypeName(String sysTypeName) { | |
| 57 | - this.sysTypeName = sysTypeName; | |
| 58 | - } | |
| 59 | - | |
| 60 | - public String getTypeName() { | |
| 61 | - return typeName; | |
| 62 | - } | |
| 63 | - | |
| 64 | - public void setTypeName(String typeName) { | |
| 65 | - this.typeName = typeName; | |
| 66 | - } | |
| 67 | - | |
| 68 | - public String getTotal() { | |
| 69 | - return total; | |
| 70 | - } | |
| 71 | - | |
| 72 | - public void setTotal(String total) { | |
| 73 | - this.total = total; | |
| 74 | - } | |
| 75 | - | |
| 76 | - public String getFree() { | |
| 77 | - return free; | |
| 78 | - } | |
| 79 | - | |
| 80 | - public void setFree(String free) { | |
| 81 | - this.free = free; | |
| 82 | - } | |
| 83 | - | |
| 84 | - public String getUsed() { | |
| 85 | - return used; | |
| 86 | - } | |
| 87 | - | |
| 88 | - public void setUsed(String used) { | |
| 89 | - this.used = used; | |
| 90 | - } | |
| 91 | - | |
| 92 | - public double getUsage() { | |
| 93 | - return usage; | |
| 94 | - } | |
| 95 | - | |
| 96 | - public void setUsage(double usage) { | |
| 97 | - this.usage = usage; | |
| 98 | - } | |
| 99 | 44 | } | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
| ... | ... | @@ -17,68 +17,6 @@ import com.ruoyi.common.utils.StringUtils; |
| 17 | 17 | */ |
| 18 | 18 | @Service("ss") |
| 19 | 19 | public class PermissionService { |
| 20 | - /** | |
| 21 | - * 所有权限标识 | |
| 22 | - */ | |
| 23 | - private static final String ALL_PERMISSION = "*:*:*"; | |
| 24 | - | |
| 25 | - /** | |
| 26 | - * 管理员角色权限标识 | |
| 27 | - */ | |
| 28 | - private static final String SUPER_ADMIN = "admin"; | |
| 29 | - | |
| 30 | - @Autowired | |
| 31 | - private TokenService tokenService; | |
| 32 | - | |
| 33 | - /** | |
| 34 | - * 验证用户是否具备某权限 | |
| 35 | - * | |
| 36 | - * @param permission 权限字符串 | |
| 37 | - * @return 用户是否具备某权限 | |
| 38 | - */ | |
| 39 | - public boolean hasPermi(String permission) { | |
| 40 | - if (StringUtils.isEmpty(permission)) { | |
| 41 | - return false; | |
| 42 | - } | |
| 43 | - LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); | |
| 44 | - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { | |
| 45 | - return false; | |
| 46 | - } | |
| 47 | - return hasPermissions(loginUser.getPermissions(), permission); | |
| 48 | - } | |
| 49 | - | |
| 50 | - /** | |
| 51 | - * 验证用户是否不具备某权限,与 hasPermi逻辑相反 | |
| 52 | - * | |
| 53 | - * @param permission 权限字符串 | |
| 54 | - * @return 用户是否不具备某权限 | |
| 55 | - */ | |
| 56 | - public boolean lacksPermi(String permission) { | |
| 57 | - return hasPermi(permission) != true; | |
| 58 | - } | |
| 59 | - | |
| 60 | - /** | |
| 61 | - * 验证用户是否具有以下任意一个权限 | |
| 62 | - * | |
| 63 | - * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 | |
| 64 | - * @return 用户是否具有以下任意一个权限 | |
| 65 | - */ | |
| 66 | - public boolean hasAnyPermi(String permissions) { | |
| 67 | - if (StringUtils.isEmpty(permissions)) { | |
| 68 | - return false; | |
| 69 | - } | |
| 70 | - LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); | |
| 71 | - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { | |
| 72 | - return false; | |
| 73 | - } | |
| 74 | - Set<String> authorities = loginUser.getPermissions(); | |
| 75 | - for (String permission : permissions.split(PERMISSION_DELIMETER)) { | |
| 76 | - if (permission != null && hasPermissions(authorities, permission)) { | |
| 77 | - return true; | |
| 78 | - } | |
| 79 | - } | |
| 80 | - return false; | |
| 81 | - } | |
| 82 | 20 | |
| 83 | 21 | /** |
| 84 | 22 | * 判断用户是否拥有某个角色 |
| ... | ... | @@ -104,16 +42,6 @@ public class PermissionService { |
| 104 | 42 | } |
| 105 | 43 | |
| 106 | 44 | /** |
| 107 | - * 验证用户是否不具备某角色,与 isRole逻辑相反。 | |
| 108 | - * | |
| 109 | - * @param role 角色名称 | |
| 110 | - * @return 用户是否不具备某角色 | |
| 111 | - */ | |
| 112 | - public boolean lacksRole(String role) { | |
| 113 | - return hasRole(role) != true; | |
| 114 | - } | |
| 115 | - | |
| 116 | - /** | |
| 117 | 45 | * 验证用户是否具有以下任意一个角色 |
| 118 | 46 | * |
| 119 | 47 | * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 |
| ... | ... | @@ -135,14 +63,4 @@ public class PermissionService { |
| 135 | 63 | return false; |
| 136 | 64 | } |
| 137 | 65 | |
| 138 | - /** | |
| 139 | - * 判断是否包含权限 | |
| 140 | - * | |
| 141 | - * @param permissions 权限列表 | |
| 142 | - * @param permission 权限字符串 | |
| 143 | - * @return 用户是否具备某权限 | |
| 144 | - */ | |
| 145 | - private boolean hasPermissions(Set<String> permissions, String permission) { | |
| 146 | - return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); | |
| 147 | - } | |
| 148 | 66 | } | ... | ... |
ruoyi-ui/src/utils/request.js
| ... | ... | @@ -61,7 +61,9 @@ service.interceptors.response.use(res => { |
| 61 | 61 | } |
| 62 | 62 | ).then(() => { |
| 63 | 63 | store.dispatch('LogOut').then(() => { |
| 64 | - location.href = '/index'; | |
| 64 | + if (location.pathname !== '/login') { // 避免重复跳转 | |
| 65 | + location.href = '/index'; | |
| 66 | + } | |
| 65 | 67 | }) |
| 66 | 68 | }) |
| 67 | 69 | } else if (code === 500) { | ... | ... |
ruoyi-ui/src/views/system/role/index.vue
| ... | ... | @@ -392,6 +392,7 @@ export default { |
| 392 | 392 | menuIds: [], |
| 393 | 393 | dataScope: undefined, |
| 394 | 394 | deptCheckStrictly: false, |
| 395 | + menuCheckStrictly: true, | |
| 395 | 396 | remark: undefined |
| 396 | 397 | }; |
| 397 | 398 | this.resetForm("form"); |
| ... | ... | @@ -471,8 +472,12 @@ export default { |
| 471 | 472 | }); |
| 472 | 473 | // 获得角色拥有的菜单集合 |
| 473 | 474 | listRoleMenus(id).then(response => { |
| 475 | + // 设置为严格,避免设置父节点自动选中子节点,解决半选中问题 | |
| 476 | + this.form.menuCheckStrictly = true | |
| 474 | 477 | // 设置选中 |
| 475 | - this.$refs.menu.setCheckedKeys(response.data, true, false); | |
| 478 | + this.$refs.menu.setCheckedKeys(response.data); | |
| 479 | + // 设置为非严格,继续使用半选中 | |
| 480 | + this.form.menuCheckStrictly = false | |
| 476 | 481 | }) |
| 477 | 482 | }, |
| 478 | 483 | /** 分配数据权限操作 */ |
| ... | ... | @@ -523,7 +528,7 @@ export default { |
| 523 | 528 | roleId: this.form.id, |
| 524 | 529 | dataScope: this.form.dataScope, |
| 525 | 530 | dataScopeDeptIds: this.form.dataScope !== SysDataScopeEnum.DEPT_CUSTOM ? [] : |
| 526 | - this.$refs.dept.getCheckedKeys(false) | |
| 531 | + this.$refs.dept.getCheckedKeys() | |
| 527 | 532 | }).then(response => { |
| 528 | 533 | this.msgSuccess("修改成功"); |
| 529 | 534 | this.openDataScope = false; |
| ... | ... | @@ -536,7 +541,7 @@ export default { |
| 536 | 541 | if (this.form.id !== undefined) { |
| 537 | 542 | assignRoleMenu({ |
| 538 | 543 | roleId: this.form.id, |
| 539 | - menuIds: this.$refs.menu.getCheckedKeys(true) | |
| 544 | + menuIds: [...this.$refs.menu.getCheckedKeys(), ...this.$refs.menu.getHalfCheckedKeys()] | |
| 540 | 545 | }).then(response => { |
| 541 | 546 | this.msgSuccess("修改成功"); |
| 542 | 547 | this.openMenu = false; | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/aop/OperateLogAspect.java
| ... | ... | @@ -233,11 +233,6 @@ public class OperateLogAspect { |
| 233 | 233 | } |
| 234 | 234 | } |
| 235 | 235 | |
| 236 | - private static void fillContentFields(SysOperateLogCreateReqVO operateLogVO) { | |
| 237 | - operateLogVO.setContent(CONTENT.get()); | |
| 238 | - operateLogVO.setExts(EXTS.get()); | |
| 239 | - } | |
| 240 | - | |
| 241 | 236 | private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) { |
| 242 | 237 | // 有 @OperateLog 注解的情况下 |
| 243 | 238 | if (operateLog != null) { | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java
| ... | ... | @@ -2,7 +2,7 @@ package cn.iocoder.dashboard.framework.security.core.handler; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | 4 | import cn.iocoder.dashboard.framework.security.config.SecurityProperties; |
| 5 | -import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService; | |
| 5 | +import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService; | |
| 6 | 6 | import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; |
| 7 | 7 | import cn.iocoder.dashboard.util.servlet.ServletUtils; |
| 8 | 8 | import org.springframework.security.core.Authentication; |
| ... | ... | @@ -26,7 +26,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { |
| 26 | 26 | private SecurityProperties securityProperties; |
| 27 | 27 | |
| 28 | 28 | @Resource |
| 29 | - private SecurityFrameworkService securityFrameworkService; | |
| 29 | + private SecurityAuthFrameworkService securityFrameworkService; | |
| 30 | 30 | |
| 31 | 31 | @Override |
| 32 | 32 | public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityFrameworkService.java renamed to src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityAuthFrameworkService.java
| ... | ... | @@ -4,16 +4,11 @@ import cn.iocoder.dashboard.framework.security.core.LoginUser; |
| 4 | 4 | import org.springframework.security.core.userdetails.UserDetailsService; |
| 5 | 5 | |
| 6 | 6 | /** |
| 7 | - * Security 框架 Service 接口,定义 security 组件需要的功能 | |
| 7 | + * Security 框架 Auth Service 接口,定义 security 组件需要的功能 | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 8 | 10 | */ |
| 9 | -public interface SecurityFrameworkService extends UserDetailsService { | |
| 10 | - | |
| 11 | - /** | |
| 12 | - * 基于 token 退出登录 | |
| 13 | - * | |
| 14 | - * @param token token | |
| 15 | - */ | |
| 16 | - void logout(String token); | |
| 11 | +public interface SecurityAuthFrameworkService extends UserDetailsService { | |
| 17 | 12 | |
| 18 | 13 | /** |
| 19 | 14 | * 校验 token 的有效性,并获取用户信息 |
| ... | ... | @@ -32,4 +27,11 @@ public interface SecurityFrameworkService extends UserDetailsService { |
| 32 | 27 | */ |
| 33 | 28 | LoginUser mockLogin(Long userId); |
| 34 | 29 | |
| 30 | + /** | |
| 31 | + * 基于 token 退出登录 | |
| 32 | + * | |
| 33 | + * @param token token | |
| 34 | + */ | |
| 35 | + void logout(String token); | |
| 36 | + | |
| 35 | 37 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityPermissionFrameworkService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core.service; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Security 框架 Permission Service 接口,定义 security 组件需要的功能 | |
| 5 | + * | |
| 6 | + * @author 芋道源码 | |
| 7 | + */ | |
| 8 | +public interface SecurityPermissionFrameworkService { | |
| 9 | + | |
| 10 | + /** | |
| 11 | + * 判断是否有权限 | |
| 12 | + * | |
| 13 | + * @param permission 权限 | |
| 14 | + * @return 是否 | |
| 15 | + */ | |
| 16 | + boolean hasPermission(String permission); | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 判断是否有权限,任一一个即可 | |
| 20 | + * | |
| 21 | + * @param permissions 权限 | |
| 22 | + * @return 是否 | |
| 23 | + */ | |
| 24 | + boolean hasAnyPermissions(String... permissions); | |
| 25 | + | |
| 26 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/web/core/handler/GlobalExceptionHandler.java
| ... | ... | @@ -3,7 +3,9 @@ package cn.iocoder.dashboard.framework.web.core.handler; |
| 3 | 3 | import cn.iocoder.dashboard.common.exception.GlobalException; |
| 4 | 4 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 5 | 5 | import cn.iocoder.dashboard.common.pojo.CommonResult; |
| 6 | +import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | |
| 6 | 7 | import lombok.extern.slf4j.Slf4j; |
| 8 | +import org.springframework.security.access.AccessDeniedException; | |
| 7 | 9 | import org.springframework.validation.BindException; |
| 8 | 10 | import org.springframework.validation.FieldError; |
| 9 | 11 | import org.springframework.web.HttpRequestMethodNotSupportedException; |
| ... | ... | @@ -64,6 +66,9 @@ public class GlobalExceptionHandler { |
| 64 | 66 | if (ex instanceof ServiceException) { |
| 65 | 67 | return serviceExceptionHandler((ServiceException) ex); |
| 66 | 68 | } |
| 69 | + if (ex instanceof AccessDeniedException) { | |
| 70 | + return accessDeniedExceptionHandler(request, (AccessDeniedException) ex); | |
| 71 | + } | |
| 67 | 72 | if (ex instanceof GlobalException) { |
| 68 | 73 | return globalExceptionHandler(request, (GlobalException) ex); |
| 69 | 74 | } |
| ... | ... | @@ -131,7 +136,7 @@ public class GlobalExceptionHandler { |
| 131 | 136 | public CommonResult<?> validationException(ValidationException ex) { |
| 132 | 137 | log.warn("[constraintViolationExceptionHandler]", ex); |
| 133 | 138 | // 无法拼接明细的错误信息,因为 Dubbo Consumer 抛出 ValidationException 异常时,是直接的字符串信息,且人类不可读 |
| 134 | - return CommonResult.error(BAD_REQUEST.getCode(), "请求参数不正确"); | |
| 139 | + return CommonResult.error(BAD_REQUEST); | |
| 135 | 140 | } |
| 136 | 141 | |
| 137 | 142 | /** |
| ... | ... | @@ -159,6 +164,18 @@ public class GlobalExceptionHandler { |
| 159 | 164 | } |
| 160 | 165 | |
| 161 | 166 | /** |
| 167 | + * 处理 Spring Security 权限不足的异常 | |
| 168 | + * | |
| 169 | + * 来源是,使用 @PreAuthorize 注解,AOP 进行权限拦截 | |
| 170 | + */ | |
| 171 | + @ExceptionHandler(value = AccessDeniedException.class) | |
| 172 | + public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AccessDeniedException ex) { | |
| 173 | + log.warn("[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]", SecurityUtils.getLoginUserId(), | |
| 174 | + req.getRequestURL(), ex); | |
| 175 | + return CommonResult.error(FORBIDDEN); | |
| 176 | + } | |
| 177 | + | |
| 178 | + /** | |
| 162 | 179 | * 处理业务异常 ServiceException |
| 163 | 180 | * |
| 164 | 181 | * 例如说,商品库存不足,用户手机号已存在。 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthLoginReqVO.java
| ... | ... | @@ -7,9 +7,7 @@ import lombok.Builder; |
| 7 | 7 | import lombok.Data; |
| 8 | 8 | import lombok.NoArgsConstructor; |
| 9 | 9 | import org.hibernate.validator.constraints.Length; |
| 10 | -import org.springframework.validation.annotation.Validated; | |
| 11 | 10 | |
| 12 | -import javax.validation.Valid; | |
| 13 | 11 | import javax.validation.constraints.NotEmpty; |
| 14 | 12 | import javax.validation.constraints.Pattern; |
| 15 | 13 | |
| ... | ... | @@ -22,7 +20,7 @@ public class SysAuthLoginReqVO { |
| 22 | 20 | |
| 23 | 21 | @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") |
| 24 | 22 | @NotEmpty(message = "登陆账号不能为空") |
| 25 | - @Length(min = 5, max = 16, message = "账号长度为 5-16 位") | |
| 23 | + @Length(min = 4, max = 16, message = "账号长度为 4-16 位") | |
| 26 | 24 | @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") |
| 27 | 25 | private String username; |
| 28 | 26 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.http
0 → 100644
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java
| ... | ... | @@ -18,6 +18,7 @@ import io.swagger.annotations.Api; |
| 18 | 18 | import io.swagger.annotations.ApiImplicitParam; |
| 19 | 19 | import io.swagger.annotations.ApiImplicitParams; |
| 20 | 20 | import io.swagger.annotations.ApiOperation; |
| 21 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 21 | 22 | import org.springframework.validation.annotation.Validated; |
| 22 | 23 | import org.springframework.web.bind.annotation.*; |
| 23 | 24 | import org.springframework.web.multipart.MultipartFile; |
| ... | ... | @@ -41,7 +42,7 @@ public class SysUserController { |
| 41 | 42 | |
| 42 | 43 | @ApiOperation("获得用户分页列表") |
| 43 | 44 | @GetMapping("/page") |
| 44 | -// @PreAuthorize("@ss.hasPermi('system:user:list')") | |
| 45 | + @PreAuthorize("@ss.hasPermission('system:user:list')") | |
| 45 | 46 | public CommonResult<PageResult<SysUserPageItemRespVO>> pageUsers(@Validated SysUserPageReqVO reqVO) { |
| 46 | 47 | // 获得用户分页列表 |
| 47 | 48 | PageResult<SysUserDO> pageResult = userService.pageUsers(reqVO); | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMenuMapper.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX; | |
| 3 | 4 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO; |
| 4 | 5 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| 5 | -import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |
| 6 | 6 | import org.apache.ibatis.annotations.Mapper; |
| 7 | 7 | |
| 8 | 8 | import java.util.Collection; |
| 9 | +import java.util.Date; | |
| 9 | 10 | import java.util.List; |
| 10 | 11 | import java.util.stream.Collectors; |
| 11 | 12 | |
| 12 | 13 | @Mapper |
| 13 | -public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuDO> { | |
| 14 | +public interface SysRoleMenuMapper extends BaseMapperX<SysRoleMenuDO> { | |
| 14 | 15 | |
| 15 | 16 | default List<SysRoleMenuDO> selectListByRoleId(Long roleId) { |
| 16 | 17 | return selectList(new QueryWrapper<SysRoleMenuDO>().eq("role_id", roleId)); |
| ... | ... | @@ -32,4 +33,9 @@ public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuDO> { |
| 32 | 33 | .in("menu_id", menuIds)); |
| 33 | 34 | } |
| 34 | 35 | |
| 36 | + default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) { | |
| 37 | + return selectOne(new QueryWrapper<SysRoleMenuDO>().select("id") | |
| 38 | + .gt("update_time", maxUpdateTime).last("LIMIT 1")) != null; | |
| 39 | + } | |
| 40 | + | |
| 35 | 41 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/permission/SysRoleMenuRefreshConsumer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.consumer.permission; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; | |
| 4 | +import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleMenuRefreshMessage; | |
| 5 | +import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | +import org.springframework.stereotype.Component; | |
| 8 | + | |
| 9 | +import javax.annotation.Resource; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 针对 {@link SysRoleMenuRefreshMessage} 的消费者 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +@Component | |
| 17 | +@Slf4j | |
| 18 | +public class SysRoleMenuRefreshConsumer extends AbstractChannelMessageListener<SysRoleMenuRefreshMessage> { | |
| 19 | + | |
| 20 | + @Resource | |
| 21 | + private SysPermissionService permissionService; | |
| 22 | + | |
| 23 | + @Override | |
| 24 | + public void onMessage(SysRoleMenuRefreshMessage message) { | |
| 25 | + log.info("[onMessage][收到 Role 与 Menu 的关联刷新消息]"); | |
| 26 | + permissionService.initLocalCache(); | |
| 27 | + } | |
| 28 | + | |
| 29 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/permission/SysRoleMenuRefreshMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.message.permission; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 角色与菜单数据刷新 Message | |
| 8 | + */ | |
| 9 | +@Data | |
| 10 | +public class SysRoleMenuRefreshMessage implements ChannelMessage { | |
| 11 | + | |
| 12 | + @Override | |
| 13 | + public String getChannel() { | |
| 14 | + return "system.role-menu.refresh"; | |
| 15 | + } | |
| 16 | + | |
| 17 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/permission/SysPermissionProducer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.producer.permission; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; | |
| 4 | +import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleMenuRefreshMessage; | |
| 5 | +import org.springframework.data.redis.core.StringRedisTemplate; | |
| 6 | +import org.springframework.stereotype.Component; | |
| 7 | + | |
| 8 | +import javax.annotation.Resource; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * Permission 权限相关消息的 Producer | |
| 12 | + */ | |
| 13 | +@Component | |
| 14 | +public class SysPermissionProducer { | |
| 15 | + | |
| 16 | + @Resource | |
| 17 | + private StringRedisTemplate stringRedisTemplate; | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 发送 {@link SysRoleMenuRefreshMessage} 消息 | |
| 21 | + */ | |
| 22 | + public void sendRoleMenuRefreshMessage() { | |
| 23 | + SysRoleMenuRefreshMessage message = new SysRoleMenuRefreshMessage(); | |
| 24 | + RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message); | |
| 25 | + } | |
| 26 | + | |
| 27 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.service.auth; |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService; | |
| 3 | +import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService; | |
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * 认证 Service 接口 |
| ... | ... | @@ -9,7 +9,7 @@ import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkSer |
| 9 | 9 | * |
| 10 | 10 | * @author 芋道源码 |
| 11 | 11 | */ |
| 12 | -public interface SysAuthService extends SecurityFrameworkService { | |
| 12 | +public interface SysAuthService extends SecurityAuthFrameworkService { | |
| 13 | 13 | |
| 14 | 14 | String login(String username, String password, String captchaUUID, String captchaCode); |
| 15 | 15 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysMenuService.java
| ... | ... | @@ -59,6 +59,14 @@ public interface SysMenuService { |
| 59 | 59 | List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes, |
| 60 | 60 | Collection<Integer> menusStatuses); |
| 61 | 61 | |
| 62 | + /** | |
| 63 | + * 获得权限对应的菜单数组 | |
| 64 | + * | |
| 65 | + * @param permission 权限标识 | |
| 66 | + * @return 数组 | |
| 67 | + */ | |
| 68 | + List<SysMenuDO> getMenuListByPermissionFromCache(String permission); | |
| 69 | + | |
| 62 | 70 | /* |
| 63 | 71 | * 创建菜单 |
| 64 | 72 | * | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.service.permission; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.security.core.service.SecurityPermissionFrameworkService; | |
| 3 | 4 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; |
| 4 | 5 | import org.springframework.lang.Nullable; |
| 5 | 6 | |
| ... | ... | @@ -14,12 +15,12 @@ import java.util.Set; |
| 14 | 15 | * |
| 15 | 16 | * @author 芋道源码 |
| 16 | 17 | */ |
| 17 | -public interface SysPermissionService { | |
| 18 | +public interface SysPermissionService extends SecurityPermissionFrameworkService { | |
| 18 | 19 | |
| 19 | 20 | /** |
| 20 | 21 | * 初始化 |
| 21 | 22 | */ |
| 22 | - void init(); | |
| 23 | + void initLocalCache(); | |
| 23 | 24 | |
| 24 | 25 | /** |
| 25 | 26 | * 获得角色们拥有的菜单列表,从缓存中获取 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java
| ... | ... | @@ -57,6 +57,16 @@ public interface SysRoleService { |
| 57 | 57 | boolean hasAnyAdmin(Collection<SysRoleDO> roleList); |
| 58 | 58 | |
| 59 | 59 | /** |
| 60 | + * 判断角色编号数组中,是否有管理员 | |
| 61 | + * | |
| 62 | + * @param ids 角色编号数组 | |
| 63 | + * @return 是否有管理员 | |
| 64 | + */ | |
| 65 | + default boolean hasAnyAdmin(Set<Long> ids) { | |
| 66 | + return hasAnyAdmin(listRolesFromCache(ids)); | |
| 67 | + } | |
| 68 | + | |
| 69 | + /** | |
| 60 | 70 | * 创建角色 |
| 61 | 71 | * |
| 62 | 72 | * @param reqVO 创建角色信息 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
| ... | ... | @@ -61,7 +61,7 @@ public class SysMenuServiceImpl implements SysMenuService { |
| 61 | 61 | * |
| 62 | 62 | * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 |
| 63 | 63 | */ |
| 64 | - private volatile Multimap<String, SysMenuDO> permMenuCache; | |
| 64 | + private volatile Multimap<String, SysMenuDO> permissionMenuCache; | |
| 65 | 65 | /** |
| 66 | 66 | * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 |
| 67 | 67 | */ |
| ... | ... | @@ -76,7 +76,7 @@ public class SysMenuServiceImpl implements SysMenuService { |
| 76 | 76 | private SysMenuProducer menuProducer; |
| 77 | 77 | |
| 78 | 78 | /** |
| 79 | - * 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存 | |
| 79 | + * 初始化 {@link #menuCache} 和 {@link #permissionMenuCache} 缓存 | |
| 80 | 80 | */ |
| 81 | 81 | @Override |
| 82 | 82 | @PostConstruct |
| ... | ... | @@ -95,7 +95,7 @@ public class SysMenuServiceImpl implements SysMenuService { |
| 95 | 95 | permMenuCacheBuilder.put(menuDO.getPermission(), menuDO); |
| 96 | 96 | }); |
| 97 | 97 | menuCache = menuCacheBuilder.build(); |
| 98 | - permMenuCache = permMenuCacheBuilder.build(); | |
| 98 | + permissionMenuCache = permMenuCacheBuilder.build(); | |
| 99 | 99 | assert menuList.size() > 0; // 断言,避免告警 |
| 100 | 100 | maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); |
| 101 | 101 | log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size()); |
| ... | ... | @@ -163,6 +163,11 @@ public class SysMenuServiceImpl implements SysMenuService { |
| 163 | 163 | } |
| 164 | 164 | |
| 165 | 165 | @Override |
| 166 | + public List<SysMenuDO> getMenuListByPermissionFromCache(String permission) { | |
| 167 | + return new ArrayList<>(permissionMenuCache.get(permission)); | |
| 168 | + } | |
| 169 | + | |
| 170 | + @Override | |
| 166 | 171 | public Long createMenu(SysMenuCreateReqVO reqVO) { |
| 167 | 172 | // 校验父菜单存在 |
| 168 | 173 | checkParentResource(reqVO.getParentId(), null); | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
| ... | ... | @@ -2,12 +2,16 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | +import cn.hutool.core.util.ArrayUtil; | |
| 6 | +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | |
| 7 | +import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | |
| 5 | 8 | import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMenuMapper; |
| 6 | 9 | import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysUserRoleMapper; |
| 7 | 10 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; |
| 8 | 11 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; |
| 9 | 12 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO; |
| 10 | 13 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO; |
| 14 | +import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysPermissionProducer; | |
| 11 | 15 | import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; |
| 12 | 16 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 13 | 17 | import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; |
| ... | ... | @@ -18,24 +22,29 @@ import com.google.common.collect.Multimap; |
| 18 | 22 | import lombok.extern.slf4j.Slf4j; |
| 19 | 23 | import org.springframework.stereotype.Service; |
| 20 | 24 | import org.springframework.transaction.annotation.Transactional; |
| 25 | +import org.springframework.transaction.support.TransactionSynchronization; | |
| 26 | +import org.springframework.transaction.support.TransactionSynchronizationManager; | |
| 21 | 27 | |
| 22 | 28 | import javax.annotation.PostConstruct; |
| 23 | 29 | import javax.annotation.Resource; |
| 24 | -import java.util.Collection; | |
| 25 | -import java.util.Collections; | |
| 26 | -import java.util.List; | |
| 27 | -import java.util.Set; | |
| 30 | +import java.util.*; | |
| 28 | 31 | |
| 29 | 32 | /** |
| 30 | 33 | * 权限 Service 实现类 |
| 31 | 34 | * |
| 32 | 35 | * @author 芋道源码 |
| 33 | 36 | */ |
| 34 | -@Service | |
| 37 | +@Service("ss") // 使用 Spring Security 的缩写,方便食用 | |
| 35 | 38 | @Slf4j |
| 36 | 39 | public class SysPermissionServiceImpl implements SysPermissionService { |
| 37 | 40 | |
| 38 | 41 | /** |
| 42 | + * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 | |
| 43 | + * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 | |
| 44 | + */ | |
| 45 | + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; | |
| 46 | + | |
| 47 | + /** | |
| 39 | 48 | * 角色编号与菜单编号的缓存映射 |
| 40 | 49 | * key:角色编号 |
| 41 | 50 | * value:菜单编号的数组 |
| ... | ... | @@ -51,6 +60,10 @@ public class SysPermissionServiceImpl implements SysPermissionService { |
| 51 | 60 | * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 |
| 52 | 61 | */ |
| 53 | 62 | private volatile Multimap<Long, Long> menuRoleCache; |
| 63 | + /** | |
| 64 | + * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 | |
| 65 | + */ | |
| 66 | + private volatile Date maxUpdateTime; | |
| 54 | 67 | |
| 55 | 68 | @Resource |
| 56 | 69 | private SysRoleMenuMapper roleMenuMapper; |
| ... | ... | @@ -62,14 +75,22 @@ public class SysPermissionServiceImpl implements SysPermissionService { |
| 62 | 75 | @Resource |
| 63 | 76 | private SysMenuService menuService; |
| 64 | 77 | |
| 78 | + @Resource | |
| 79 | + private SysPermissionProducer permissionProducer; | |
| 80 | + | |
| 65 | 81 | /** |
| 66 | 82 | * 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存 |
| 67 | 83 | */ |
| 68 | 84 | @Override |
| 69 | 85 | @PostConstruct |
| 70 | - public void init() { | |
| 86 | + public void initLocalCache() { | |
| 87 | + // 获取角色与菜单的关联列表,如果有更新 | |
| 88 | + List<SysRoleMenuDO> roleMenuList = this.loadRoleMenuIfUpdate(maxUpdateTime); | |
| 89 | + if (CollUtil.isEmpty(roleMenuList)) { | |
| 90 | + return; | |
| 91 | + } | |
| 92 | + | |
| 71 | 93 | // 初始化 roleMenuCache 和 menuRoleCache 缓存 |
| 72 | - List<SysRoleMenuDO> roleMenuList = roleMenuMapper.selectList(null); | |
| 73 | 94 | ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder(); |
| 74 | 95 | ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder(); |
| 75 | 96 | roleMenuList.forEach(roleMenuDO -> { |
| ... | ... | @@ -78,9 +99,32 @@ public class SysPermissionServiceImpl implements SysPermissionService { |
| 78 | 99 | }); |
| 79 | 100 | roleMenuCache = roleMenuCacheBuilder.build(); |
| 80 | 101 | menuRoleCache = menuRoleCacheBuilder.build(); |
| 102 | + assert roleMenuList.size() > 0; // 断言,避免告警 | |
| 103 | + maxUpdateTime = roleMenuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); | |
| 81 | 104 | log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size()); |
| 82 | 105 | } |
| 83 | 106 | |
| 107 | + /** | |
| 108 | + * 如果角色与菜单的关联发生变化,从数据库中获取最新的全量角色与菜单的关联。 | |
| 109 | + * 如果未发生变化,则返回空 | |
| 110 | + * | |
| 111 | + * @param maxUpdateTime 当前角色与菜单的关联的最大更新时间 | |
| 112 | + * @return 角色与菜单的关联列表 | |
| 113 | + */ | |
| 114 | + private List<SysRoleMenuDO> loadRoleMenuIfUpdate(Date maxUpdateTime) { | |
| 115 | + // 第一步,判断是否要更新。 | |
| 116 | + if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 | |
| 117 | + log.info("[loadRoleMenuIfUpdate][首次加载全量角色与菜单的关联]"); | |
| 118 | + } else { // 判断数据库中是否有更新的角色与菜单的关联 | |
| 119 | + if (!roleMenuMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) { | |
| 120 | + return null; | |
| 121 | + } | |
| 122 | + log.info("[loadRoleMenuIfUpdate][增量加载全量角色与菜单的关联]"); | |
| 123 | + } | |
| 124 | + // 第二步,如果有更新,则从数据库加载所有角色与菜单的关联 | |
| 125 | + return roleMenuMapper.selectList(); | |
| 126 | + } | |
| 127 | + | |
| 84 | 128 | @Override |
| 85 | 129 | public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes, |
| 86 | 130 | Collection<Integer> menusStatuses) { |
| ... | ... | @@ -140,6 +184,15 @@ public class SysPermissionServiceImpl implements SysPermissionService { |
| 140 | 184 | if (!CollectionUtil.isEmpty(deleteMenuIds)) { |
| 141 | 185 | roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds); |
| 142 | 186 | } |
| 187 | + // 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了 | |
| 188 | + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { | |
| 189 | + | |
| 190 | + @Override | |
| 191 | + public void afterCommit() { | |
| 192 | + permissionProducer.sendRoleMenuRefreshMessage(); | |
| 193 | + } | |
| 194 | + | |
| 195 | + }); | |
| 143 | 196 | } |
| 144 | 197 | |
| 145 | 198 | @Override |
| ... | ... | @@ -189,4 +242,39 @@ public class SysPermissionServiceImpl implements SysPermissionService { |
| 189 | 242 | // TODO 实现我 |
| 190 | 243 | } |
| 191 | 244 | |
| 245 | + @Override | |
| 246 | + public boolean hasPermission(String permission) { | |
| 247 | + return hasAnyPermissions(permission); | |
| 248 | + } | |
| 249 | + | |
| 250 | + @Override | |
| 251 | + public boolean hasAnyPermissions(String... permissions) { | |
| 252 | + // 如果为空,说明已经有权限 | |
| 253 | + if (ArrayUtil.isEmpty(permissions)) { | |
| 254 | + return true; | |
| 255 | + } | |
| 256 | + | |
| 257 | + // 获得当前登陆的角色。如果为空,说明没有权限 | |
| 258 | + Set<Long> roleIds = SecurityUtils.getLoginUserRoleIds(); | |
| 259 | + if (CollUtil.isEmpty(roleIds)) { | |
| 260 | + return false; | |
| 261 | + } | |
| 262 | + // 判断是否是超管。如果是,当然符合条件 | |
| 263 | + if (roleService.hasAnyAdmin(roleIds)) { | |
| 264 | + return true; | |
| 265 | + } | |
| 266 | + | |
| 267 | + // 遍历权限,判断是否有一个满足 | |
| 268 | + return Arrays.stream(permissions).anyMatch(permission -> { | |
| 269 | + List<SysMenuDO> menuList = menuService.getMenuListByPermissionFromCache(permission); | |
| 270 | + // 采用严格模式,如果权限找不到对应的 Menu 的话,认为 | |
| 271 | + if (CollUtil.isEmpty(menuList)) { | |
| 272 | + return false; | |
| 273 | + } | |
| 274 | + // 获得是否拥有该权限,任一一个 | |
| 275 | + return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds, | |
| 276 | + menuRoleCache.get(menu.getId()))); | |
| 277 | + }); | |
| 278 | + } | |
| 279 | + | |
| 192 | 280 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
| ... | ... | @@ -217,6 +217,8 @@ public class SysRoleServiceImpl implements SysRoleService { |
| 217 | 217 | updateObject.setId(id); |
| 218 | 218 | updateObject.setStatus(status); |
| 219 | 219 | roleMapper.updateById(updateObject); |
| 220 | + // 发送刷新消息 | |
| 221 | + roleProducer.sendRoleRefreshMessage(); | |
| 220 | 222 | } |
| 221 | 223 | |
| 222 | 224 | @Override |
| ... | ... | @@ -229,6 +231,8 @@ public class SysRoleServiceImpl implements SysRoleService { |
| 229 | 231 | updateObject.setDataScope(dataScope); |
| 230 | 232 | updateObject.setDataScopeDeptIds(dataScopeDeptIds); |
| 231 | 233 | roleMapper.updateById(updateObject); |
| 234 | + // 发送刷新消息 | |
| 235 | + roleProducer.sendRoleRefreshMessage(); | |
| 232 | 236 | } |
| 233 | 237 | |
| 234 | 238 | /** | ... | ... |