mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-06-22 16:32:00 +08:00
完成部分权限的认证操作的迁移
This commit is contained in:
parent
aa38c0f9d1
commit
bbe71ec2c8
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"local": {
|
"local": {
|
||||||
"baseUrl": "http://127.0.0.1:8080/api",
|
"baseUrl": "http://127.0.0.1:8080/api",
|
||||||
"token": "yudaoyuanma1"
|
"token": "test1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,10 @@ import com.ruoyi.framework.web.domain.Server;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/monitor/server")
|
@RequestMapping("/monitor/server")
|
||||||
public class ServerController
|
public class ServerController {
|
||||||
{
|
|
||||||
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
|
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
|
||||||
@GetMapping()
|
@GetMapping()
|
||||||
public AjaxResult getInfo() throws Exception
|
public AjaxResult getInfo() throws Exception {
|
||||||
{
|
|
||||||
Server server = new Server();
|
Server server = new Server();
|
||||||
server.copyTo();
|
server.copyTo();
|
||||||
return AjaxResult.success(server);
|
return AjaxResult.success(server);
|
||||||
|
@ -5,31 +5,45 @@ package com.ruoyi.system.domain;
|
|||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
public class SysUserOnline
|
public class SysUserOnline {
|
||||||
{
|
/**
|
||||||
/** 会话编号 */
|
* 会话编号
|
||||||
|
*/
|
||||||
private String tokenId;
|
private String tokenId;
|
||||||
|
|
||||||
/** 部门名称 */
|
/**
|
||||||
|
* 部门名称
|
||||||
|
*/
|
||||||
private String deptName;
|
private String deptName;
|
||||||
|
|
||||||
/** 用户名称 */
|
/**
|
||||||
|
* 用户名称
|
||||||
|
*/
|
||||||
private String userName;
|
private String userName;
|
||||||
|
|
||||||
/** 登录IP地址 */
|
/**
|
||||||
|
* 登录IP地址
|
||||||
|
*/
|
||||||
private String ipaddr;
|
private String ipaddr;
|
||||||
|
|
||||||
/** 登录地址 */
|
/**
|
||||||
|
* 登录地址
|
||||||
|
*/
|
||||||
private String loginLocation;
|
private String loginLocation;
|
||||||
|
|
||||||
/** 浏览器类型 */
|
/**
|
||||||
|
* 浏览器类型
|
||||||
|
*/
|
||||||
private String browser;
|
private String browser;
|
||||||
|
|
||||||
/** 操作系统 */
|
/**
|
||||||
|
* 操作系统
|
||||||
|
*/
|
||||||
private String os;
|
private String os;
|
||||||
|
|
||||||
/** 登录时间 */
|
/**
|
||||||
|
* 登录时间
|
||||||
|
*/
|
||||||
private Long loginTime;
|
private Long loginTime;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,7 @@ import java.lang.annotation.Target;
|
|||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface DataScope
|
public @interface DataScope {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* 部门表的别名
|
* 部门表的别名
|
||||||
*/
|
*/
|
||||||
|
@ -6,21 +6,21 @@ import java.lang.annotation.Inherited;
|
|||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import com.ruoyi.common.enums.DataSourceType;
|
import com.ruoyi.common.enums.DataSourceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义多数据源切换注解
|
* 自定义多数据源切换注解
|
||||||
*
|
* <p>
|
||||||
* 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
|
* 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@Inherited
|
@Inherited
|
||||||
public @interface DataSource
|
public @interface DataSource {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* 切换数据源名称
|
* 切换数据源名称
|
||||||
*/
|
*/
|
||||||
|
@ -11,13 +11,11 @@ import java.lang.annotation.Target;
|
|||||||
* 自定义注解防止表单重复提交
|
* 自定义注解防止表单重复提交
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Inherited
|
@Inherited
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface RepeatSubmit
|
public @interface RepeatSubmit {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ public class GenConstants
|
|||||||
/** 树表(增删改查) */
|
/** 树表(增删改查) */
|
||||||
public static final String TPL_TREE = "tree";
|
public static final String TPL_TREE = "tree";
|
||||||
|
|
||||||
|
|
||||||
/** 树编码字段 */
|
/** 树编码字段 */
|
||||||
public static final String TREE_CODE = "treeCode";
|
public static final String TREE_CODE = "treeCode";
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import javax.servlet.ServletException;
|
|||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
@ -16,37 +17,29 @@ import com.ruoyi.common.utils.StringUtils;
|
|||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
public class RepeatableFilter implements Filter
|
public class RepeatableFilter implements Filter {
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig filterConfig) throws ServletException
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
throws IOException, ServletException
|
throws IOException, ServletException {
|
||||||
{
|
|
||||||
ServletRequest requestWrapper = null;
|
ServletRequest requestWrapper = null;
|
||||||
if (request instanceof HttpServletRequest
|
if (request instanceof HttpServletRequest
|
||||||
&& StringUtils.equalsAnyIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
|
&& StringUtils.equalsAnyIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
|
||||||
{
|
|
||||||
requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
|
requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
|
||||||
}
|
}
|
||||||
if (null == requestWrapper)
|
if (null == requestWrapper) {
|
||||||
{
|
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
chain.doFilter(requestWrapper, response);
|
chain.doFilter(requestWrapper, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy()
|
public void destroy() {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import javax.servlet.ServletInputStream;
|
|||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletRequestWrapper;
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
|
||||||
import com.ruoyi.common.utils.http.HttpHelper;
|
import com.ruoyi.common.utils.http.HttpHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,12 +17,10 @@ import com.ruoyi.common.utils.http.HttpHelper;
|
|||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
|
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
|
||||||
{
|
|
||||||
private final byte[] body;
|
private final byte[] body;
|
||||||
|
|
||||||
public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
|
public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
|
||||||
{
|
|
||||||
super(request);
|
super(request);
|
||||||
request.setCharacterEncoding("UTF-8");
|
request.setCharacterEncoding("UTF-8");
|
||||||
response.setCharacterEncoding("UTF-8");
|
response.setCharacterEncoding("UTF-8");
|
||||||
@ -30,41 +29,34 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BufferedReader getReader() throws IOException
|
public BufferedReader getReader() throws IOException {
|
||||||
{
|
|
||||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ServletInputStream getInputStream() throws IOException
|
public ServletInputStream getInputStream() throws IOException {
|
||||||
{
|
|
||||||
|
|
||||||
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||||
|
|
||||||
return new ServletInputStream()
|
return new ServletInputStream() {
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException
|
public int read() throws IOException {
|
||||||
{
|
|
||||||
return bais.read();
|
return bais.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFinished()
|
public boolean isFinished() {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isReady()
|
public boolean isReady() {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setReadListener(ReadListener readListener)
|
public void setReadListener(ReadListener readListener) {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -55,46 +55,6 @@ public class Server {
|
|||||||
*/
|
*/
|
||||||
private List<SysFile> sysFiles = new LinkedList<SysFile>();
|
private List<SysFile> sysFiles = new LinkedList<SysFile>();
|
||||||
|
|
||||||
public Cpu getCpu() {
|
|
||||||
return cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCpu(Cpu cpu) {
|
|
||||||
this.cpu = cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mem getMem() {
|
|
||||||
return mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMem(Mem mem) {
|
|
||||||
this.mem = mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Jvm getJvm() {
|
|
||||||
return jvm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setJvm(Jvm jvm) {
|
|
||||||
this.jvm = jvm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Sys getSys() {
|
|
||||||
return sys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSys(Sys sys) {
|
|
||||||
this.sys = sys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<SysFile> getSysFiles() {
|
|
||||||
return sysFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSysFiles(List<SysFile> sysFiles) {
|
|
||||||
this.sysFiles = sysFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void copyTo() throws Exception {
|
public void copyTo() throws Exception {
|
||||||
SystemInfo si = new SystemInfo();
|
SystemInfo si = new SystemInfo();
|
||||||
HardwareAbstractionLayer hal = si.getHardware();
|
HardwareAbstractionLayer hal = si.getHardware();
|
||||||
|
@ -38,22 +38,10 @@ public class Cpu {
|
|||||||
*/
|
*/
|
||||||
private double free;
|
private double free;
|
||||||
|
|
||||||
public int getCpuNum() {
|
|
||||||
return cpuNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCpuNum(int cpuNum) {
|
|
||||||
this.cpuNum = cpuNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getTotal() {
|
public double getTotal() {
|
||||||
return Arith.round(Arith.mul(total, 100), 2);
|
return Arith.round(Arith.mul(total, 100), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTotal(double total) {
|
|
||||||
this.total = total;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getSys() {
|
public double getSys() {
|
||||||
return Arith.round(Arith.mul(sys / total, 100), 2);
|
return Arith.round(Arith.mul(sys / total, 100), 2);
|
||||||
}
|
}
|
||||||
|
@ -40,18 +40,10 @@ public class Jvm {
|
|||||||
return Arith.div(total, (1024 * 1024), 2);
|
return Arith.div(total, (1024 * 1024), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTotal(double total) {
|
|
||||||
this.total = total;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getMax() {
|
public double getMax() {
|
||||||
return Arith.div(max, (1024 * 1024), 2);
|
return Arith.div(max, (1024 * 1024), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMax(double max) {
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getFree() {
|
public double getFree() {
|
||||||
return Arith.div(free, (1024 * 1024), 2);
|
return Arith.div(free, (1024 * 1024), 2);
|
||||||
}
|
}
|
||||||
@ -75,22 +67,6 @@ public class Jvm {
|
|||||||
return ManagementFactory.getRuntimeMXBean().getVmName();
|
return ManagementFactory.getRuntimeMXBean().getVmName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVersion(String version) {
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHome() {
|
|
||||||
return home;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHome(String home) {
|
|
||||||
this.home = home;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JDK启动时间
|
* JDK启动时间
|
||||||
*/
|
*/
|
||||||
|
@ -27,27 +27,16 @@ public class Mem {
|
|||||||
return Arith.div(total, (1024 * 1024 * 1024), 2);
|
return Arith.div(total, (1024 * 1024 * 1024), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTotal(long total) {
|
|
||||||
this.total = total;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getUsed() {
|
public double getUsed() {
|
||||||
return Arith.div(used, (1024 * 1024 * 1024), 2);
|
return Arith.div(used, (1024 * 1024 * 1024), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsed(long used) {
|
|
||||||
this.used = used;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getFree() {
|
public double getFree() {
|
||||||
return Arith.div(free, (1024 * 1024 * 1024), 2);
|
return Arith.div(free, (1024 * 1024 * 1024), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFree(long free) {
|
|
||||||
this.free = free;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getUsage() {
|
public double getUsage() {
|
||||||
return Arith.mul(Arith.div(used, total, 4), 100);
|
return Arith.mul(Arith.div(used, total, 4), 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,43 +31,4 @@ public class Sys {
|
|||||||
*/
|
*/
|
||||||
private String osArch;
|
private String osArch;
|
||||||
|
|
||||||
public String getComputerName() {
|
|
||||||
return computerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setComputerName(String computerName) {
|
|
||||||
this.computerName = computerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getComputerIp() {
|
|
||||||
return computerIp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setComputerIp(String computerIp) {
|
|
||||||
this.computerIp = computerIp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserDir() {
|
|
||||||
return userDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserDir(String userDir) {
|
|
||||||
this.userDir = userDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOsName() {
|
|
||||||
return osName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOsName(String osName) {
|
|
||||||
this.osName = osName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOsArch() {
|
|
||||||
return osArch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOsArch(String osArch) {
|
|
||||||
this.osArch = osArch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -41,59 +41,4 @@ public class SysFile {
|
|||||||
*/
|
*/
|
||||||
private double usage;
|
private double usage;
|
||||||
|
|
||||||
public String getDirName() {
|
|
||||||
return dirName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDirName(String dirName) {
|
|
||||||
this.dirName = dirName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSysTypeName() {
|
|
||||||
return sysTypeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSysTypeName(String sysTypeName) {
|
|
||||||
this.sysTypeName = sysTypeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTypeName() {
|
|
||||||
return typeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypeName(String typeName) {
|
|
||||||
this.typeName = typeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTotal() {
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTotal(String total) {
|
|
||||||
this.total = total;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFree() {
|
|
||||||
return free;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFree(String free) {
|
|
||||||
this.free = free;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsed() {
|
|
||||||
return used;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsed(String used) {
|
|
||||||
this.used = used;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getUsage() {
|
|
||||||
return usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsage(double usage) {
|
|
||||||
this.usage = usage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,68 +17,6 @@ import com.ruoyi.common.utils.StringUtils;
|
|||||||
*/
|
*/
|
||||||
@Service("ss")
|
@Service("ss")
|
||||||
public class PermissionService {
|
public class PermissionService {
|
||||||
/**
|
|
||||||
* 所有权限标识
|
|
||||||
*/
|
|
||||||
private static final String ALL_PERMISSION = "*:*:*";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 管理员角色权限标识
|
|
||||||
*/
|
|
||||||
private static final String SUPER_ADMIN = "admin";
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TokenService tokenService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证用户是否具备某权限
|
|
||||||
*
|
|
||||||
* @param permission 权限字符串
|
|
||||||
* @return 用户是否具备某权限
|
|
||||||
*/
|
|
||||||
public boolean hasPermi(String permission) {
|
|
||||||
if (StringUtils.isEmpty(permission)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
|
|
||||||
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return hasPermissions(loginUser.getPermissions(), permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证用户是否不具备某权限,与 hasPermi逻辑相反
|
|
||||||
*
|
|
||||||
* @param permission 权限字符串
|
|
||||||
* @return 用户是否不具备某权限
|
|
||||||
*/
|
|
||||||
public boolean lacksPermi(String permission) {
|
|
||||||
return hasPermi(permission) != true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证用户是否具有以下任意一个权限
|
|
||||||
*
|
|
||||||
* @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
|
|
||||||
* @return 用户是否具有以下任意一个权限
|
|
||||||
*/
|
|
||||||
public boolean hasAnyPermi(String permissions) {
|
|
||||||
if (StringUtils.isEmpty(permissions)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
|
|
||||||
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Set<String> authorities = loginUser.getPermissions();
|
|
||||||
for (String permission : permissions.split(PERMISSION_DELIMETER)) {
|
|
||||||
if (permission != null && hasPermissions(authorities, permission)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断用户是否拥有某个角色
|
* 判断用户是否拥有某个角色
|
||||||
@ -103,16 +41,6 @@ public class PermissionService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证用户是否不具备某角色,与 isRole逻辑相反。
|
|
||||||
*
|
|
||||||
* @param role 角色名称
|
|
||||||
* @return 用户是否不具备某角色
|
|
||||||
*/
|
|
||||||
public boolean lacksRole(String role) {
|
|
||||||
return hasRole(role) != true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证用户是否具有以下任意一个角色
|
* 验证用户是否具有以下任意一个角色
|
||||||
*
|
*
|
||||||
@ -135,14 +63,4 @@ public class PermissionService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否包含权限
|
|
||||||
*
|
|
||||||
* @param permissions 权限列表
|
|
||||||
* @param permission 权限字符串
|
|
||||||
* @return 用户是否具备某权限
|
|
||||||
*/
|
|
||||||
private boolean hasPermissions(Set<String> permissions, String permission) {
|
|
||||||
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,9 @@ service.interceptors.response.use(res => {
|
|||||||
}
|
}
|
||||||
).then(() => {
|
).then(() => {
|
||||||
store.dispatch('LogOut').then(() => {
|
store.dispatch('LogOut').then(() => {
|
||||||
location.href = '/index';
|
if (location.pathname !== '/login') { // 避免重复跳转
|
||||||
|
location.href = '/index';
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else if (code === 500) {
|
} else if (code === 500) {
|
||||||
|
@ -392,6 +392,7 @@ export default {
|
|||||||
menuIds: [],
|
menuIds: [],
|
||||||
dataScope: undefined,
|
dataScope: undefined,
|
||||||
deptCheckStrictly: false,
|
deptCheckStrictly: false,
|
||||||
|
menuCheckStrictly: true,
|
||||||
remark: undefined
|
remark: undefined
|
||||||
};
|
};
|
||||||
this.resetForm("form");
|
this.resetForm("form");
|
||||||
@ -471,8 +472,12 @@ export default {
|
|||||||
});
|
});
|
||||||
// 获得角色拥有的菜单集合
|
// 获得角色拥有的菜单集合
|
||||||
listRoleMenus(id).then(response => {
|
listRoleMenus(id).then(response => {
|
||||||
|
// 设置为严格,避免设置父节点自动选中子节点,解决半选中问题
|
||||||
|
this.form.menuCheckStrictly = true
|
||||||
// 设置选中
|
// 设置选中
|
||||||
this.$refs.menu.setCheckedKeys(response.data, true, false);
|
this.$refs.menu.setCheckedKeys(response.data);
|
||||||
|
// 设置为非严格,继续使用半选中
|
||||||
|
this.form.menuCheckStrictly = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/** 分配数据权限操作 */
|
/** 分配数据权限操作 */
|
||||||
@ -523,7 +528,7 @@ export default {
|
|||||||
roleId: this.form.id,
|
roleId: this.form.id,
|
||||||
dataScope: this.form.dataScope,
|
dataScope: this.form.dataScope,
|
||||||
dataScopeDeptIds: this.form.dataScope !== SysDataScopeEnum.DEPT_CUSTOM ? [] :
|
dataScopeDeptIds: this.form.dataScope !== SysDataScopeEnum.DEPT_CUSTOM ? [] :
|
||||||
this.$refs.dept.getCheckedKeys(false)
|
this.$refs.dept.getCheckedKeys()
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
this.msgSuccess("修改成功");
|
this.msgSuccess("修改成功");
|
||||||
this.openDataScope = false;
|
this.openDataScope = false;
|
||||||
@ -536,7 +541,7 @@ export default {
|
|||||||
if (this.form.id !== undefined) {
|
if (this.form.id !== undefined) {
|
||||||
assignRoleMenu({
|
assignRoleMenu({
|
||||||
roleId: this.form.id,
|
roleId: this.form.id,
|
||||||
menuIds: this.$refs.menu.getCheckedKeys(true)
|
menuIds: [...this.$refs.menu.getCheckedKeys(), ...this.$refs.menu.getHalfCheckedKeys()]
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
this.msgSuccess("修改成功");
|
this.msgSuccess("修改成功");
|
||||||
this.openMenu = false;
|
this.openMenu = false;
|
||||||
|
@ -233,11 +233,6 @@ public class OperateLogAspect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void fillContentFields(SysOperateLogCreateReqVO operateLogVO) {
|
|
||||||
operateLogVO.setContent(CONTENT.get());
|
|
||||||
operateLogVO.setExts(EXTS.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) {
|
private static boolean isLogEnable(ProceedingJoinPoint joinPoint, OperateLog operateLog) {
|
||||||
// 有 @OperateLog 注解的情况下
|
// 有 @OperateLog 注解的情况下
|
||||||
if (operateLog != null) {
|
if (operateLog != null) {
|
||||||
|
@ -2,7 +2,7 @@ package cn.iocoder.dashboard.framework.security.core.handler;
|
|||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
|
import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
|
||||||
import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService;
|
import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService;
|
||||||
import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
|
import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
|
||||||
import cn.iocoder.dashboard.util.servlet.ServletUtils;
|
import cn.iocoder.dashboard.util.servlet.ServletUtils;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -26,7 +26,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
|
|||||||
private SecurityProperties securityProperties;
|
private SecurityProperties securityProperties;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SecurityFrameworkService securityFrameworkService;
|
private SecurityAuthFrameworkService securityFrameworkService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||||
|
@ -4,16 +4,11 @@ import cn.iocoder.dashboard.framework.security.core.LoginUser;
|
|||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security 框架 Service 接口,定义 security 组件需要的功能
|
* Security 框架 Auth Service 接口,定义 security 组件需要的功能
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
public interface SecurityFrameworkService extends UserDetailsService {
|
public interface SecurityAuthFrameworkService extends UserDetailsService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 基于 token 退出登录
|
|
||||||
*
|
|
||||||
* @param token token
|
|
||||||
*/
|
|
||||||
void logout(String token);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验 token 的有效性,并获取用户信息
|
* 校验 token 的有效性,并获取用户信息
|
||||||
@ -32,4 +27,11 @@ public interface SecurityFrameworkService extends UserDetailsService {
|
|||||||
*/
|
*/
|
||||||
LoginUser mockLogin(Long userId);
|
LoginUser mockLogin(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于 token 退出登录
|
||||||
|
*
|
||||||
|
* @param token token
|
||||||
|
*/
|
||||||
|
void logout(String token);
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package cn.iocoder.dashboard.framework.security.core.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security 框架 Permission Service 接口,定义 security 组件需要的功能
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public interface SecurityPermissionFrameworkService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有权限
|
||||||
|
*
|
||||||
|
* @param permission 权限
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
boolean hasPermission(String permission);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有权限,任一一个即可
|
||||||
|
*
|
||||||
|
* @param permissions 权限
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
boolean hasAnyPermissions(String... permissions);
|
||||||
|
|
||||||
|
}
|
@ -3,7 +3,9 @@ package cn.iocoder.dashboard.framework.web.core.handler;
|
|||||||
import cn.iocoder.dashboard.common.exception.GlobalException;
|
import cn.iocoder.dashboard.common.exception.GlobalException;
|
||||||
import cn.iocoder.dashboard.common.exception.ServiceException;
|
import cn.iocoder.dashboard.common.exception.ServiceException;
|
||||||
import cn.iocoder.dashboard.common.pojo.CommonResult;
|
import cn.iocoder.dashboard.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.validation.BindException;
|
import org.springframework.validation.BindException;
|
||||||
import org.springframework.validation.FieldError;
|
import org.springframework.validation.FieldError;
|
||||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
@ -64,6 +66,9 @@ public class GlobalExceptionHandler {
|
|||||||
if (ex instanceof ServiceException) {
|
if (ex instanceof ServiceException) {
|
||||||
return serviceExceptionHandler((ServiceException) ex);
|
return serviceExceptionHandler((ServiceException) ex);
|
||||||
}
|
}
|
||||||
|
if (ex instanceof AccessDeniedException) {
|
||||||
|
return accessDeniedExceptionHandler(request, (AccessDeniedException) ex);
|
||||||
|
}
|
||||||
if (ex instanceof GlobalException) {
|
if (ex instanceof GlobalException) {
|
||||||
return globalExceptionHandler(request, (GlobalException) ex);
|
return globalExceptionHandler(request, (GlobalException) ex);
|
||||||
}
|
}
|
||||||
@ -131,7 +136,7 @@ public class GlobalExceptionHandler {
|
|||||||
public CommonResult<?> validationException(ValidationException ex) {
|
public CommonResult<?> validationException(ValidationException ex) {
|
||||||
log.warn("[constraintViolationExceptionHandler]", ex);
|
log.warn("[constraintViolationExceptionHandler]", ex);
|
||||||
// 无法拼接明细的错误信息,因为 Dubbo Consumer 抛出 ValidationException 异常时,是直接的字符串信息,且人类不可读
|
// 无法拼接明细的错误信息,因为 Dubbo Consumer 抛出 ValidationException 异常时,是直接的字符串信息,且人类不可读
|
||||||
return CommonResult.error(BAD_REQUEST.getCode(), "请求参数不正确");
|
return CommonResult.error(BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,6 +163,18 @@ public class GlobalExceptionHandler {
|
|||||||
return CommonResult.error(METHOD_NOT_ALLOWED.getCode(), String.format("请求方法不正确:%s", ex.getMessage()));
|
return CommonResult.error(METHOD_NOT_ALLOWED.getCode(), String.format("请求方法不正确:%s", ex.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 Spring Security 权限不足的异常
|
||||||
|
*
|
||||||
|
* 来源是,使用 @PreAuthorize 注解,AOP 进行权限拦截
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(value = AccessDeniedException.class)
|
||||||
|
public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AccessDeniedException ex) {
|
||||||
|
log.warn("[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]", SecurityUtils.getLoginUserId(),
|
||||||
|
req.getRequestURL(), ex);
|
||||||
|
return CommonResult.error(FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理业务异常 ServiceException
|
* 处理业务异常 ServiceException
|
||||||
*
|
*
|
||||||
|
@ -7,9 +7,7 @@ import lombok.Builder;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.hibernate.validator.constraints.Length;
|
import org.hibernate.validator.constraints.Length;
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
@ -22,7 +20,7 @@ public class SysAuthLoginReqVO {
|
|||||||
|
|
||||||
@ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
|
@ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
|
||||||
@NotEmpty(message = "登陆账号不能为空")
|
@NotEmpty(message = "登陆账号不能为空")
|
||||||
@Length(min = 5, max = 16, message = "账号长度为 5-16 位")
|
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
### 请求 /system/user/page 接口 => 没有权限
|
||||||
|
GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10
|
||||||
|
Authorization: Bearer test104 # 使用测试账号
|
@ -18,6 +18,7 @@ import io.swagger.annotations.Api;
|
|||||||
import io.swagger.annotations.ApiImplicitParam;
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
import io.swagger.annotations.ApiImplicitParams;
|
import io.swagger.annotations.ApiImplicitParams;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@ -41,7 +42,7 @@ public class SysUserController {
|
|||||||
|
|
||||||
@ApiOperation("获得用户分页列表")
|
@ApiOperation("获得用户分页列表")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
// @PreAuthorize("@ss.hasPermi('system:user:list')")
|
@PreAuthorize("@ss.hasPermission('system:user:list')")
|
||||||
public CommonResult<PageResult<SysUserPageItemRespVO>> pageUsers(@Validated SysUserPageReqVO reqVO) {
|
public CommonResult<PageResult<SysUserPageItemRespVO>> pageUsers(@Validated SysUserPageReqVO reqVO) {
|
||||||
// 获得用户分页列表
|
// 获得用户分页列表
|
||||||
PageResult<SysUserDO> pageResult = userService.pageUsers(reqVO);
|
PageResult<SysUserDO> pageResult = userService.pageUsers(reqVO);
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
|
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission;
|
||||||
|
|
||||||
|
import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuDO> {
|
public interface SysRoleMenuMapper extends BaseMapperX<SysRoleMenuDO> {
|
||||||
|
|
||||||
default List<SysRoleMenuDO> selectListByRoleId(Long roleId) {
|
default List<SysRoleMenuDO> selectListByRoleId(Long roleId) {
|
||||||
return selectList(new QueryWrapper<SysRoleMenuDO>().eq("role_id", roleId));
|
return selectList(new QueryWrapper<SysRoleMenuDO>().eq("role_id", roleId));
|
||||||
@ -32,4 +33,9 @@ public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenuDO> {
|
|||||||
.in("menu_id", menuIds));
|
.in("menu_id", menuIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
|
||||||
|
return selectOne(new QueryWrapper<SysRoleMenuDO>().select("id")
|
||||||
|
.gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.dashboard.modules.system.mq.consumer.permission;
|
||||||
|
|
||||||
|
import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||||
|
import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleMenuRefreshMessage;
|
||||||
|
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 针对 {@link SysRoleMenuRefreshMessage} 的消费者
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class SysRoleMenuRefreshConsumer extends AbstractChannelMessageListener<SysRoleMenuRefreshMessage> {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysPermissionService permissionService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(SysRoleMenuRefreshMessage message) {
|
||||||
|
log.info("[onMessage][收到 Role 与 Menu 的关联刷新消息]");
|
||||||
|
permissionService.initLocalCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package cn.iocoder.dashboard.modules.system.mq.message.permission;
|
||||||
|
|
||||||
|
import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色与菜单数据刷新 Message
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SysRoleMenuRefreshMessage implements ChannelMessage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannel() {
|
||||||
|
return "system.role-menu.refresh";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package cn.iocoder.dashboard.modules.system.mq.producer.permission;
|
||||||
|
|
||||||
|
import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils;
|
||||||
|
import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleMenuRefreshMessage;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission 权限相关消息的 Producer
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SysPermissionProducer {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送 {@link SysRoleMenuRefreshMessage} 消息
|
||||||
|
*/
|
||||||
|
public void sendRoleMenuRefreshMessage() {
|
||||||
|
SysRoleMenuRefreshMessage message = new SysRoleMenuRefreshMessage();
|
||||||
|
RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.service.auth;
|
package cn.iocoder.dashboard.modules.system.service.auth;
|
||||||
|
|
||||||
import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService;
|
import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 认证 Service 接口
|
* 认证 Service 接口
|
||||||
@ -9,7 +9,7 @@ import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkSer
|
|||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
public interface SysAuthService extends SecurityFrameworkService {
|
public interface SysAuthService extends SecurityAuthFrameworkService {
|
||||||
|
|
||||||
String login(String username, String password, String captchaUUID, String captchaCode);
|
String login(String username, String password, String captchaUUID, String captchaCode);
|
||||||
|
|
||||||
|
@ -59,6 +59,14 @@ public interface SysMenuService {
|
|||||||
List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||||
Collection<Integer> menusStatuses);
|
Collection<Integer> menusStatuses);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得权限对应的菜单数组
|
||||||
|
*
|
||||||
|
* @param permission 权限标识
|
||||||
|
* @return 数组
|
||||||
|
*/
|
||||||
|
List<SysMenuDO> getMenuListByPermissionFromCache(String permission);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 创建菜单
|
* 创建菜单
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.service.permission;
|
package cn.iocoder.dashboard.modules.system.service.permission;
|
||||||
|
|
||||||
|
import cn.iocoder.dashboard.framework.security.core.service.SecurityPermissionFrameworkService;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
@ -14,12 +15,12 @@ import java.util.Set;
|
|||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
public interface SysPermissionService {
|
public interface SysPermissionService extends SecurityPermissionFrameworkService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化
|
* 初始化
|
||||||
*/
|
*/
|
||||||
void init();
|
void initLocalCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得角色们拥有的菜单列表,从缓存中获取
|
* 获得角色们拥有的菜单列表,从缓存中获取
|
||||||
|
@ -56,6 +56,16 @@ public interface SysRoleService {
|
|||||||
*/
|
*/
|
||||||
boolean hasAnyAdmin(Collection<SysRoleDO> roleList);
|
boolean hasAnyAdmin(Collection<SysRoleDO> roleList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断角色编号数组中,是否有管理员
|
||||||
|
*
|
||||||
|
* @param ids 角色编号数组
|
||||||
|
* @return 是否有管理员
|
||||||
|
*/
|
||||||
|
default boolean hasAnyAdmin(Set<Long> ids) {
|
||||||
|
return hasAnyAdmin(listRolesFromCache(ids));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建角色
|
* 创建角色
|
||||||
*
|
*
|
||||||
|
@ -61,7 +61,7 @@ public class SysMenuServiceImpl implements SysMenuService {
|
|||||||
*
|
*
|
||||||
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
||||||
*/
|
*/
|
||||||
private volatile Multimap<String, SysMenuDO> permMenuCache;
|
private volatile Multimap<String, SysMenuDO> permissionMenuCache;
|
||||||
/**
|
/**
|
||||||
* 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
|
* 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
|
||||||
*/
|
*/
|
||||||
@ -76,7 +76,7 @@ public class SysMenuServiceImpl implements SysMenuService {
|
|||||||
private SysMenuProducer menuProducer;
|
private SysMenuProducer menuProducer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存
|
* 初始化 {@link #menuCache} 和 {@link #permissionMenuCache} 缓存
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@ -95,7 +95,7 @@ public class SysMenuServiceImpl implements SysMenuService {
|
|||||||
permMenuCacheBuilder.put(menuDO.getPermission(), menuDO);
|
permMenuCacheBuilder.put(menuDO.getPermission(), menuDO);
|
||||||
});
|
});
|
||||||
menuCache = menuCacheBuilder.build();
|
menuCache = menuCacheBuilder.build();
|
||||||
permMenuCache = permMenuCacheBuilder.build();
|
permissionMenuCache = permMenuCacheBuilder.build();
|
||||||
assert menuList.size() > 0; // 断言,避免告警
|
assert menuList.size() > 0; // 断言,避免告警
|
||||||
maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||||
log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
|
log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
|
||||||
@ -162,6 +162,11 @@ public class SysMenuServiceImpl implements SysMenuService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SysMenuDO> getMenuListByPermissionFromCache(String permission) {
|
||||||
|
return new ArrayList<>(permissionMenuCache.get(permission));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createMenu(SysMenuCreateReqVO reqVO) {
|
public Long createMenu(SysMenuCreateReqVO reqVO) {
|
||||||
// 校验父菜单存在
|
// 校验父菜单存在
|
||||||
|
@ -2,12 +2,16 @@ package cn.iocoder.dashboard.modules.system.service.permission.impl;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMenuMapper;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMenuMapper;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysUserRoleMapper;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysUserRoleMapper;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO;
|
||||||
|
import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysPermissionProducer;
|
||||||
import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
|
import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
|
||||||
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
|
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
|
||||||
import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
|
import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
|
||||||
@ -18,23 +22,28 @@ import com.google.common.collect.Multimap;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronization;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限 Service 实现类
|
* 权限 Service 实现类
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service("ss") // 使用 Spring Security 的缩写,方便食用
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SysPermissionServiceImpl implements SysPermissionService {
|
public class SysPermissionServiceImpl implements SysPermissionService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
|
||||||
|
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
|
||||||
|
*/
|
||||||
|
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色编号与菜单编号的缓存映射
|
* 角色编号与菜单编号的缓存映射
|
||||||
* key:角色编号
|
* key:角色编号
|
||||||
@ -51,6 +60,10 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|||||||
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
||||||
*/
|
*/
|
||||||
private volatile Multimap<Long, Long> menuRoleCache;
|
private volatile Multimap<Long, Long> menuRoleCache;
|
||||||
|
/**
|
||||||
|
* 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
|
||||||
|
*/
|
||||||
|
private volatile Date maxUpdateTime;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SysRoleMenuMapper roleMenuMapper;
|
private SysRoleMenuMapper roleMenuMapper;
|
||||||
@ -62,14 +75,22 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|||||||
@Resource
|
@Resource
|
||||||
private SysMenuService menuService;
|
private SysMenuService menuService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysPermissionProducer permissionProducer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
|
* 初始化 {@link #roleMenuCache} 和 {@link #menuRoleCache} 缓存
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void initLocalCache() {
|
||||||
|
// 获取角色与菜单的关联列表,如果有更新
|
||||||
|
List<SysRoleMenuDO> roleMenuList = this.loadRoleMenuIfUpdate(maxUpdateTime);
|
||||||
|
if (CollUtil.isEmpty(roleMenuList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化 roleMenuCache 和 menuRoleCache 缓存
|
// 初始化 roleMenuCache 和 menuRoleCache 缓存
|
||||||
List<SysRoleMenuDO> roleMenuList = roleMenuMapper.selectList(null);
|
|
||||||
ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
|
ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
|
||||||
ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
|
ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
|
||||||
roleMenuList.forEach(roleMenuDO -> {
|
roleMenuList.forEach(roleMenuDO -> {
|
||||||
@ -78,9 +99,32 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|||||||
});
|
});
|
||||||
roleMenuCache = roleMenuCacheBuilder.build();
|
roleMenuCache = roleMenuCacheBuilder.build();
|
||||||
menuRoleCache = menuRoleCacheBuilder.build();
|
menuRoleCache = menuRoleCacheBuilder.build();
|
||||||
|
assert roleMenuList.size() > 0; // 断言,避免告警
|
||||||
|
maxUpdateTime = roleMenuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||||
log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
|
log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果角色与菜单的关联发生变化,从数据库中获取最新的全量角色与菜单的关联。
|
||||||
|
* 如果未发生变化,则返回空
|
||||||
|
*
|
||||||
|
* @param maxUpdateTime 当前角色与菜单的关联的最大更新时间
|
||||||
|
* @return 角色与菜单的关联列表
|
||||||
|
*/
|
||||||
|
private List<SysRoleMenuDO> loadRoleMenuIfUpdate(Date maxUpdateTime) {
|
||||||
|
// 第一步,判断是否要更新。
|
||||||
|
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
||||||
|
log.info("[loadRoleMenuIfUpdate][首次加载全量角色与菜单的关联]");
|
||||||
|
} else { // 判断数据库中是否有更新的角色与菜单的关联
|
||||||
|
if (!roleMenuMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
log.info("[loadRoleMenuIfUpdate][增量加载全量角色与菜单的关联]");
|
||||||
|
}
|
||||||
|
// 第二步,如果有更新,则从数据库加载所有角色与菜单的关联
|
||||||
|
return roleMenuMapper.selectList();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
public List<SysMenuDO> listRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||||
Collection<Integer> menusStatuses) {
|
Collection<Integer> menusStatuses) {
|
||||||
@ -140,6 +184,15 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|||||||
if (!CollectionUtil.isEmpty(deleteMenuIds)) {
|
if (!CollectionUtil.isEmpty(deleteMenuIds)) {
|
||||||
roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
|
roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
|
||||||
}
|
}
|
||||||
|
// 发送刷新消息. 注意,需要事务提交后,在进行发送刷新消息。不然 db 还未提交,结果缓存先刷新了
|
||||||
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCommit() {
|
||||||
|
permissionProducer.sendRoleMenuRefreshMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -189,4 +242,39 @@ public class SysPermissionServiceImpl implements SysPermissionService {
|
|||||||
// TODO 实现我
|
// TODO 实现我
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(String permission) {
|
||||||
|
return hasAnyPermissions(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAnyPermissions(String... permissions) {
|
||||||
|
// 如果为空,说明已经有权限
|
||||||
|
if (ArrayUtil.isEmpty(permissions)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得当前登陆的角色。如果为空,说明没有权限
|
||||||
|
Set<Long> roleIds = SecurityUtils.getLoginUserRoleIds();
|
||||||
|
if (CollUtil.isEmpty(roleIds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 判断是否是超管。如果是,当然符合条件
|
||||||
|
if (roleService.hasAnyAdmin(roleIds)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历权限,判断是否有一个满足
|
||||||
|
return Arrays.stream(permissions).anyMatch(permission -> {
|
||||||
|
List<SysMenuDO> menuList = menuService.getMenuListByPermissionFromCache(permission);
|
||||||
|
// 采用严格模式,如果权限找不到对应的 Menu 的话,认为
|
||||||
|
if (CollUtil.isEmpty(menuList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 获得是否拥有该权限,任一一个
|
||||||
|
return menuList.stream().anyMatch(menu -> CollUtil.containsAny(roleIds,
|
||||||
|
menuRoleCache.get(menu.getId())));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -217,6 +217,8 @@ public class SysRoleServiceImpl implements SysRoleService {
|
|||||||
updateObject.setId(id);
|
updateObject.setId(id);
|
||||||
updateObject.setStatus(status);
|
updateObject.setStatus(status);
|
||||||
roleMapper.updateById(updateObject);
|
roleMapper.updateById(updateObject);
|
||||||
|
// 发送刷新消息
|
||||||
|
roleProducer.sendRoleRefreshMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -229,6 +231,8 @@ public class SysRoleServiceImpl implements SysRoleService {
|
|||||||
updateObject.setDataScope(dataScope);
|
updateObject.setDataScope(dataScope);
|
||||||
updateObject.setDataScopeDeptIds(dataScopeDeptIds);
|
updateObject.setDataScopeDeptIds(dataScopeDeptIds);
|
||||||
roleMapper.updateById(updateObject);
|
roleMapper.updateById(updateObject);
|
||||||
|
// 发送刷新消息
|
||||||
|
roleProducer.sendRoleRefreshMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +34,7 @@ yudao:
|
|||||||
token-timeout: 1d
|
token-timeout: 1d
|
||||||
session-timeout: 30m
|
session-timeout: 30m
|
||||||
mock-enable: true
|
mock-enable: true
|
||||||
mock-secret: yudaoyuanma
|
mock-secret: test
|
||||||
swagger:
|
swagger:
|
||||||
title: 管理后台
|
title: 管理后台
|
||||||
description: 提供管理员管理的所有功能
|
description: 提供管理员管理的所有功能
|
||||||
|
Loading…
x
Reference in New Issue
Block a user