diff --git a/pom.xml b/pom.xml
index 16501c330..7763720bc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,6 +26,7 @@
2.2.11
3.3.0
2.3
+ 1.28.0
3.5.0
3.9.1
5.7.18
@@ -137,6 +138,19 @@
${velocity.version}
+
+
+ cn.dev33
+ sa-token-spring-boot-starter
+ ${satoken.version}
+
+
+
+ cn.dev33
+ sa-token-jwt
+ ${satoken.version}
+
+
com.sun.xml.bind
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
index ef81c99a3..255f0b11b 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
@@ -1,5 +1,6 @@
package com.ruoyi.web.controller.monitor;
+import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
@@ -9,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -30,7 +30,7 @@ public class CacheController {
private final RedisTemplate redisTemplate;
@ApiOperation("获取缓存监控详细信息")
- @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @SaCheckPermission("monitor:cache:list")
@GetMapping()
public AjaxResult
-
+
- org.springframework.boot
- spring-boot-starter-security
+ cn.dev33
+ sa-token-spring-boot-starter
+
+
+
+ cn.dev33
+ sa-token-jwt
+
+
+
+ org.springframework.security
+ spring-security-crypto
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
index 617567d0b..5e07c96c0 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -1,6 +1,5 @@
package com.ruoyi.common.constant;
-
/**
* 通用常量信息
*
@@ -66,7 +65,12 @@ public interface Constants {
/**
* 登录用户 redis key
*/
- String LOGIN_TOKEN_KEY = "login_tokens:";
+ public static final String LOGIN_TOKEN_KEY = "Authorization:login:token:";
+
+ /**
+ * 在线用户 redis key
+ */
+ public static final String ONLINE_TOKEN_KEY = "online_tokens:";
/**
* 防重提交 redis key
@@ -103,41 +107,11 @@ public interface Constants {
*/
String TOKEN = "token";
- /**
- * 令牌前缀
- */
- String TOKEN_PREFIX = "Bearer ";
-
/**
* 令牌前缀
*/
String LOGIN_USER_KEY = "login_user_key";
- /**
- * 用户ID
- */
- String JWT_USERID = "userid";
-
- /**
- * 用户名称
- */
- String JWT_USERNAME = "sub";
-
- /**
- * 用户头像
- */
- String JWT_AVATAR = "avatar";
-
- /**
- * 创建时间
- */
- String JWT_CREATED = "created";
-
- /**
- * 用户权限
- */
- String JWT_AUTHORITIES = "authorities";
-
/**
* 参数管理 cache key
*/
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
index bad8f77a5..4c5e1977c 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
@@ -2,7 +2,7 @@ package com.ruoyi.common.core.controller;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.LoginUtils;
import com.ruoyi.common.utils.StringUtils;
/**
@@ -71,27 +71,27 @@ public class BaseController {
* 获取用户缓存信息
*/
public LoginUser getLoginUser() {
- return SecurityUtils.getLoginUser();
+ return LoginUtils.getLoginUser();
}
/**
* 获取登录用户id
*/
public Long getUserId() {
- return getLoginUser().getUserId();
+ return LoginUtils.getUserId();
}
/**
* 获取登录部门id
*/
public Long getDeptId() {
- return getLoginUser().getDeptId();
+ return LoginUtils.getDeptId();
}
/**
* 获取登录用户名
*/
public String getUsername() {
- return getLoginUser().getUsername();
+ return LoginUtils.getUsername();
}
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java
new file mode 100644
index 000000000..5eb52806c
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java
@@ -0,0 +1,62 @@
+package com.ruoyi.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 当前在线会话
+ *
+ * @author ruoyi
+ */
+
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+public class UserOnlineDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 会话编号
+ */
+ private String tokenId;
+
+ /**
+ * 部门名称
+ */
+ private String deptName;
+
+ /**
+ * 用户名称
+ */
+ private String userName;
+
+ /**
+ * 登录IP地址
+ */
+ private String ipaddr;
+
+ /**
+ * 登录地址
+ */
+ private String loginLocation;
+
+ /**
+ * 浏览器类型
+ */
+ private String browser;
+
+ /**
+ * 操作系统
+ */
+ private String os;
+
+ /**
+ * 登录时间
+ */
+ private Long loginTime;
+
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
index 0506883b2..7554e3f4b 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
@@ -1,13 +1,10 @@
package com.ruoyi.common.core.domain.model;
-import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.UserDetails;
-import java.util.Collection;
+import java.io.Serializable;
import java.util.Set;
/**
@@ -19,7 +16,7 @@ import java.util.Set;
@Data
@NoArgsConstructor
@Accessors(chain = true)
-public class LoginUser implements UserDetails {
+public class LoginUser implements Serializable {
private static final long serialVersionUID = 1L;
@@ -71,72 +68,16 @@ public class LoginUser implements UserDetails {
/**
* 菜单权限
*/
- private Set menuPermissions;
+ private Set menuPermission;
/**
* 角色权限
*/
- private Set rolePermissions;
+ private Set rolePermission;
/**
* 用户名
*/
private String username;
- /**
- * 密码
- */
- private String password;
-
- @JsonIgnore
- @Override
- public String getPassword() {
- return password;
- }
-
- @Override
- public String getUsername() {
- return username;
- }
-
- /**
- * 账户是否未过期,过期无法验证
- */
- @JsonIgnore
- @Override
- public boolean isAccountNonExpired() {
- return true;
- }
-
- /**
- * 指定用户是否解锁,锁定的用户无法进行身份验证
- */
- @JsonIgnore
- @Override
- public boolean isAccountNonLocked() {
- return true;
- }
-
- /**
- * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
- */
- @JsonIgnore
- @Override
- public boolean isCredentialsNonExpired() {
- return true;
- }
-
- /**
- * 是否可用 ,禁用的用户不能身份验证
- */
- @JsonIgnore
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- @Override
- public Collection extends GrantedAuthority> getAuthorities() {
- return null;
- }
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/TokenService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/TokenService.java
deleted file mode 100644
index d9a9f0acf..000000000
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/TokenService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.ruoyi.common.core.service;
-
-import com.ruoyi.common.core.domain.model.LoginUser;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * token验证处理
- *
- * @author Lion Li
- */
-public interface TokenService {
-
- /**
- * 获取用户身份信息
- *
- * @return 用户信息
- */
- LoginUser getLoginUser(HttpServletRequest request);
-
- /**
- * 设置用户身份信息
- */
- void setLoginUser(LoginUser loginUser);
-
- /**
- * 删除用户身份信息
- */
- void delLoginUser(String token);
-
- /**
- * 创建令牌
- *
- * @param loginUser 用户信息
- * @return 令牌
- */
- String createToken(LoginUser loginUser);
-
- /**
- * 验证令牌有效期,相差不足20分钟,自动刷新缓存
- *
- * @param loginUser
- * @return 令牌
- */
- void verifyToken(LoginUser loginUser);
-
- /**
- * 刷新令牌有效期
- *
- * @param loginUser 登录信息
- */
- void refreshToken(LoginUser loginUser);
-
- /**
- * 设置用户代理信息
- *
- * @param loginUser 登录信息
- */
- void setUserAgent(LoginUser loginUser);
-
- /**
- * 从令牌中获取用户名
- *
- * @param token 令牌
- * @return 用户名
- */
- String getUsernameFromToken(String token);
-
-}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java
new file mode 100644
index 000000000..e6ac849df
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java
@@ -0,0 +1,27 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 设备类型
+ * 针对一套 用户体系
+ *
+ * @author Lion Li
+ */
+@Getter
+@AllArgsConstructor
+public enum DeviceType {
+
+ /**
+ * pc端
+ */
+ PC("pc"),
+
+ /**
+ * app端
+ */
+ APP("app");
+
+ private final String device;
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserType.java
new file mode 100644
index 000000000..9c3b53195
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserType.java
@@ -0,0 +1,27 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 设备类型
+ * 针对两套 用户体系
+ *
+ * @author Lion Li
+ */
+@Getter
+@AllArgsConstructor
+public enum UserType {
+
+ /**
+ * pc端
+ */
+ SYS_USER("sys_user:"),
+
+ /**
+ * app端
+ */
+ APP_USER("app_user:");
+
+ private final String userType;
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/properties/TokenProperties.java b/ruoyi-common/src/main/java/com/ruoyi/common/properties/TokenProperties.java
deleted file mode 100644
index 927a9413d..000000000
--- a/ruoyi-common/src/main/java/com/ruoyi/common/properties/TokenProperties.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.ruoyi.common.properties;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-/**
- * token 配置属性
- *
- * @author Lion Li
- */
-@Data
-@Component
-@ConfigurationProperties(prefix = "token")
-public class TokenProperties {
-
- /**
- * 令牌自定义标识
- */
- private String header;
-
- /**
- * 令牌秘钥
- */
- private String secret;
-
- /**
- * 令牌有效期(默认30分钟)
- */
- private int expireTime;
-}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/LoginUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LoginUtils.java
new file mode 100644
index 000000000..a5dc50eb1
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LoginUtils.java
@@ -0,0 +1,107 @@
+package com.ruoyi.common.utils;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.enums.DeviceType;
+import com.ruoyi.common.enums.UserType;
+import com.ruoyi.common.exception.UtilException;
+
+/**
+ * 登录鉴权工具
+ * 为适配多端登录而封装
+ *
+ * @author Lion Li
+ */
+public class LoginUtils {
+
+ private final static String LOGIN_USER_KEY = "loginUser";
+
+ /**
+ * 登录系统
+ * 针对两套用户体系
+ * @param loginUser 登录用户信息
+ */
+ public static void login(LoginUser loginUser, UserType userType) {
+ StpUtil.login(userType.getUserType() + loginUser.getUserId());
+ setLoginUser(loginUser);
+ }
+
+ /**
+ * 登录系统 基于 设备类型
+ * 针对一套用户体系
+ * @param loginUser 登录用户信息
+ */
+ public static void loginByDevice(LoginUser loginUser, UserType userType, DeviceType deviceType) {
+ StpUtil.login(userType.getUserType() + loginUser.getUserId(), deviceType.getDevice());
+ setLoginUser(loginUser);
+ }
+
+ /**
+ * 设置用户数据
+ */
+ public static void setLoginUser(LoginUser loginUser) {
+ StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
+ }
+
+ /**
+ * 获取用户
+ **/
+ public static LoginUser getLoginUser() {
+ return (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
+ }
+
+ /**
+ * 获取用户id
+ */
+ public static Long getUserId() {
+ LoginUser loginUser = getLoginUser();
+ if (ObjectUtil.isNull(loginUser)) {
+ String loginId = StpUtil.getLoginIdAsString();
+ String userId;
+ String replace = "";
+ if (StringUtils.contains(loginId, UserType.SYS_USER.getUserType())) {
+ userId = StringUtils.replace(loginId, UserType.SYS_USER.getUserType(), replace);
+ } else if (StringUtils.contains(loginId, UserType.APP_USER.getUserType())){
+ userId = StringUtils.replace(loginId, UserType.APP_USER.getUserType(), replace);
+ } else {
+ throw new UtilException("登录用户: LoginId异常 => " + loginId);
+ }
+ return Long.parseLong(userId);
+ }
+ return loginUser.getUserId();
+ }
+
+ /**
+ * 获取部门ID
+ **/
+ public static Long getDeptId() {
+ return getLoginUser().getDeptId();
+ }
+
+ /**
+ * 获取用户账户
+ **/
+ public static String getUsername() {
+ return getLoginUser().getUsername();
+ }
+
+ /**
+ * 获取用户类型
+ */
+ public static UserType getUserType() {
+ String loginId = StpUtil.getLoginIdAsString();
+ return getUserType(loginId);
+ }
+
+ public static UserType getUserType(Object loginId) {
+ if (StringUtils.contains(loginId.toString(), UserType.SYS_USER.getUserType())) {
+ return UserType.SYS_USER;
+ } else if (StringUtils.contains(loginId.toString(), UserType.APP_USER.getUserType())){
+ return UserType.APP_USER;
+ } else {
+ throw new UtilException("登录用户: LoginId异常 => " + loginId);
+ }
+ }
+
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
index 43cb8fe17..72c9453c0 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
@@ -1,73 +1,17 @@
package com.ruoyi.common.utils;
-import cn.hutool.http.HttpStatus;
-import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.exception.ServiceException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* 安全服务工具类
*
- * @author ruoyi
+ * @author Long Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SecurityUtils {
- /**
- * 用户ID
- **/
- public static Long getUserId() {
- try {
- return getLoginUser().getUserId();
- } catch (Exception e) {
- throw new ServiceException("获取用户ID异常", HttpStatus.HTTP_UNAUTHORIZED);
- }
- }
-
- /**
- * 获取部门ID
- **/
- public static Long getDeptId() {
- try {
- return getLoginUser().getDeptId();
- } catch (Exception e) {
- throw new ServiceException("获取部门ID异常", HttpStatus.HTTP_UNAUTHORIZED);
- }
- }
-
- /**
- * 获取用户账户
- **/
- public static String getUsername() {
- try {
- return getLoginUser().getUsername();
- } catch (Exception e) {
- throw new ServiceException("获取用户账户异常", HttpStatus.HTTP_UNAUTHORIZED);
- }
- }
-
- /**
- * 获取用户
- **/
- public static LoginUser getLoginUser() {
- try {
- return (LoginUser) getAuthentication().getPrincipal();
- } catch (Exception e) {
- throw new ServiceException("获取用户信息异常", HttpStatus.HTTP_UNAUTHORIZED);
- }
- }
-
- /**
- * 获取Authentication
- */
- public static Authentication getAuthentication() {
- return SecurityContextHolder.getContext().getAuthentication();
- }
-
/**
* 生成BCryptPasswordEncoder密码
*
diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
index 003d7dc45..9a8cf63c8 100644
--- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
+++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
@@ -1,5 +1,6 @@
package com.ruoyi.demo.controller;
+import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
@@ -22,7 +23,6 @@ import com.ruoyi.demo.service.ITestDemoService;
import io.swagger.annotations.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -53,28 +53,28 @@ public class TestDemoController extends BaseController {
* 查询测试单表列表
*/
@ApiOperation("查询测试单表列表")
- @PreAuthorize("@ss.hasPermi('demo:demo:list')")
+ @SaCheckPermission("demo:demo:list")
@GetMapping("/list")
public TableDataInfo list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
return iTestDemoService.queryPageList(bo, pageQuery);
}
- /**
- * 自定义分页查询
- */
- @ApiOperation("自定义分页查询")
- @PreAuthorize("@ss.hasPermi('demo:demo:list')")
- @GetMapping("/page")
- public TableDataInfo page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
- return iTestDemoService.customPageList(bo, pageQuery);
- }
+ /**
+ * 自定义分页查询
+ */
+ @ApiOperation("自定义分页查询")
+ @SaCheckPermission("demo:demo:list")
+ @GetMapping("/page")
+ public TableDataInfo page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
+ return iTestDemoService.customPageList(bo, pageQuery);
+ }
@ApiOperation("导入测试-校验")
@ApiImplicitParams({
@ApiImplicitParam(name = "file", value = "导入文件", dataType = "java.io.File", required = true),
})
@Log(title = "测试单表", businessType = BusinessType.IMPORT)
- @PreAuthorize("@ss.hasPermi('demo:demo:import')")
+ @SaCheckPermission("demo:demo:import")
@PostMapping("/importData")
public AjaxResult importData(@RequestPart("file") MultipartFile file) throws Exception {
ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
@@ -88,7 +88,7 @@ public class TestDemoController extends BaseController {
* 导出测试单表列表
*/
@ApiOperation("导出测试单表列表")
- @PreAuthorize("@ss.hasPermi('demo:demo:export')")
+ @SaCheckPermission("demo:demo:export")
@Log(title = "测试单表", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
@@ -104,7 +104,7 @@ public class TestDemoController extends BaseController {
* 获取测试单表详细信息
*/
@ApiOperation("获取测试单表详细信息")
- @PreAuthorize("@ss.hasPermi('demo:demo:query')")
+ @SaCheckPermission("demo:demo:query")
@GetMapping("/{id}")
public AjaxResult getInfo(@ApiParam("测试ID")
@NotNull(message = "主键不能为空")
@@ -116,7 +116,7 @@ public class TestDemoController extends BaseController {
* 新增测试单表
*/
@ApiOperation("新增测试单表")
- @PreAuthorize("@ss.hasPermi('demo:demo:add')")
+ @SaCheckPermission("demo:demo:add")
@Log(title = "测试单表", businessType = BusinessType.INSERT)
@RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "不允许重复提交")
@PostMapping()
@@ -131,7 +131,7 @@ public class TestDemoController extends BaseController {
* 修改测试单表
*/
@ApiOperation("修改测试单表")
- @PreAuthorize("@ss.hasPermi('demo:demo:edit')")
+ @SaCheckPermission("demo:demo:edit")
@Log(title = "测试单表", businessType = BusinessType.UPDATE)
@RepeatSubmit
@PutMapping()
@@ -143,8 +143,8 @@ public class TestDemoController extends BaseController {
* 删除测试单表
*/
@ApiOperation("删除测试单表")
- @PreAuthorize("@ss.hasPermi('demo:demo:remove')")
- @Log(title = "测试单表", businessType = BusinessType.DELETE)
+ @SaCheckPermission("demo:demo:remove")
+ @Log(title = "测试单表" , businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@ApiParam("测试ID串")
@NotEmpty(message = "主键不能为空")
diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
index 463ea3bf6..f0231bb2c 100644
--- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
+++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
@@ -1,5 +1,6 @@
package com.ruoyi.demo.controller;
+import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
@@ -17,7 +18,6 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -46,7 +46,7 @@ public class TestTreeController extends BaseController {
* 查询测试树表列表
*/
@ApiOperation("查询测试树表列表")
- @PreAuthorize("@ss.hasPermi('demo:tree:list')")
+ @SaCheckPermission("demo:tree:list")
@GetMapping("/list")
public AjaxResult> list(@Validated(QueryGroup.class) TestTreeBo bo) {
List list = iTestTreeService.queryList(bo);
@@ -57,7 +57,7 @@ public class TestTreeController extends BaseController {
* 导出测试树表列表
*/
@ApiOperation("导出测试树表列表")
- @PreAuthorize("@ss.hasPermi('demo:tree:export')")
+ @SaCheckPermission("demo:tree:export")
@Log(title = "测试树表", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
@@ -69,7 +69,7 @@ public class TestTreeController extends BaseController {
* 获取测试树表详细信息
*/
@ApiOperation("获取测试树表详细信息")
- @PreAuthorize("@ss.hasPermi('demo:tree:query')")
+ @SaCheckPermission("demo:tree:query")
@GetMapping("/{id}")
public AjaxResult getInfo(@ApiParam("测试树ID")
@NotNull(message = "主键不能为空")
@@ -81,7 +81,7 @@ public class TestTreeController extends BaseController {
* 新增测试树表
*/
@ApiOperation("新增测试树表")
- @PreAuthorize("@ss.hasPermi('demo:tree:add')")
+ @SaCheckPermission("demo:tree:add")
@Log(title = "测试树表", businessType = BusinessType.INSERT)
@RepeatSubmit
@PostMapping()
@@ -93,7 +93,7 @@ public class TestTreeController extends BaseController {
* 修改测试树表
*/
@ApiOperation("修改测试树表")
- @PreAuthorize("@ss.hasPermi('demo:tree:edit')")
+ @SaCheckPermission("demo:tree:edit")
@Log(title = "测试树表", businessType = BusinessType.UPDATE)
@RepeatSubmit
@PutMapping()
@@ -105,8 +105,8 @@ public class TestTreeController extends BaseController {
* 删除测试树表
*/
@ApiOperation("删除测试树表")
- @PreAuthorize("@ss.hasPermi('demo:tree:remove')")
- @Log(title = "测试树表", businessType = BusinessType.DELETE)
+ @SaCheckPermission("demo:tree:remove")
+ @Log(title = "测试树表" , businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@ApiParam("测试树ID串")
@NotEmpty(message = "主键不能为空")
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
new file mode 100644
index 000000000..58427da66
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
@@ -0,0 +1,138 @@
+package com.ruoyi.framework.aspectj;
+
+import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.core.domain.BaseEntity;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.service.UserService;
+import com.ruoyi.common.utils.LoginUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.stereotype.Component;
+
+/**
+ * 数据过滤处理
+ *
+ * @author Lion Li
+ * @deprecated 3.6.0 移除 {@link com.ruoyi.framework.handler.PlusDataPermissionHandler}
+ */
+@Aspect
+@Component
+@Deprecated
+public class DataScopeAspect {
+
+ /**
+ * 全部数据权限
+ */
+ public static final String DATA_SCOPE_ALL = "1";
+
+ /**
+ * 自定数据权限
+ */
+ public static final String DATA_SCOPE_CUSTOM = "2";
+
+ /**
+ * 部门数据权限
+ */
+ public static final String DATA_SCOPE_DEPT = "3";
+
+ /**
+ * 部门及以下数据权限
+ */
+ public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
+
+ /**
+ * 仅本人数据权限
+ */
+ public static final String DATA_SCOPE_SELF = "5";
+
+ /**
+ * 数据权限过滤关键字
+ */
+ public static final String DATA_SCOPE = "dataScope";
+
+ @Before("@annotation(controllerDataScope)")
+ public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable {
+ clearDataScope(point);
+ handleDataScope(point, controllerDataScope);
+ }
+
+ protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) {
+ // 获取当前的用户
+ SysUser currentUser = SpringUtils.getBean(UserService.class).selectUserById(LoginUtils.getUserId());
+ // 如果是超级管理员,则不过滤数据
+ if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
+ dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
+ controllerDataScope.userAlias(), controllerDataScope.isUser());
+ }
+ }
+
+ /**
+ * 数据范围过滤
+ *
+ * @param joinPoint 切点
+ * @param user 用户
+ * @param userAlias 别名
+ */
+ public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, boolean isUser) {
+ StringBuilder sqlString = new StringBuilder();
+
+ // 将 "." 提取出,不写别名为单表查询,写别名为多表查询
+ deptAlias = StringUtils.isNotBlank(deptAlias) ? deptAlias + "." : "";
+ userAlias = StringUtils.isNotBlank(userAlias) ? userAlias + "." : "";
+
+ for (SysRole role : user.getRoles()) {
+ String dataScope = role.getDataScope();
+ if (DATA_SCOPE_ALL.equals(dataScope)) {
+ sqlString = new StringBuilder();
+ break;
+ } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
+ sqlString.append(StringUtils.format(
+ " OR {}dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
+ deptAlias, role.getRoleId()));
+ } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
+ sqlString.append(StringUtils.format(" OR {}dept_id = {} ",
+ deptAlias, user.getDeptId()));
+ } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
+ sqlString.append(StringUtils.format(
+ " OR {}dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
+ deptAlias, user.getDeptId(), user.getDeptId()));
+ } else if (DATA_SCOPE_SELF.equals(dataScope)) {
+ if (isUser) {
+ sqlString.append(StringUtils.format(" OR {}user_id = {} ",
+ userAlias, user.getUserId()));
+ } else {
+ // 数据权限为仅本人且没有userAlias别名不查询任何数据
+ sqlString.append(" OR 1=0 ");
+ }
+ }
+ }
+
+ if (StringUtils.isNotBlank(sqlString.toString())) {
+ putDataScope(joinPoint, sqlString.substring(4));
+ }
+ }
+
+ /**
+ * 拼接权限sql前先清空params.dataScope参数防止注入
+ */
+ private void clearDataScope(final JoinPoint joinPoint) {
+ Object params = joinPoint.getArgs()[0];
+ if (StringUtils.isNotNull(params)) {
+ putDataScope(joinPoint, "");
+ }
+ }
+
+ private static void putDataScope(JoinPoint joinPoint, String sql) {
+ Object params = joinPoint.getArgs()[0];
+ if (StringUtils.isNotNull(params)) {
+ if (params instanceof BaseEntity) {
+ BaseEntity baseEntity = (BaseEntity) params;
+ baseEntity.getParams().put(DATA_SCOPE, sql);
+ }
+ }
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
index 642076d9e..e7aa90996 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
@@ -2,12 +2,11 @@ package com.ruoyi.framework.aspectj;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.dto.OperLogDTO;
-import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.service.OperLogService;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.utils.JsonUtils;
-import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.LoginUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
@@ -60,9 +59,6 @@ public class LogAspect {
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try {
- // 获取当前的用户
- LoginUser loginUser = SecurityUtils.getLoginUser();
-
// *========数据库日志=========*//
OperLogDTO operLog = new OperLogDTO();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
@@ -70,9 +66,7 @@ public class LogAspect {
String ip = ServletUtils.getClientIP();
operLog.setOperIp(ip);
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
- if (loginUser != null) {
- operLog.setOperName(loginUser.getUsername());
- }
+ operLog.setOperName(LoginUtils.getUsername());
if (e != null) {
operLog.setStatus(BusinessStatus.FAIL.ordinal());
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
index 48761ac29..9fa586b41 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
@@ -1,10 +1,10 @@
package com.ruoyi.framework.aspectj;
+import cn.dev33.satoken.SaManager;
import cn.hutool.crypto.SecureUtil;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.common.properties.TokenProperties;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.ServletUtils;
@@ -37,7 +37,6 @@ import java.util.concurrent.TimeUnit;
@Component
public class RepeatSubmitAspect {
- private final TokenProperties tokenProperties;
private final RepeatSubmitProperties repeatSubmitProperties;
@Before("@annotation(repeatSubmit)")
@@ -57,7 +56,7 @@ public class RepeatSubmitAspect {
String url = request.getRequestURI();
// 唯一值(没有消息头则使用请求地址)
- String submitKey = StringUtils.trimToEmpty(request.getHeader(tokenProperties.getHeader()));
+ String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName()));
submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
// 唯一标识(指定key + url + 消息头)
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
deleted file mode 100644
index 728773d94..000000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.ruoyi.framework.config;
-
-import cn.hutool.core.util.ArrayUtil;
-import com.ruoyi.common.exception.ServiceException;
-import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
-import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
-
-import java.util.Arrays;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ScheduledExecutorService;
-
-/**
- * 异步配置
- *
- * @author Lion Li
- */
-@EnableAsync
-@Configuration
-public class AsyncConfig extends AsyncConfigurerSupport {
-
- @Autowired
- @Qualifier("scheduledExecutorService")
- private ScheduledExecutorService scheduledExecutorService;
-
- /**
- * 异步执行需要使用权限框架自带的包装线程池 保证权限信息的传递
- */
- @Override
- public Executor getAsyncExecutor() {
- return new DelegatingSecurityContextExecutorService(scheduledExecutorService);
- }
-
- /**
- * 异步执行异常处理
- */
- @Override
- public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
- return (throwable, method, objects) -> {
- throwable.printStackTrace();
- StringBuilder sb = new StringBuilder();
- sb.append("Exception message - ").append(throwable.getMessage())
- .append(", Method name - ").append(method.getName());
- if (ArrayUtil.isNotEmpty(objects)) {
- sb.append(", Parameter value - ").append(Arrays.toString(objects));
- }
- throw new ServiceException(sb.toString());
- };
- }
-
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
index 83565b6ec..f43ecab7d 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
@@ -19,10 +19,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class ResourcesConfig implements WebMvcConfigurer {
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- }
-
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 全局链路跟踪拦截器
@@ -31,6 +27,10 @@ public class ResourcesConfig implements WebMvcConfigurer {
registry.addInterceptor(new PlusWebInvokeTimeInterceptor());
}
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ }
+
/**
* 跨域配置
*/
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java
new file mode 100644
index 000000000..260424545
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java
@@ -0,0 +1,63 @@
+package com.ruoyi.framework.config;
+
+import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
+import cn.dev33.satoken.interceptor.SaRouteInterceptor;
+import cn.dev33.satoken.jwt.StpLogicJwtForStyle;
+import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.dev33.satoken.stp.StpUtil;
+import com.ruoyi.common.utils.LoginUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.config.properties.SecurityProperties;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * sa-token 配置
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@Configuration
+public class SaTokenConfig implements WebMvcConfigurer {
+
+ @Autowired
+ private SecurityProperties securityProperties;
+
+ /**
+ * 注册sa-token的拦截器
+ */
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 注册路由拦截器,自定义验证规则
+ registry.addInterceptor(new SaRouteInterceptor((request, response, handler) -> {
+ // 登录验证 -- 排除多个路径
+ SaRouter
+ // 获取所有的
+ .match("/**")
+ // 排除下不需要拦截的
+ .notMatch(securityProperties.getExcludes())
+ .check(() -> {
+ if (log.isDebugEnabled()) {
+ Long userId = LoginUtils.getUserId();
+ if (StringUtils.isNotNull(userId)) {
+ log.debug("剩余有效时间: {}", StpUtil.getTokenTimeout());
+ log.debug("临时有效时间: {}", StpUtil.getTokenActivityTimeout());
+ }
+ }
+ });
+ })).addPathPatterns("/**");
+ registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
+ }
+
+ @Bean
+ public StpLogic getStpLogicJwt() {
+ // Sa-Token 整合 jwt (Style模式)
+ return new StpLogicJwtForStyle();
+ }
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
deleted file mode 100644
index 450eccd45..000000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package com.ruoyi.framework.config;
-
-import com.ruoyi.framework.config.properties.SecurityProperties;
-import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
-import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
-import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.http.HttpMethod;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.config.http.SessionCreationPolicy;
-import org.springframework.security.core.userdetails.UserDetailsService;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-import org.springframework.security.web.authentication.logout.LogoutFilter;
-import org.springframework.web.filter.CorsFilter;
-
-/**
- * spring security配置
- *
- * @author ruoyi
- */
-@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
-public class SecurityConfig extends WebSecurityConfigurerAdapter {
- /**
- * 自定义用户认证逻辑
- */
- @Autowired
- private UserDetailsService userDetailsService;
-
- /**
- * 认证失败处理类
- */
- @Autowired
- private AuthenticationEntryPointImpl unauthorizedHandler;
-
- /**
- * 退出处理类
- */
- @Autowired
- private LogoutSuccessHandlerImpl logoutSuccessHandler;
-
- /**
- * token认证过滤器
- */
- @Autowired
- private JwtAuthenticationTokenFilter authenticationTokenFilter;
-
- /**
- * 跨域过滤器
- */
- @Autowired
- private CorsFilter corsFilter;
-
- @Autowired
- private SecurityProperties securityProperties;
-
- /**
- * 解决 无法直接注入 AuthenticationManager
- *
- * @return
- * @throws Exception
- */
- @Bean
- @Override
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
-
- /**
- * anyRequest | 匹配所有请求路径
- * access | SpringEl表达式结果为true时可以访问
- * anonymous | 匿名可以访问
- * denyAll | 用户不能访问
- * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
- * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
- * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
- * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
- * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
- * hasRole | 如果有参数,参数表示角色,则其角色可以访问
- * permitAll | 用户可以任意访问
- * rememberMe | 允许通过remember-me登录的用户访问
- * authenticated | 用户登录后可访问
- */
- @Override
- protected void configure(HttpSecurity httpSecurity) throws Exception {
- httpSecurity
- // CSRF禁用,因为不使用session
- .csrf().disable()
- // 认证失败处理类
- .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
- // 基于token,所以不需要session
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
- // 过滤请求
- .authorizeRequests()
- .antMatchers(
- HttpMethod.GET,
- "/",
- "/*.html",
- "/**/*.html",
- "/**/*.css",
- "/**/*.js"
- ).permitAll()
- .antMatchers(securityProperties.getAnonymous()).anonymous()
- .antMatchers(securityProperties.getPermitAll()).permitAll()
- // 除上面外的所有请求全部需要鉴权认证
- .anyRequest().authenticated()
- .and()
- .headers().frameOptions().disable();
- httpSecurity.logout().logoutUrl(securityProperties.getLogoutUrl()).logoutSuccessHandler(logoutSuccessHandler);
- // 添加JWT filter
- httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
- // 添加CORS filter
- httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
- httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
- }
-
- /**
- * 强散列哈希加密实现
- */
- @Bean
- public BCryptPasswordEncoder bCryptPasswordEncoder() {
- return new BCryptPasswordEncoder();
- }
-
- /**
- * 身份认证接口
- */
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
- }
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
index 16437f44f..167ac2ec1 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
@@ -1,8 +1,8 @@
package com.ruoyi.framework.config;
+import cn.dev33.satoken.config.SaTokenConfig;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
-import com.ruoyi.common.properties.TokenProperties;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.SwaggerProperties;
@@ -40,7 +40,7 @@ import java.util.List;
public class SwaggerConfig {
private final SwaggerProperties swaggerProperties;
- private final TokenProperties tokenProperties;
+ private final SaTokenConfig saTokenConfig;
private final OpenApiExtensionResolver openApiExtensionResolver;
/**
@@ -110,7 +110,7 @@ public class SwaggerConfig {
*/
private List securitySchemes() {
List apiKeyList = new ArrayList();
- String header = tokenProperties.getHeader();
+ String header = saTokenConfig.getTokenName();
apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue()));
return apiKeyList;
}
@@ -136,7 +136,7 @@ public class SwaggerConfig {
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List securityReferences = new ArrayList<>();
- securityReferences.add(new SecurityReference(tokenProperties.getHeader(), authorizationScopes));
+ securityReferences.add(new SecurityReference(saTokenConfig.getTokenName(), authorizationScopes));
return securityReferences;
}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java
index c83ffccbe..b37418181 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java
@@ -15,18 +15,9 @@ import org.springframework.stereotype.Component;
public class SecurityProperties {
/**
- * 退出登录url
+ * 排除路径
*/
- private String logoutUrl;
+ private String[] excludes;
- /**
- * 匿名放行路径
- */
- private String[] anonymous;
-
- /**
- * 用户任意访问放行路径
- */
- private String[] permitAll;
}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java
index 4c4f36c6b..87781eb59 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java
@@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.LoginUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
@@ -72,18 +72,18 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
}
}
- /**
- * 获取登录用户名
- */
- private String getLoginUsername() {
- LoginUser loginUser;
- try {
- loginUser = SecurityUtils.getLoginUser();
- } catch (Exception e) {
- log.warn("自动注入警告 => 用户未登录");
- return null;
- }
- return loginUser.getUsername();
- }
+ /**
+ * 获取登录用户名
+ */
+ private String getLoginUsername() {
+ LoginUser loginUser;
+ try {
+ loginUser = LoginUtils.getLoginUser();
+ } catch (Exception e) {
+ log.warn("自动注入警告 => 用户未登录");
+ return null;
+ }
+ return loginUser.getUsername();
+ }
}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java
index d5f4f5832..2318f6f4e 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java
@@ -13,7 +13,7 @@ import com.ruoyi.common.core.service.UserService;
import com.ruoyi.common.enums.DataScopeType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.DataPermissionHelper;
-import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.LoginUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
@@ -76,7 +76,7 @@ public class PlusDataPermissionHandler {
}
SysUser currentUser = DataPermissionHelper.getVariable("user");
if (ObjectUtil.isNull(currentUser)) {
- currentUser = SpringUtils.getBean(UserService.class).selectUserById(SecurityUtils.getUserId());
+ currentUser = SpringUtils.getBean(UserService.class).selectUserById(LoginUtils.getUserId());
DataPermissionHelper.setVariable("user", currentUser);
}
// 如果是超级管理员,则不过滤数据
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java
new file mode 100644
index 000000000..95837fe49
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java
@@ -0,0 +1,121 @@
+package com.ruoyi.framework.listener;
+
+import cn.dev33.satoken.config.SaTokenConfig;
+import cn.dev33.satoken.listener.SaTokenListener;
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.dto.UserOnlineDTO;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.service.UserService;
+import com.ruoyi.common.enums.UserType;
+import com.ruoyi.common.utils.LoginUtils;
+import com.ruoyi.common.utils.RedisUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.ip.AddressUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 用户行为 侦听器的实现
+ */
+@Component
+@Slf4j
+public class UserActionListener implements SaTokenListener {
+
+ @Autowired
+ private SaTokenConfig saTokenConfig;
+
+ /**
+ * 每次登录时触发
+ */
+ @Override
+ public void doLogin(String loginType, Object loginId, SaLoginModel loginModel) {
+ UserType userType = LoginUtils.getUserType(loginId);
+ if (userType == UserType.SYS_USER) {
+ UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
+ String ip = ServletUtils.getClientIP();
+ SysUser user = SpringUtils.getBean(UserService.class).selectUserById(LoginUtils.getUserId());
+ String tokenValue = StpUtil.getTokenValue();
+ UserOnlineDTO userOnlineDTO = new UserOnlineDTO()
+ .setIpaddr(ip)
+ .setLoginLocation(AddressUtils.getRealAddressByIP(ip))
+ .setBrowser(userAgent.getBrowser().getName())
+ .setOs(userAgent.getOs().getName())
+ .setLoginTime(System.currentTimeMillis())
+ .setTokenId(tokenValue)
+ .setUserName(user.getUserName());
+ if (StringUtils.isNotNull(user.getDept())) {
+ userOnlineDTO.setDeptName(user.getDept().getDeptName());
+ }
+ RedisUtils.setCacheObject(Constants.ONLINE_TOKEN_KEY + tokenValue, userOnlineDTO, saTokenConfig.getTimeout(), TimeUnit.SECONDS);
+ log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue);
+ } else if (userType == UserType.APP_USER) {
+ // app端 自行根据业务编写
+ }
+ }
+
+ /**
+ * 每次注销时触发
+ */
+ @Override
+ public void doLogout(String loginType, Object loginId, String tokenValue) {
+ RedisUtils.deleteObject(Constants.ONLINE_TOKEN_KEY + tokenValue);
+ log.info("user doLogout, useId:{}, token:{}", loginId, tokenValue);
+ }
+
+ /**
+ * 每次被踢下线时触发
+ */
+ @Override
+ public void doKickout(String loginType, Object loginId, String tokenValue) {
+ RedisUtils.deleteObject(Constants.ONLINE_TOKEN_KEY + tokenValue);
+ log.info("user doLogoutByLoginId, useId:{}, token:{}", loginId, tokenValue);
+ }
+
+ /**
+ * 每次被顶下线时触发
+ */
+ @Override
+ public void doReplaced(String loginType, Object loginId, String tokenValue) {
+ RedisUtils.deleteObject(Constants.ONLINE_TOKEN_KEY + tokenValue);
+ log.info("user doReplaced, useId:{}, token:{}", loginId, tokenValue);
+ }
+
+ /**
+ * 每次被封禁时触发
+ */
+ @Override
+ public void doDisable(String loginType, Object loginId, long disableTime) {
+ }
+
+ /**
+ * 每次被解封时触发
+ */
+ @Override
+ public void doUntieDisable(String loginType, Object loginId) {
+ }
+
+ /**
+ * 每次创建Session时触发
+ */
+ @Override
+ public void doCreateSession(String id) {
+ }
+
+ /**
+ * 每次注销Session时触发
+ */
+ @Override
+ public void doLogoutSession(String id) {
+ }
+
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java
new file mode 100644
index 000000000..556133c15
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java
@@ -0,0 +1,178 @@
+package com.ruoyi.framework.satoken.dao;
+
+import cn.dev33.satoken.dao.SaTokenDao;
+import cn.dev33.satoken.util.SaFoxUtil;
+import com.ruoyi.common.utils.RedisUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
+ *
+ * @author Lion Li
+ */
+@Component
+public class PlusSaTokenDao implements SaTokenDao {
+
+ /**
+ * 获取Value,如无返空
+ */
+ @Override
+ public String get(String key) {
+ return RedisUtils.getCacheObject(key);
+ }
+
+ /**
+ * 写入Value,并设定存活时间 (单位: 秒)
+ */
+ @Override
+ public void set(String key, String value, long timeout) {
+ if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
+ return;
+ }
+ // 判断是否为永不过期
+ if(timeout == SaTokenDao.NEVER_EXPIRE) {
+ RedisUtils.setCacheObject(key, value);
+ } else {
+ RedisUtils.setCacheObject(key, value, timeout, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * 修修改指定key-value键值对 (过期时间不变)
+ */
+ @Override
+ public void update(String key, String value) {
+ long expire = getTimeout(key);
+ // -2 = 无此键
+ if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
+ return;
+ }
+ this.set(key, value, expire);
+ }
+
+ /**
+ * 删除Value
+ */
+ @Override
+ public void delete(String key) {
+ RedisUtils.deleteObject(key);
+ }
+
+ /**
+ * 获取Value的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public long getTimeout(String key) {
+ return RedisUtils.getTimeToLive(key) / 1000;
+ }
+
+ /**
+ * 修改Value的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public void updateTimeout(String key, long timeout) {
+ // 判断是否想要设置为永久
+ if(timeout == SaTokenDao.NEVER_EXPIRE) {
+ long expire = getTimeout(key);
+ if(expire == SaTokenDao.NEVER_EXPIRE) {
+ // 如果其已经被设置为永久,则不作任何处理
+ } else {
+ // 如果尚未被设置为永久,那么再次set一次
+ this.set(key, this.get(key), timeout);
+ }
+ return;
+ }
+ RedisUtils.expire(key, timeout, TimeUnit.SECONDS);
+ }
+
+
+
+ /**
+ * 获取Object,如无返空
+ */
+ @Override
+ public Object getObject(String key) {
+ return RedisUtils.getCacheObject(key);
+ }
+
+ /**
+ * 写入Object,并设定存活时间 (单位: 秒)
+ */
+ @Override
+ public void setObject(String key, Object object, long timeout) {
+ if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
+ return;
+ }
+ // 判断是否为永不过期
+ if(timeout == SaTokenDao.NEVER_EXPIRE) {
+ RedisUtils.setCacheObject(key, object);
+ } else {
+ RedisUtils.setCacheObject(key, object, timeout, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * 更新Object (过期时间不变)
+ */
+ @Override
+ public void updateObject(String key, Object object) {
+ long expire = getObjectTimeout(key);
+ // -2 = 无此键
+ if(expire == SaTokenDao.NOT_VALUE_EXPIRE) {
+ return;
+ }
+ this.setObject(key, object, expire);
+ }
+
+ /**
+ * 删除Object
+ */
+ @Override
+ public void deleteObject(String key) {
+ RedisUtils.deleteObject(key);
+ }
+
+ /**
+ * 获取Object的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public long getObjectTimeout(String key) {
+ return RedisUtils.getTimeToLive(key) / 1000;
+ }
+
+ /**
+ * 修改Object的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public void updateObjectTimeout(String key, long timeout) {
+ // 判断是否想要设置为永久
+ if(timeout == SaTokenDao.NEVER_EXPIRE) {
+ long expire = getObjectTimeout(key);
+ if(expire == SaTokenDao.NEVER_EXPIRE) {
+ // 如果其已经被设置为永久,则不作任何处理
+ } else {
+ // 如果尚未被设置为永久,那么再次set一次
+ this.setObject(key, this.getObject(key), timeout);
+ }
+ return;
+ }
+ RedisUtils.expire(key, timeout, TimeUnit.SECONDS);
+ }
+
+
+ /**
+ * 搜索数据
+ */
+ @Override
+ public List searchData(String prefix, String keyword, int start, int size) {
+ Collection keys = RedisUtils.keys(prefix + "*" + keyword + "*");
+ List list = new ArrayList<>(keys);
+ return SaFoxUtil.searchList(list, start, size);
+ }
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/service/SaInterfaceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/service/SaInterfaceImpl.java
new file mode 100644
index 000000000..e5bacd342
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/service/SaInterfaceImpl.java
@@ -0,0 +1,38 @@
+package com.ruoyi.framework.satoken.service;
+
+import cn.dev33.satoken.stp.StpInterface;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.enums.UserType;
+import com.ruoyi.common.utils.LoginUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class SaInterfaceImpl implements StpInterface {
+
+ @Override
+ public List getPermissionList(Object loginId, String loginType) {
+ UserType userType = LoginUtils.getUserType(loginId);
+ if (userType == UserType.SYS_USER) {
+ LoginUser loginUser = LoginUtils.getLoginUser();
+ return new ArrayList<>(loginUser.getMenuPermission());
+ } else if (userType == UserType.APP_USER) {
+ // app端权限返回 自行根据业务编写
+ }
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List getRoleList(Object loginId, String loginType) {
+ UserType userType = LoginUtils.getUserType(loginId);
+ if (userType == UserType.SYS_USER) {
+ LoginUser loginUser = LoginUtils.getLoginUser();
+ return new ArrayList<>(loginUser.getRolePermission());
+ } else if (userType == UserType.APP_USER) {
+ // app端权限返回 自行根据业务编写
+ }
+ return new ArrayList<>();
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java
deleted file mode 100644
index 4ebb4aaea..000000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.ruoyi.framework.security.filter;
-
-import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.core.service.TokenService;
-import com.ruoyi.common.utils.SecurityUtils;
-import com.ruoyi.common.utils.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.stereotype.Component;
-import org.springframework.web.filter.OncePerRequestFilter;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * token过滤器 验证token有效性
- *
- * @author ruoyi
- */
-@Component
-public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
-
- @Autowired
- private TokenService tokenService;
-
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
- throws ServletException, IOException {
- LoginUser loginUser = tokenService.getLoginUser(request);
- if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
- tokenService.verifyToken(loginUser);
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
- authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(authenticationToken);
- }
- chain.doFilter(request, response);
- }
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java
deleted file mode 100644
index 35b0f921c..000000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.ruoyi.framework.security.handle;
-
-import cn.hutool.http.HttpStatus;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.utils.JsonUtils;
-import com.ruoyi.common.utils.ServletUtils;
-import com.ruoyi.common.utils.StringUtils;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.stereotype.Component;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.Serializable;
-
-/**
- * 认证失败处理类 返回未授权
- *
- * @author ruoyi
- */
-@Component
-public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {
- private static final long serialVersionUID = -8970718410437077606L;
-
- @Override
- public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
- throws IOException {
- int code = HttpStatus.HTTP_UNAUTHORIZED;
- String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
- ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(code, msg)));
- }
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
deleted file mode 100644
index 969af512c..000000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.ruoyi.framework.security.handle;
-
-import cn.hutool.http.HttpStatus;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.core.service.LogininforService;
-import com.ruoyi.common.core.service.TokenService;
-import com.ruoyi.common.utils.JsonUtils;
-import com.ruoyi.common.utils.MessageUtils;
-import com.ruoyi.common.utils.ServletUtils;
-import com.ruoyi.common.utils.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * 自定义退出处理类 返回成功
- *
- * @author ruoyi
- */
-@Configuration
-public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
-
- @Autowired
- private TokenService tokenService;
-
- @Autowired
- private LogininforService asyncService;
-
- /**
- * 退出处理
- */
- @Override
- public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
- throws IOException, ServletException {
- LoginUser loginUser = tokenService.getLoginUser(request);
- String message = MessageUtils.message("user.logout.success");
- if (StringUtils.isNotNull(loginUser)) {
- String userName = loginUser.getUsername();
- // 删除用户缓存记录
- tokenService.delLoginUser(loginUser.getToken());
- // 记录用户退出日志
- asyncService.recordLogininfor(userName, Constants.LOGOUT, message, request);
- }
- ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(HttpStatus.HTTP_OK, message)));
- }
-
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
index 4c642c2d7..60979dd0e 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
@@ -1,13 +1,18 @@
package com.ruoyi.framework.web.exception;
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.exception.NotPermissionException;
+import cn.dev33.satoken.exception.NotRoleException;
+import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.http.HttpStatus;
+import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
-import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -31,13 +36,33 @@ public class GlobalExceptionHandler {
/**
* 权限校验异常
*/
- @ExceptionHandler(AccessDeniedException.class)
- public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
+ @ExceptionHandler(NotPermissionException.class)
+ public AjaxResult handleAccessDeniedException(NotPermissionException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
return AjaxResult.error(HttpStatus.HTTP_FORBIDDEN, "没有权限,请联系管理员授权");
}
+ /**
+ * 角色校验异常
+ */
+ @ExceptionHandler(NotRoleException.class)
+ public AjaxResult handleAccessDeniedException(NotRoleException e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',角色校验失败'{}'", requestURI, e.getMessage());
+ return AjaxResult.error(HttpStatus.HTTP_FORBIDDEN, "没有角色,请联系管理员授权");
+ }
+
+ /**
+ * 认证失败
+ */
+ @ExceptionHandler(NotLoginException.class)
+ public AjaxResult handleAccessDeniedException(NotLoginException e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
+ return AjaxResult.error(HttpStatus.HTTP_UNAUTHORIZED, StringUtils.format("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI));
+ }
+
/**
* 请求方式不支持
*/
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
deleted file mode 100644
index 0e97b92f0..000000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.ruoyi.framework.web.service;
-
-import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.utils.SecurityUtils;
-import com.ruoyi.common.utils.StringUtils;
-import org.springframework.stereotype.Service;
-import org.springframework.util.CollectionUtils;
-
-import java.util.Set;
-
-/**
- * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母
- *
- * @author ruoyi
- */
-@Service("ss")
-public class PermissionService {
- /**
- * 所有权限标识
- */
- private static final String ALL_PERMISSION = "*:*:*";
-
- /**
- * 管理员角色权限标识
- */
- private static final String SUPER_ADMIN = "admin";
-
- private static final String ROLE_DELIMETER = ",";
-
- private static final String PERMISSION_DELIMETER = ",";
-
- /**
- * 验证用户是否具备某权限
- *
- * @param permission 权限字符串
- * @return 用户是否具备某权限
- */
- public boolean hasPermi(String permission) {
- if (StringUtils.isEmpty(permission)) {
- return false;
- }
- LoginUser loginUser = SecurityUtils.getLoginUser();
- if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getMenuPermissions())) {
- return false;
- }
- return hasPermissions(loginUser.getMenuPermissions(), 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 = SecurityUtils.getLoginUser();
- if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getMenuPermissions())) {
- return false;
- }
- Set authorities = loginUser.getMenuPermissions();
- for (String permission : permissions.split(PERMISSION_DELIMETER)) {
- if (permission != null && hasPermissions(authorities, permission)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * 判断用户是否拥有某个角色
- *
- * @param role 角色字符串
- * @return 用户是否具备某角色
- */
- public boolean hasRole(String role) {
- if (StringUtils.isEmpty(role)) {
- return false;
- }
- LoginUser loginUser = SecurityUtils.getLoginUser();
- if (StringUtils.isNull(loginUser)) {
- return false;
- }
- Set rolePermissions = loginUser.getRolePermissions();
- if (CollectionUtils.isEmpty(rolePermissions)) {
- return false;
- }
- for (String roleKey : rolePermissions) {
- if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * 验证用户是否不具备某角色,与 isRole逻辑相反。
- *
- * @param role 角色名称
- * @return 用户是否不具备某角色
- */
- public boolean lacksRole(String role) {
- return hasRole(role) != true;
- }
-
- /**
- * 验证用户是否具有以下任意一个角色
- *
- * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
- * @return 用户是否具有以下任意一个角色
- */
- public boolean hasAnyRoles(String roles) {
- if (StringUtils.isEmpty(roles)) {
- return false;
- }
- LoginUser loginUser = SecurityUtils.getLoginUser();
- if (StringUtils.isNull(loginUser)) {
- return false;
- }
- Set rolePermissions = loginUser.getRolePermissions();
- if (CollectionUtils.isEmpty(rolePermissions)) {
- return false;
- }
- for (String role : roles.split(ROLE_DELIMETER)) {
- for (String roleKey : rolePermissions) {
- if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * 判断是否包含权限
- *
- * @param permissions 权限列表
- * @param permission 权限字符串
- * @return 用户是否具备某权限
- */
- private boolean hasPermissions(Set permissions, String permission) {
- return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
- }
-}
diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java
index 685a5bbc0..f1ff61a36 100644
--- a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java
+++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java
@@ -1,5 +1,6 @@
package com.ruoyi.generator.controller;
+import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import com.ruoyi.common.annotation.Log;
@@ -16,7 +17,6 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -45,7 +45,7 @@ public class GenController extends BaseController {
* 查询代码生成列表
*/
@ApiOperation("查询代码生成列表")
- @PreAuthorize("@ss.hasPermi('tool:gen:list')")
+ @SaCheckPermission("tool:gen:list")
@GetMapping("/list")
public TableDataInfo genList(GenTable genTable, PageQuery pageQuery) {
return genTableService.selectPageGenTableList(genTable, pageQuery);
@@ -55,7 +55,7 @@ public class GenController extends BaseController {
* 修改代码生成业务
*/
@ApiOperation("修改代码生成业务")
- @PreAuthorize("@ss.hasPermi('tool:gen:query')")
+ @SaCheckPermission("tool:gen:query")
@GetMapping(value = "/{talbleId}")
public AjaxResult