Commit bbe71ec2c893abc4e8a4a983f48e416f9e3e94db

Authored by YunaiV
1 parent aa38c0f9

完成部分权限的认证操作的迁移

Showing 39 changed files with 370 additions and 394 deletions
http-client.env.json
1 { 1 {
2 "local": { 2 "local": {
3 "baseUrl": "http://127.0.0.1:8080/api", 3 "baseUrl": "http://127.0.0.1:8080/api",
4 - "token": "yudaoyuanma1" 4 + "token": "test1"
5 } 5 }
6 } 6 }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java
@@ -9,17 +9,15 @@ import com.ruoyi.framework.web.domain.Server; @@ -9,17 +9,15 @@ import com.ruoyi.framework.web.domain.Server;
9 9
10 /** 10 /**
11 * 服务器监控 11 * 服务器监控
12 - * 12 + *
13 * @author ruoyi 13 * @author ruoyi
14 */ 14 */
15 @RestController 15 @RestController
16 @RequestMapping("/monitor/server") 16 @RequestMapping("/monitor/server")
17 -public class ServerController  
18 -{ 17 +public class ServerController {
19 @PreAuthorize("@ss.hasPermi('monitor:server:list')") 18 @PreAuthorize("@ss.hasPermi('monitor:server:list')")
20 @GetMapping() 19 @GetMapping()
21 - public AjaxResult getInfo() throws Exception  
22 - { 20 + public AjaxResult getInfo() throws Exception {
23 Server server = new Server(); 21 Server server = new Server();
24 server.copyTo(); 22 server.copyTo();
25 return AjaxResult.success(server); 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,31 +5,45 @@ package com.ruoyi.system.domain;
5 * 5 *
6 * @author ruoyi 6 * @author ruoyi
7 */ 7 */
8 -public class SysUserOnline  
9 -{  
10 - /** 会话编号 */ 8 +public class SysUserOnline {
  9 + /**
  10 + * 会话编号
  11 + */
11 private String tokenId; 12 private String tokenId;
12 13
13 - /** 部门名称 */ 14 + /**
  15 + * 部门名称
  16 + */
14 private String deptName; 17 private String deptName;
15 18
16 - /** 用户名称 */ 19 + /**
  20 + * 用户名称
  21 + */
17 private String userName; 22 private String userName;
18 23
19 - /** 登录IP地址 */ 24 + /**
  25 + * 登录IP地址
  26 + */
20 private String ipaddr; 27 private String ipaddr;
21 28
22 - /** 登录地址 */ 29 + /**
  30 + * 登录地址
  31 + */
23 private String loginLocation; 32 private String loginLocation;
24 33
25 - /** 浏览器类型 */ 34 + /**
  35 + * 浏览器类型
  36 + */
26 private String browser; 37 private String browser;
27 38
28 - /** 操作系统 */ 39 + /**
  40 + * 操作系统
  41 + */
29 private String os; 42 private String os;
30 43
31 - /** 登录时间 */ 44 + /**
  45 + * 登录时间
  46 + */
32 private Long loginTime; 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,14 +8,13 @@ import java.lang.annotation.Target;
8 8
9 /** 9 /**
10 * 数据权限过滤注解 10 * 数据权限过滤注解
11 - * 11 + *
12 * @author ruoyi 12 * @author ruoyi
13 */ 13 */
14 @Target(ElementType.METHOD) 14 @Target(ElementType.METHOD)
15 @Retention(RetentionPolicy.RUNTIME) 15 @Retention(RetentionPolicy.RUNTIME)
16 @Documented 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,21 +6,21 @@ import java.lang.annotation.Inherited;
6 import java.lang.annotation.Retention; 6 import java.lang.annotation.Retention;
7 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.RetentionPolicy;
8 import java.lang.annotation.Target; 8 import java.lang.annotation.Target;
  9 +
9 import com.ruoyi.common.enums.DataSourceType; 10 import com.ruoyi.common.enums.DataSourceType;
10 11
11 /** 12 /**
12 * 自定义多数据源切换注解 13 * 自定义多数据源切换注解
13 - * 14 + * <p>
14 * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 15 * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
15 * 16 *
16 * @author ruoyi 17 * @author ruoyi
17 */ 18 */
18 -@Target({ ElementType.METHOD, ElementType.TYPE }) 19 +@Target({ElementType.METHOD, ElementType.TYPE})
19 @Retention(RetentionPolicy.RUNTIME) 20 @Retention(RetentionPolicy.RUNTIME)
20 @Documented 21 @Documented
21 @Inherited 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,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 @Inherited 15 @Inherited
17 @Target(ElementType.METHOD) 16 @Target(ElementType.METHOD)
18 @Retention(RetentionPolicy.RUNTIME) 17 @Retention(RetentionPolicy.RUNTIME)
19 @Documented 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,7 +2,7 @@ package com.ruoyi.common.constant;
2 2
3 /** 3 /**
4 * 代码生成通用常量 4 * 代码生成通用常量
5 - * 5 + *
6 * @author ruoyi 6 * @author ruoyi
7 */ 7 */
8 public class GenConstants 8 public class GenConstants
@@ -13,6 +13,7 @@ public class GenConstants @@ -13,6 +13,7 @@ public class GenConstants
13 /** 树表(增删改查) */ 13 /** 树表(增删改查) */
14 public static final String TPL_TREE = "tree"; 14 public static final String TPL_TREE = "tree";
15 15
  16 +
16 /** 树编码字段 */ 17 /** 树编码字段 */
17 public static final String TREE_CODE = "treeCode"; 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,45 +8,38 @@ import javax.servlet.ServletException;
8 import javax.servlet.ServletRequest; 8 import javax.servlet.ServletRequest;
9 import javax.servlet.ServletResponse; 9 import javax.servlet.ServletResponse;
10 import javax.servlet.http.HttpServletRequest; 10 import javax.servlet.http.HttpServletRequest;
  11 +
11 import org.springframework.http.MediaType; 12 import org.springframework.http.MediaType;
12 import com.ruoyi.common.utils.StringUtils; 13 import com.ruoyi.common.utils.StringUtils;
13 14
14 /** 15 /**
15 * Repeatable 过滤器 16 * Repeatable 过滤器
16 - * 17 + *
17 * @author ruoyi 18 * @author ruoyi
18 */ 19 */
19 -public class RepeatableFilter implements Filter  
20 -{ 20 +public class RepeatableFilter implements Filter {
21 @Override 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 @Override 26 @Override
28 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 27 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
29 - throws IOException, ServletException  
30 - { 28 + throws IOException, ServletException {
31 ServletRequest requestWrapper = null; 29 ServletRequest requestWrapper = null;
32 if (request instanceof HttpServletRequest 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 requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); 32 requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
36 } 33 }
37 - if (null == requestWrapper)  
38 - { 34 + if (null == requestWrapper) {
39 chain.doFilter(request, response); 35 chain.doFilter(request, response);
40 - }  
41 - else  
42 - { 36 + } else {
43 chain.doFilter(requestWrapper, response); 37 chain.doFilter(requestWrapper, response);
44 } 38 }
45 } 39 }
46 40
47 @Override 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,19 +9,18 @@ import javax.servlet.ServletInputStream;
9 import javax.servlet.ServletResponse; 9 import javax.servlet.ServletResponse;
10 import javax.servlet.http.HttpServletRequest; 10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletRequestWrapper; 11 import javax.servlet.http.HttpServletRequestWrapper;
  12 +
12 import com.ruoyi.common.utils.http.HttpHelper; 13 import com.ruoyi.common.utils.http.HttpHelper;
13 14
14 /** 15 /**
15 * 构建可重复读取inputStream的request 16 * 构建可重复读取inputStream的request
16 - * 17 + *
17 * @author ruoyi 18 * @author ruoyi
18 */ 19 */
19 -public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper  
20 -{ 20 +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
21 private final byte[] body; 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 super(request); 24 super(request);
26 request.setCharacterEncoding("UTF-8"); 25 request.setCharacterEncoding("UTF-8");
27 response.setCharacterEncoding("UTF-8"); 26 response.setCharacterEncoding("UTF-8");
@@ -30,41 +29,34 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper @@ -30,41 +29,34 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
30 } 29 }
31 30
32 @Override 31 @Override
33 - public BufferedReader getReader() throws IOException  
34 - { 32 + public BufferedReader getReader() throws IOException {
35 return new BufferedReader(new InputStreamReader(getInputStream())); 33 return new BufferedReader(new InputStreamReader(getInputStream()));
36 } 34 }
37 35
38 @Override 36 @Override
39 - public ServletInputStream getInputStream() throws IOException  
40 - { 37 + public ServletInputStream getInputStream() throws IOException {
41 38
42 final ByteArrayInputStream bais = new ByteArrayInputStream(body); 39 final ByteArrayInputStream bais = new ByteArrayInputStream(body);
43 40
44 - return new ServletInputStream()  
45 - { 41 + return new ServletInputStream() {
46 42
47 @Override 43 @Override
48 - public int read() throws IOException  
49 - { 44 + public int read() throws IOException {
50 return bais.read(); 45 return bais.read();
51 } 46 }
52 47
53 @Override 48 @Override
54 - public boolean isFinished()  
55 - { 49 + public boolean isFinished() {
56 return false; 50 return false;
57 } 51 }
58 52
59 @Override 53 @Override
60 - public boolean isReady()  
61 - { 54 + public boolean isReady() {
62 return false; 55 return false;
63 } 56 }
64 57
65 @Override 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,46 +55,6 @@ public class Server {
55 */ 55 */
56 private List<SysFile> sysFiles = new LinkedList<SysFile>(); 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 public void copyTo() throws Exception { 58 public void copyTo() throws Exception {
99 SystemInfo si = new SystemInfo(); 59 SystemInfo si = new SystemInfo();
100 HardwareAbstractionLayer hal = si.getHardware(); 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,22 +38,10 @@ public class Cpu {
38 */ 38 */
39 private double free; 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 public double getTotal() { 41 public double getTotal() {
50 return Arith.round(Arith.mul(total, 100), 2); 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 public double getSys() { 45 public double getSys() {
58 return Arith.round(Arith.mul(sys / total, 100), 2); 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,18 +40,10 @@ public class Jvm {
40 return Arith.div(total, (1024 * 1024), 2); 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 public double getMax() { 43 public double getMax() {
48 return Arith.div(max, (1024 * 1024), 2); 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 public double getFree() { 47 public double getFree() {
56 return Arith.div(free, (1024 * 1024), 2); 48 return Arith.div(free, (1024 * 1024), 2);
57 } 49 }
@@ -75,22 +67,6 @@ public class Jvm { @@ -75,22 +67,6 @@ public class Jvm {
75 return ManagementFactory.getRuntimeMXBean().getVmName(); 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 * JDK启动时间 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 +27,16 @@ public class Mem {
27 return Arith.div(total, (1024 * 1024 * 1024), 2); 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 public double getUsed() { 30 public double getUsed() {
35 return Arith.div(used, (1024 * 1024 * 1024), 2); 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 public double getFree() { 34 public double getFree() {
43 return Arith.div(free, (1024 * 1024 * 1024), 2); 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 public double getUsage() { 38 public double getUsage() {
51 return Arith.mul(Arith.div(used, total, 4), 100); 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,43 +31,4 @@ public class Sys {
31 */ 31 */
32 private String osArch; 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,59 +41,4 @@ public class SysFile {
41 */ 41 */
42 private double usage; 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,68 +17,6 @@ import com.ruoyi.common.utils.StringUtils;
17 */ 17 */
18 @Service("ss") 18 @Service("ss")
19 public class PermissionService { 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,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 * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 47 * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
@@ -135,14 +63,4 @@ public class PermissionService { @@ -135,14 +63,4 @@ public class PermissionService {
135 return false; 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 =&gt; { @@ -61,7 +61,9 @@ service.interceptors.response.use(res =&gt; {
61 } 61 }
62 ).then(() => { 62 ).then(() => {
63 store.dispatch('LogOut').then(() => { 63 store.dispatch('LogOut').then(() => {
64 - location.href = '/index'; 64 + if (location.pathname !== '/login') { // 避免重复跳转
  65 + location.href = '/index';
  66 + }
65 }) 67 })
66 }) 68 })
67 } else if (code === 500) { 69 } else if (code === 500) {
ruoyi-ui/src/views/system/role/index.vue
@@ -392,6 +392,7 @@ export default { @@ -392,6 +392,7 @@ export default {
392 menuIds: [], 392 menuIds: [],
393 dataScope: undefined, 393 dataScope: undefined,
394 deptCheckStrictly: false, 394 deptCheckStrictly: false,
  395 + menuCheckStrictly: true,
395 remark: undefined 396 remark: undefined
396 }; 397 };
397 this.resetForm("form"); 398 this.resetForm("form");
@@ -471,8 +472,12 @@ export default { @@ -471,8 +472,12 @@ export default {
471 }); 472 });
472 // 获得角色拥有的菜单集合 473 // 获得角色拥有的菜单集合
473 listRoleMenus(id).then(response => { 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,7 +528,7 @@ export default {
523 roleId: this.form.id, 528 roleId: this.form.id,
524 dataScope: this.form.dataScope, 529 dataScope: this.form.dataScope,
525 dataScopeDeptIds: this.form.dataScope !== SysDataScopeEnum.DEPT_CUSTOM ? [] : 530 dataScopeDeptIds: this.form.dataScope !== SysDataScopeEnum.DEPT_CUSTOM ? [] :
526 - this.$refs.dept.getCheckedKeys(false) 531 + this.$refs.dept.getCheckedKeys()
527 }).then(response => { 532 }).then(response => {
528 this.msgSuccess("修改成功"); 533 this.msgSuccess("修改成功");
529 this.openDataScope = false; 534 this.openDataScope = false;
@@ -536,7 +541,7 @@ export default { @@ -536,7 +541,7 @@ export default {
536 if (this.form.id !== undefined) { 541 if (this.form.id !== undefined) {
537 assignRoleMenu({ 542 assignRoleMenu({
538 roleId: this.form.id, 543 roleId: this.form.id,
539 - menuIds: this.$refs.menu.getCheckedKeys(true) 544 + menuIds: [...this.$refs.menu.getCheckedKeys(), ...this.$refs.menu.getHalfCheckedKeys()]
540 }).then(response => { 545 }).then(response => {
541 this.msgSuccess("修改成功"); 546 this.msgSuccess("修改成功");
542 this.openMenu = false; 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,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 private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) { 236 private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) {
242 // 有 @OperateLog 注解的情况下 237 // 有 @OperateLog 注解的情况下
243 if (operateLog != null) { 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,7 +2,7 @@ package cn.iocoder.dashboard.framework.security.core.handler;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.dashboard.framework.security.config.SecurityProperties; 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 import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; 6 import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
7 import cn.iocoder.dashboard.util.servlet.ServletUtils; 7 import cn.iocoder.dashboard.util.servlet.ServletUtils;
8 import org.springframework.security.core.Authentication; 8 import org.springframework.security.core.Authentication;
@@ -26,7 +26,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { @@ -26,7 +26,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
26 private SecurityProperties securityProperties; 26 private SecurityProperties securityProperties;
27 27
28 @Resource 28 @Resource
29 - private SecurityFrameworkService securityFrameworkService; 29 + private SecurityAuthFrameworkService securityFrameworkService;
30 30
31 @Override 31 @Override
32 public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { 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,16 +4,11 @@ import cn.iocoder.dashboard.framework.security.core.LoginUser;
4 import org.springframework.security.core.userdetails.UserDetailsService; 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 * 校验 token 的有效性,并获取用户信息 14 * 校验 token 的有效性,并获取用户信息
@@ -32,4 +27,11 @@ public interface SecurityFrameworkService extends UserDetailsService { @@ -32,4 +27,11 @@ public interface SecurityFrameworkService extends UserDetailsService {
32 */ 27 */
33 LoginUser mockLogin(Long userId); 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,7 +3,9 @@ package cn.iocoder.dashboard.framework.web.core.handler;
3 import cn.iocoder.dashboard.common.exception.GlobalException; 3 import cn.iocoder.dashboard.common.exception.GlobalException;
4 import cn.iocoder.dashboard.common.exception.ServiceException; 4 import cn.iocoder.dashboard.common.exception.ServiceException;
5 import cn.iocoder.dashboard.common.pojo.CommonResult; 5 import cn.iocoder.dashboard.common.pojo.CommonResult;
  6 +import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
6 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
  8 +import org.springframework.security.access.AccessDeniedException;
7 import org.springframework.validation.BindException; 9 import org.springframework.validation.BindException;
8 import org.springframework.validation.FieldError; 10 import org.springframework.validation.FieldError;
9 import org.springframework.web.HttpRequestMethodNotSupportedException; 11 import org.springframework.web.HttpRequestMethodNotSupportedException;
@@ -64,6 +66,9 @@ public class GlobalExceptionHandler { @@ -64,6 +66,9 @@ public class GlobalExceptionHandler {
64 if (ex instanceof ServiceException) { 66 if (ex instanceof ServiceException) {
65 return serviceExceptionHandler((ServiceException) ex); 67 return serviceExceptionHandler((ServiceException) ex);
66 } 68 }
  69 + if (ex instanceof AccessDeniedException) {
  70 + return accessDeniedExceptionHandler(request, (AccessDeniedException) ex);
  71 + }
67 if (ex instanceof GlobalException) { 72 if (ex instanceof GlobalException) {
68 return globalExceptionHandler(request, (GlobalException) ex); 73 return globalExceptionHandler(request, (GlobalException) ex);
69 } 74 }
@@ -131,7 +136,7 @@ public class GlobalExceptionHandler { @@ -131,7 +136,7 @@ public class GlobalExceptionHandler {
131 public CommonResult<?> validationException(ValidationException ex) { 136 public CommonResult<?> validationException(ValidationException ex) {
132 log.warn("[constraintViolationExceptionHandler]", ex); 137 log.warn("[constraintViolationExceptionHandler]", ex);
133 // 无法拼接明细的错误信息,因为 Dubbo Consumer 抛出 ValidationException 异常时,是直接的字符串信息,且人类不可读 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,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 * 处理业务异常 ServiceException 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,9 +7,7 @@ import lombok.Builder;
7 import lombok.Data; 7 import lombok.Data;
8 import lombok.NoArgsConstructor; 8 import lombok.NoArgsConstructor;
9 import org.hibernate.validator.constraints.Length; 9 import org.hibernate.validator.constraints.Length;
10 -import org.springframework.validation.annotation.Validated;  
11 10
12 -import javax.validation.Valid;  
13 import javax.validation.constraints.NotEmpty; 11 import javax.validation.constraints.NotEmpty;
14 import javax.validation.constraints.Pattern; 12 import javax.validation.constraints.Pattern;
15 13
@@ -22,7 +20,7 @@ public class SysAuthLoginReqVO { @@ -22,7 +20,7 @@ public class SysAuthLoginReqVO {
22 20
23 @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") 21 @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
24 @NotEmpty(message = "登陆账号不能为空") 22 @NotEmpty(message = "登陆账号不能为空")
25 - @Length(min = 5, max = 16, message = "账号长度为 5-16 位") 23 + @Length(min = 4, max = 16, message = "账号长度为 4-16 位")
26 @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") 24 @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
27 private String username; 25 private String username;
28 26
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.http 0 → 100644
  1 +### 请求 /system/user/page 接口 => 没有权限
  2 +GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10
  3 +Authorization: Bearer test104 # 使用测试账号
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java
@@ -18,6 +18,7 @@ import io.swagger.annotations.Api; @@ -18,6 +18,7 @@ import io.swagger.annotations.Api;
18 import io.swagger.annotations.ApiImplicitParam; 18 import io.swagger.annotations.ApiImplicitParam;
19 import io.swagger.annotations.ApiImplicitParams; 19 import io.swagger.annotations.ApiImplicitParams;
20 import io.swagger.annotations.ApiOperation; 20 import io.swagger.annotations.ApiOperation;
  21 +import org.springframework.security.access.prepost.PreAuthorize;
21 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
22 import org.springframework.web.bind.annotation.*; 23 import org.springframework.web.bind.annotation.*;
23 import org.springframework.web.multipart.MultipartFile; 24 import org.springframework.web.multipart.MultipartFile;
@@ -41,7 +42,7 @@ public class SysUserController { @@ -41,7 +42,7 @@ public class SysUserController {
41 42
42 @ApiOperation("获得用户分页列表") 43 @ApiOperation("获得用户分页列表")
43 @GetMapping("/page") 44 @GetMapping("/page")
44 -// @PreAuthorize("@ss.hasPermi('system:user:list')") 45 + @PreAuthorize("@ss.hasPermission('system:user:list')")
45 public CommonResult<PageResult<SysUserPageItemRespVO>> pageUsers(@Validated SysUserPageReqVO reqVO) { 46 public CommonResult<PageResult<SysUserPageItemRespVO>> pageUsers(@Validated SysUserPageReqVO reqVO) {
46 // 获得用户分页列表 47 // 获得用户分页列表
47 PageResult<SysUserDO> pageResult = userService.pageUsers(reqVO); 48 PageResult<SysUserDO> pageResult = userService.pageUsers(reqVO);
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMenuMapper.java
1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission; 1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
2 2
  3 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
3 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO; 4 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
4 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 5 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
5 -import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
6 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
7 7
8 import java.util.Collection; 8 import java.util.Collection;
  9 +import java.util.Date;
9 import java.util.List; 10 import java.util.List;
10 import java.util.stream.Collectors; 11 import java.util.stream.Collectors;
11 12
12 @Mapper 13 @Mapper
13 -public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuDO> { 14 +public interface SysRoleMenuMapper extends BaseMapperX<SysRoleMenuDO> {
14 15
15 default List<SysRoleMenuDO> selectListByRoleId(Long roleId) { 16 default List<SysRoleMenuDO> selectListByRoleId(Long roleId) {
16 return selectList(new QueryWrapper<SysRoleMenuDO>().eq("role_id", roleId)); 17 return selectList(new QueryWrapper<SysRoleMenuDO>().eq("role_id", roleId));
@@ -32,4 +33,9 @@ public interface SysRoleMenuMapper extends BaseMapper&lt;SysRoleMenuDO&gt; { @@ -32,4 +33,9 @@ public interface SysRoleMenuMapper extends BaseMapper&lt;SysRoleMenuDO&gt; {
32 .in("menu_id", menuIds)); 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 package cn.iocoder.dashboard.modules.system.service.auth; 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 * 认证 Service 接口 6 * 认证 Service 接口
@@ -9,7 +9,7 @@ import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkSer @@ -9,7 +9,7 @@ import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkSer
9 * 9 *
10 * @author 芋道源码 10 * @author 芋道源码
11 */ 11 */
12 -public interface SysAuthService extends SecurityFrameworkService { 12 +public interface SysAuthService extends SecurityAuthFrameworkService {
13 13
14 String login(String username, String password, String captchaUUID, String captchaCode); 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,6 +59,14 @@ public interface SysMenuService {
59 List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes, 59 List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
60 Collection<Integer> menusStatuses); 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 package cn.iocoder.dashboard.modules.system.service.permission; 1 package cn.iocoder.dashboard.modules.system.service.permission;
2 2
  3 +import cn.iocoder.dashboard.framework.security.core.service.SecurityPermissionFrameworkService;
3 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; 4 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
4 import org.springframework.lang.Nullable; 5 import org.springframework.lang.Nullable;
5 6
@@ -14,12 +15,12 @@ import java.util.Set; @@ -14,12 +15,12 @@ import java.util.Set;
14 * 15 *
15 * @author 芋道源码 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,6 +57,16 @@ public interface SysRoleService {
57 boolean hasAnyAdmin(Collection<SysRoleDO> roleList); 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 * @param reqVO 创建角色信息 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,7 +61,7 @@ public class SysMenuServiceImpl implements SysMenuService {
61 * 61 *
62 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 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,7 +76,7 @@ public class SysMenuServiceImpl implements SysMenuService {
76 private SysMenuProducer menuProducer; 76 private SysMenuProducer menuProducer;
77 77
78 /** 78 /**
79 - * 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存 79 + * 初始化 {@link #menuCache} 和 {@link #permissionMenuCache} 缓存
80 */ 80 */
81 @Override 81 @Override
82 @PostConstruct 82 @PostConstruct
@@ -95,7 +95,7 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -95,7 +95,7 @@ public class SysMenuServiceImpl implements SysMenuService {
95 permMenuCacheBuilder.put(menuDO.getPermission(), menuDO); 95 permMenuCacheBuilder.put(menuDO.getPermission(), menuDO);
96 }); 96 });
97 menuCache = menuCacheBuilder.build(); 97 menuCache = menuCacheBuilder.build();
98 - permMenuCache = permMenuCacheBuilder.build(); 98 + permissionMenuCache = permMenuCacheBuilder.build();
99 assert menuList.size() > 0; // 断言,避免告警 99 assert menuList.size() > 0; // 断言,避免告警
100 maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); 100 maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
101 log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size()); 101 log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
@@ -163,6 +163,11 @@ public class SysMenuServiceImpl implements SysMenuService { @@ -163,6 +163,11 @@ public class SysMenuServiceImpl implements SysMenuService {
163 } 163 }
164 164
165 @Override 165 @Override
  166 + public List<SysMenuDO> getMenuListByPermissionFromCache(String permission) {
  167 + return new ArrayList<>(permissionMenuCache.get(permission));
  168 + }
  169 +
  170 + @Override
166 public Long createMenu(SysMenuCreateReqVO reqVO) { 171 public Long createMenu(SysMenuCreateReqVO reqVO) {
167 // 校验父菜单存在 172 // 校验父菜单存在
168 checkParentResource(reqVO.getParentId(), null); 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,12 +2,16 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 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 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMenuMapper; 8 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMenuMapper;
6 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysUserRoleMapper; 9 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysUserRoleMapper;
7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; 10 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
8 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; 11 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
9 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO; 12 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
10 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO; 13 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO;
  14 +import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysPermissionProducer;
11 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; 15 import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
12 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; 16 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
13 import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; 17 import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
@@ -18,24 +22,29 @@ import com.google.common.collect.Multimap; @@ -18,24 +22,29 @@ import com.google.common.collect.Multimap;
18 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
20 import org.springframework.transaction.annotation.Transactional; 24 import org.springframework.transaction.annotation.Transactional;
  25 +import org.springframework.transaction.support.TransactionSynchronization;
  26 +import org.springframework.transaction.support.TransactionSynchronizationManager;
21 27
22 import javax.annotation.PostConstruct; 28 import javax.annotation.PostConstruct;
23 import javax.annotation.Resource; 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 * 权限 Service 实现类 33 * 权限 Service 实现类
31 * 34 *
32 * @author 芋道源码 35 * @author 芋道源码
33 */ 36 */
34 -@Service 37 +@Service("ss") // 使用 Spring Security 的缩写,方便食用
35 @Slf4j 38 @Slf4j
36 public class SysPermissionServiceImpl implements SysPermissionService { 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 * key:角色编号 49 * key:角色编号
41 * value:菜单编号的数组 50 * value:菜单编号的数组
@@ -51,6 +60,10 @@ public class SysPermissionServiceImpl implements SysPermissionService { @@ -51,6 +60,10 @@ public class SysPermissionServiceImpl implements SysPermissionService {
51 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 60 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
52 */ 61 */
53 private volatile Multimap<Long, Long> menuRoleCache; 62 private volatile Multimap<Long, Long> menuRoleCache;
  63 + /**
  64 + * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
  65 + */
  66 + private volatile Date maxUpdateTime;
54 67
55 @Resource 68 @Resource
56 private SysRoleMenuMapper roleMenuMapper; 69 private SysRoleMenuMapper roleMenuMapper;
@@ -62,14 +75,22 @@ public class SysPermissionServiceImpl implements SysPermissionService { @@ -62,14 +75,22 @@ public class SysPermissionServiceImpl implements SysPermissionService {
62 @Resource 75 @Resource
63 private SysMenuService menuService; 76 private SysMenuService menuService;
64 77
  78 + @Resource
  79 + private SysPermissionProducer permissionProducer;
  80 +
65 /** 81 /**
66 * 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存 82 * 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
67 */ 83 */
68 @Override 84 @Override
69 @PostConstruct 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 // 初始化 roleMenuCache 和 menuRoleCache 缓存 93 // 初始化 roleMenuCache 和 menuRoleCache 缓存
72 - List<SysRoleMenuDO> roleMenuList = roleMenuMapper.selectList(null);  
73 ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder(); 94 ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
74 ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder(); 95 ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
75 roleMenuList.forEach(roleMenuDO -> { 96 roleMenuList.forEach(roleMenuDO -> {
@@ -78,9 +99,32 @@ public class SysPermissionServiceImpl implements SysPermissionService { @@ -78,9 +99,32 @@ public class SysPermissionServiceImpl implements SysPermissionService {
78 }); 99 });
79 roleMenuCache = roleMenuCacheBuilder.build(); 100 roleMenuCache = roleMenuCacheBuilder.build();
80 menuRoleCache = menuRoleCacheBuilder.build(); 101 menuRoleCache = menuRoleCacheBuilder.build();
  102 + assert roleMenuList.size() > 0; // 断言,避免告警
  103 + maxUpdateTime = roleMenuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
81 log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size()); 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 @Override 128 @Override
85 public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes, 129 public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
86 Collection<Integer> menusStatuses) { 130 Collection<Integer> menusStatuses) {
@@ -140,6 +184,15 @@ public class SysPermissionServiceImpl implements SysPermissionService { @@ -140,6 +184,15 @@ public class SysPermissionServiceImpl implements SysPermissionService {
140 if (!CollectionUtil.isEmpty(deleteMenuIds)) { 184 if (!CollectionUtil.isEmpty(deleteMenuIds)) {
141 roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds); 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 @Override 198 @Override
@@ -189,4 +242,39 @@ public class SysPermissionServiceImpl implements SysPermissionService { @@ -189,4 +242,39 @@ public class SysPermissionServiceImpl implements SysPermissionService {
189 // TODO 实现我 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,6 +217,8 @@ public class SysRoleServiceImpl implements SysRoleService {
217 updateObject.setId(id); 217 updateObject.setId(id);
218 updateObject.setStatus(status); 218 updateObject.setStatus(status);
219 roleMapper.updateById(updateObject); 219 roleMapper.updateById(updateObject);
  220 + // 发送刷新消息
  221 + roleProducer.sendRoleRefreshMessage();
220 } 222 }
221 223
222 @Override 224 @Override
@@ -229,6 +231,8 @@ public class SysRoleServiceImpl implements SysRoleService { @@ -229,6 +231,8 @@ public class SysRoleServiceImpl implements SysRoleService {
229 updateObject.setDataScope(dataScope); 231 updateObject.setDataScope(dataScope);
230 updateObject.setDataScopeDeptIds(dataScopeDeptIds); 232 updateObject.setDataScopeDeptIds(dataScopeDeptIds);
231 roleMapper.updateById(updateObject); 233 roleMapper.updateById(updateObject);
  234 + // 发送刷新消息
  235 + roleProducer.sendRoleRefreshMessage();
232 } 236 }
233 237
234 /** 238 /**
src/main/resources/application.yaml
@@ -34,7 +34,7 @@ yudao: @@ -34,7 +34,7 @@ yudao:
34 token-timeout: 1d 34 token-timeout: 1d
35 session-timeout: 30m 35 session-timeout: 30m
36 mock-enable: true 36 mock-enable: true
37 - mock-secret: yudaoyuanma 37 + mock-secret: test
38 swagger: 38 swagger:
39 title: 管理后台 39 title: 管理后台
40 description: 提供管理员管理的所有功能 40 description: 提供管理员管理的所有功能