Browse Source

feat(login): 添加浏览器关闭时自动退出登录功能

- 在Shiro配置中添加/sys/logout路径为匿名访问
- 修改登录逻辑,移除多地点登录的复杂验证流程
- 实现支持两种退出方式的logout方法:正常退出和浏览器关闭时的退出
- 添加根据token查询用户信息的方法queryByToken
- 修改退出逻辑,改为直接删除token记录而非生成新token
- 实现幂等性退出,确保各种情况下都能正常退出登录
master
常熟吴彦祖 5 days ago
parent
commit
379d77f9c5
  1. 1
      src/main/java/com/gaotao/config/ShiroConfig.java
  2. 62
      src/main/java/com/gaotao/modules/sys/controller/SysLoginController.java
  3. 9
      src/main/java/com/gaotao/modules/sys/service/SysUserTokenService.java
  4. 18
      src/main/java/com/gaotao/modules/sys/service/impl/SysUserTokenServiceImpl.java

1
src/main/java/com/gaotao/config/ShiroConfig.java

@ -46,6 +46,7 @@ public class ShiroConfig {
filterMap.put("/druid/**", "anon");
filterMap.put("/app/**", "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/sys/logout", "anon");//退出登录支持浏览器关闭时自动退出 - rqrq
filterMap.put("/sysLanguageList/searchSysLanguage", "anon");//登录页面切换语言
filterMap.put("/accessSite/getUserAuthorizedSites", "anon");//登录页选择工厂
filterMap.put("/api/wms/**", "anon");//wcsrcs反馈信息

62
src/main/java/com/gaotao/modules/sys/controller/SysLoginController.java

@ -107,32 +107,16 @@ public class SysLoginController extends AbstractController {
return R.error(getLanguageMsg(SysMsgConstant.OBJECT_ID_200207));
}
//账号锁定
if(user.getStatus() == 0){
return R.error(getLanguageMsg(SysMsgConstant.OBJECT_ID_200208));
//账号锁定
if(user.getStatus() == 0){
return R.error(getLanguageMsg(SysMsgConstant.OBJECT_ID_200208));
}
}
}
// 检查用户是否已登录超级管理员跳过检查- rqrq
if(user.getUserId() != Constant.SUPER_ADMIN && sysUserTokenService.isUserLoggedIn(user.getUserId())){
// 检查请求中是否携带token - rqrq
String oldToken = request.getHeader("token");
if(oldToken != null && !oldToken.isEmpty()) {
// 验证前端token是否与数据库中的token一致 - rqrq
SysUserTokenEntity tokenEntity = sysUserTokenService.getById(user.getUserId());
if(tokenEntity != null && tokenEntity.getToken().equals(oldToken)) {
// Token一致说明是真的多地登录 - rqrq
return R.error("用户已经登录,请先退出后再登录");
}
}
// 以下情况允许登录
// 1. 前端没有tokenCookie丢失或浏览器关闭后重新打开
// 2. 前端token与数据库不一致可能是旧token
// 自动清理旧token允许重新登录 - rqrq
sysUserTokenService.forceLogout(user.getUserId());
return R.error("用户已经登录,请先退出后再登录");
}
session.setAttribute("user", user);
session.setAttribute("user", user);
//生成token并保存到数据库
R r = sysUserTokenService.createToken(user.getUserId());
@ -142,12 +126,38 @@ public class SysLoginController extends AbstractController {
/**
* 退出
* 退出登录 - rqrq
* 支持两种方式
* 1. 正常退出从session获取userId
* 2. 浏览器关闭从请求体token获取userId
*/
@PostMapping("/sys/logout")
public R logout() {
sysUserTokenService.logout(getUserId());
return R.ok(getLanguageMsg(SysMsgConstant.OBJECT_ID_200000));
public R logout(@RequestBody(required = false) Map<String, Object> params) {
Long userId = null;
try {
// 1. 优先从session获取userId正常点击退出按钮- rqrq
userId = getUserId();
} catch (Exception e) {
// 2. session获取失败从请求体获取token浏览器关闭时- rqrq
if (params != null && params.containsKey("token")) {
String token = (String) params.get("token");
if (token != null && !token.isEmpty()) {
SysUserTokenEntity tokenEntity = sysUserTokenService.queryByToken(token);
if (tokenEntity != null) {
userId = tokenEntity.getUserId();
}
}
}
}
if (userId != null) {
sysUserTokenService.logout(userId);
return R.ok(getLanguageMsg(SysMsgConstant.OBJECT_ID_200000));
} else {
// 如果获取不到userId也返回成功幂等性- rqrq
return R.ok("退出成功");
}
}
}

9
src/main/java/com/gaotao/modules/sys/service/SysUserTokenService.java

@ -40,4 +40,13 @@ public interface SysUserTokenService extends IService<SysUserTokenEntity> {
*/
void forceLogout(long userId);
/**
* @Description 根据token查询用户token信息 - rqrq
* @param token token字符串
* @return SysUserTokenEntity
* @author rqrq
* @date 2026/02/27
*/
SysUserTokenEntity queryByToken(String token);
}

18
src/main/java/com/gaotao/modules/sys/service/impl/SysUserTokenServiceImpl.java

@ -57,14 +57,8 @@ public class SysUserTokenServiceImpl extends ServiceImpl<SysUserTokenDao, SysUse
@Override
public void logout(long userId) {
//生成一个token
String token = TokenGenerator.generateValue();
//修改token
SysUserTokenEntity tokenEntity = new SysUserTokenEntity();
tokenEntity.setUserId(userId);
tokenEntity.setToken(token);
this.updateById(tokenEntity);
// 退出登录直接删除token记录 - rqrq
this.removeById(userId);
}
@Override
@ -93,4 +87,12 @@ public class SysUserTokenServiceImpl extends ServiceImpl<SysUserTokenDao, SysUse
this.removeById(userId);
}
}
@Override
public SysUserTokenEntity queryByToken(String token) {
// 根据token查询用户token信息 - rqrq
return this.lambdaQuery()
.eq(SysUserTokenEntity::getToken, token)
.one();
}
}
Loading…
Cancel
Save