【sa-token】登录和退出换写法

This commit is contained in:
JEECG
2025-10-16 11:06:47 +08:00
parent c85bb1f62d
commit 468af57489
6 changed files with 119 additions and 110 deletions

View File

@ -7,8 +7,8 @@ import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.LoginUserUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.cas.util.CasServiceUtil;
import org.jeecg.modules.cas.util.XmlUtils;
@ -16,6 +16,7 @@ import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysDepartService;
import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
@ -78,10 +79,10 @@ public class CasClientController {
if(!result.isSuccess()) {
return result;
}
String token = JwtUtil.sign(sysUser.getUsername(), sysUser.getPassword());
// 设置超时时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000);
// 使用Sa-Token生成token使用username作为loginId
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
String token = LoginUserUtils.doLogin(loginUser);
//获取用户部门信息
JSONObject obj = new JSONObject();

View File

@ -6,10 +6,12 @@ import org.apache.commons.lang3.ObjectUtils;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.LoginUserUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.BeanUtils;
import org.jeecg.config.StaticConfig;
import org.jeecg.modules.message.entity.SysMessage;
import org.jeecg.modules.message.handle.ISendMsgHandle;
@ -240,12 +242,10 @@ public class EmailSendMsgHandle implements ISendMsgHandle {
* @return
*/
private String getToken(SysUser user) {
// 生成token
String token = JwtUtil.sign(user.getUsername(), user.getPassword());
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
// 设置超时时间 1个小时
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 1 / 1000);
return token;
// 使用封装方法:一步完成登录和设置用户信息
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(user, loginUser);
return LoginUserUtils.doLogin(loginUser);
}
/**

View File

@ -7,11 +7,13 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import jakarta.servlet.http.HttpServletRequest;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.LoginUserUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.RestUtil;
import org.jeecg.modules.openapi.entity.OpenApi;
@ -24,6 +26,7 @@ import org.jeecg.modules.openapi.service.OpenApiService;
import org.jeecg.modules.openapi.swagger.*;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
@ -33,7 +36,6 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import jakarta.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
@ -163,7 +165,7 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
String appkey = request.getHeader("appkey");
OpenApiAuth openApiAuth = openApiAuthService.getByAppkey(appkey);
SysUser systemUser = sysUserService.getUserByName(openApiAuth.getCreateBy());
String token = this.getToken(systemUser.getUsername(), systemUser.getPassword());
String token = this.getToken(systemUser);
httpHeaders.put("X-Access-Token", Lists.newArrayList(token));
httpHeaders.put("Content-Type",Lists.newArrayList("application/json"));
HttpEntity<String> httpEntity = new HttpEntity<>(json, httpHeaders);
@ -202,15 +204,13 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
/**
* 生成接口访问令牌 Token
*
* @param USERNAME
* @param PASSWORD
* @return
*/
private String getToken(String USERNAME, String PASSWORD) {
String token = JwtUtil.sign(USERNAME, PASSWORD);
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, 60);
return token;
private String getToken(SysUser user) {
// 使用封装方法:一步完成登录和设置用户信息
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(user, loginUser);
return LoginUserUtils.doLogin(loginUser);
}

View File

@ -1,5 +1,6 @@
package org.jeecg.modules.system.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
@ -8,8 +9,9 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.jeecg.common.util.LoginUserUtils;
import cn.dev33.satoken.annotation.SaCheckRole;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
@ -23,7 +25,6 @@ import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysRoleIndex;
import org.jeecg.modules.system.entity.SysTenant;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.model.SysLoginModel;
import org.jeecg.modules.system.service.*;
@ -31,7 +32,6 @@ import org.jeecg.modules.system.service.impl.SysBaseApiImpl;
import org.jeecg.modules.system.util.RandImageUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@ -42,7 +42,6 @@ import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @Author scott
@ -75,7 +74,7 @@ public class LoginController {
/**
* 线程池用于异步发送纪要
*/
public static ExecutorService cachedThreadPool = new ShiroThreadPoolExecutor(0, 1024, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
public static ExecutorService cachedThreadPool = new ThreadPoolExecutor(0, 1024, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
@Operation(summary="登录接口")
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ -149,7 +148,8 @@ public class LoginController {
public Result<JSONObject> getUserInfo(HttpServletRequest request){
long start = System.currentTimeMillis();
Result<JSONObject> result = new Result<JSONObject>();
String username = JwtUtil.getUserNameByToken(request);
// 使用Sa-Token获取登录用户名loginId现在是username
String username = StpUtil.getLoginIdAsString();
if(oConvertUtils.isNotEmpty(username)) {
// 根据用户名查询用户信息
SysUser sysUser = sysUserService.getUserByName(username);
@ -200,7 +200,8 @@ public class LoginController {
LoginUser sysUser = sysBaseApi.getUserByName(username);
if(sysUser!=null) {
asyncClearLogoutCache(token, sysUser); // 异步清理
SecurityUtils.getSubject().logout();
// 使用Sa-Token退出登录
StpUtil.logout();
return Result.ok("退出登录成功!");
}else {
return Result.error("Token无效!");
@ -208,17 +209,13 @@ public class LoginController {
}
/**
* 清理用户缓存
* 清理用户缓存使用Sa-Token
*
* @param token
* @param sysUser
*/
private void asyncClearLogoutCache(String token, LoginUser sysUser) {
cachedThreadPool.execute(()->{
//清空用户登录Token缓存
redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token);
//清空用户登录Shiro权限缓存
redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
//清空用户的缓存信息包括部门信息例如sys:cache:user::<username>
redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
baseCommonService.addLog("用户名: "+sysUser.getRealname()+",退出成功!", CommonConstant.LOG_TYPE_1, null, sysUser);
@ -290,7 +287,7 @@ public class LoginController {
Result<JSONObject> result = new Result<JSONObject>();
String username = user.getUsername();
if(oConvertUtils.isEmpty(username)) {
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
LoginUser sysUser = LoginUserUtils.getSessionUser();
username = sysUser.getUsername();
}
@ -478,15 +475,13 @@ public class LoginController {
*/
private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result, HttpServletRequest request) {
String username = sysUser.getUsername();
String syspassword = sysUser.getPassword();
// 获取用户部门信息
JSONObject obj = new JSONObject(new LinkedHashMap<>());
//1.生成token
String token = JwtUtil.sign(username, syspassword);
// 设置token缓存有效时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
//1.使用sa-token生成token使用username作为loginId将用户信息存入session
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
String token = LoginUserUtils.doLogin(loginUser);
obj.put("token", token);
//2.设置登录租户
@ -584,7 +579,7 @@ public class LoginController {
/**
* 切换菜单表为vue3的表
*/
@RequiresRoles({"admin"})
@SaCheckRole({"admin"})
@GetMapping(value = "/switchVue3Menu")
public Result<String> switchVue3Menu(HttpServletResponse response) {
Result<String> res = new Result<String>();
@ -656,11 +651,11 @@ public class LoginController {
//5. 设置登录用户信息
obj.put("userInfo", sysUser);
//6. 生成token
String token = JwtUtil.sign(username, syspassword);
// 设置超时时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000);
//6. 使用Sa-Token生成token使用username作为loginId、将用户信息存入session
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
String token = LoginUserUtils.doLogin(loginUser);
log.info("App登录成功用户名{}token={}", username, token);
//token 信息
obj.put("token", token);
@ -792,7 +787,7 @@ public class LoginController {
result.setSuccess(false);
return result;
}
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
LoginUser sysUser = LoginUserUtils.getSessionUser();
String username = sysUser.getUsername();
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<>();
query.eq(SysUser::getUsername, username).eq(SysUser::getPhone, mobile);

View File

@ -1,28 +1,23 @@
package org.jeecg.modules.system.controller;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.service.impl.SysBaseApiImpl;
import org.jeecg.modules.system.vo.SysUserOnlineVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -39,43 +34,58 @@ public class SysUserOnlineController {
@Autowired
private RedisUtil redisUtil;
@Autowired
public RedisTemplate redisTemplate;
@Autowired
public ISysUserService userService;
@Autowired
private SysBaseApiImpl sysBaseApi;
@Resource
private BaseCommonService baseCommonService;
/**
* 获取在线用户列表使用Sa-Token
*/
@RequestMapping(value = "/list", method = RequestMethod.GET)
public Result<Page<SysUserOnlineVO>> list(@RequestParam(name="username", required=false) String username,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,@RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
Collection<String> keys = redisUtil.scan(CommonConstant.PREFIX_USER_TOKEN + "*");
List<SysUserOnlineVO> onlineList = new ArrayList<SysUserOnlineVO>();
for (String key : keys) {
String token = (String)redisUtil.get(key);
if (StringUtils.isNotEmpty(token)) {
SysUserOnlineVO online = new SysUserOnlineVO();
online.setToken(token);
//TODO 改成一次性查询
LoginUser loginUser = sysBaseApi.getUserByName(JwtUtil.getUsername(token));
if (loginUser != null && !"_reserve_user_external".equals(loginUser.getUsername())) {
//update-begin---author:wangshuai ---date:20220104 for[JTC-382]在线用户查询无效------------
//验证用户名是否与传过来的用户名相同
boolean isMatchUsername=true;
//判断用户名是否为空并且当前循环的用户不包含传过来的用户名那么就设成false
if(oConvertUtils.isNotEmpty(username) && !loginUser.getUsername().contains(username)){
isMatchUsername = false;
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
List<SysUserOnlineVO> onlineList = new ArrayList<>();
try {
// 使用Sa-Token获取所有在线用户的session ID列表
List<String> sessionIdList = StpUtil.searchSessionId("", 0, -1, false);
for (String sessionId : sessionIdList) {
try {
// 获取session
SaSession session = StpUtil.getSessionBySessionId(sessionId);
if (session == null) {
continue;
}
if(isMatchUsername){
BeanUtils.copyProperties(loginUser, online);
onlineList.add(online);
// 从session中获取用户信息
LoginUser loginUser = (LoginUser) session.get("loginUser");
if (loginUser != null && !"_reserve_user_external".equals(loginUser.getUsername())) {
// 用户名筛选
if (oConvertUtils.isEmpty(username) || loginUser.getUsername().contains(username)) {
SysUserOnlineVO online = new SysUserOnlineVO();
BeanUtils.copyProperties(loginUser, online);
// 获取该用户的tokenloginId现在是username
try {
String token = StpUtil.getTokenValueByLoginId(loginUser.getUsername());
online.setToken(token);
} catch (Exception e) {
log.debug("获取用户token失败: {}", e.getMessage());
}
onlineList.add(online);
}
}
//update-end---author:wangshuai ---date:20220104 for[JTC-382]在线用户查询无效------------
} catch (Exception e) {
// 旧的Session数据可能导致反序列化失败记录debug级别日志即可
log.debug("获取session失败可能是旧数据: {}", e.getMessage());
}
}
} catch (Exception e) {
log.error("获取在线用户列表失败: {}", e.getMessage(), e);
}
Collections.reverse(onlineList);
Page<SysUserOnlineVO> page = new Page<SysUserOnlineVO>(pageNo, pageSize);
@ -100,30 +110,33 @@ public class SysUserOnlineController {
}
/**
* 强退用户
* 强退用户使用Sa-Token
*/
@RequestMapping(value = "/forceLogout",method = RequestMethod.POST)
public Result<Object> forceLogout(@RequestBody SysUserOnlineVO online) {
//用户退出逻辑
if(oConvertUtils.isEmpty(online.getToken())) {
return Result.error("退出登录失败!");
}
String username = JwtUtil.getUsername(online.getToken());
LoginUser sysUser = sysBaseApi.getUserByName(username);
if(sysUser!=null) {
baseCommonService.addLog("强制: "+sysUser.getRealname()+"退出成功!", CommonConstant.LOG_TYPE_1, null,sysUser);
log.info(" 强制 "+sysUser.getRealname()+"退出成功! ");
//清空用户登录Token缓存
redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + online.getToken());
//清空用户登录Shiro权限缓存
redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
//清空用户的缓存信息包括部门信息例如sys:cache:user::<username>
redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
//调用shiro的logout
SecurityUtils.getSubject().logout();
try {
// 验证参数
if (oConvertUtils.isEmpty(online.getToken())) {
return Result.error("Token不能为空");
}
// 使用Sa-Token通过token强制退出登录
StpUtil.logoutByTokenValue(online.getToken());
// 清空用户的缓存信息(如果有用户名)
if (oConvertUtils.isNotEmpty(online.getUsername())) {
redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, online.getUsername()));
}
// 记录日志
String username = oConvertUtils.isNotEmpty(online.getUsername()) ? online.getUsername() : "未知用户";
baseCommonService.addLog("强制: " + username + " 退出成功!", CommonConstant.LOG_TYPE_1, null);
log.info("强制 {} 退出成功!", username);
return Result.ok("退出登录成功!");
}else {
return Result.error("Token无效!");
} catch (Exception e) {
log.error("强制退出失败: {}", e.getMessage(), e);
return Result.error("退出登录失败:" + e.getMessage());
}
}
}

View File

@ -17,7 +17,9 @@ import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.*;
import org.springframework.beans.BeanUtils;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysThirdAccount;
@ -211,12 +213,10 @@ public class ThirdLoginController {
}
private String saveToken(SysUser user) {
// 生成token
String token = JwtUtil.sign(user.getUsername(), user.getPassword());
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
// 设置超时时间
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
return token;
// 使用封装方法:一步完成登录和设置用户信息
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(user, loginUser);
return LoginUserUtils.doLogin(loginUser);
}
/**