sa-token update

This commit is contained in:
lixiaodong 2023-07-21 08:32:56 +08:00
parent ac6921a278
commit 7de2627fee
20 changed files with 317 additions and 176 deletions

View File

@ -125,13 +125,6 @@
<version>${sa-token-version}</version> <version>${sa-token-version}</version>
</dependency> </dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>1.35.0.RC</version>
</dependency>
<!-- sa-token end --> <!-- sa-token end -->
<dependency> <dependency>

View File

@ -3,10 +3,13 @@ package net.lab1024.sa.admin.config;
import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import net.lab1024.sa.common.config.UrlConfig; import net.lab1024.sa.admin.module.system.menu.service.MenuCacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.List;
/** /**
* sa-token 配置 * sa-token 配置
* *
@ -16,6 +19,9 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class SaTokenConfig { public class SaTokenConfig {
@Autowired
private MenuCacheService menuService;
/** /**
* 定义 Sa-Token 拦截器定义详细认证规则 * 定义 Sa-Token 拦截器定义详细认证规则
*/ */
@ -23,12 +29,9 @@ public class SaTokenConfig {
public SaInterceptor saInterceptor() { public SaInterceptor saInterceptor() {
// 关闭注解鉴权 只做路由拦截校验 // 关闭注解鉴权 只做路由拦截校验
return new SaInterceptor(handler -> { return new SaInterceptor(handler -> {
/** // 查询数据表中 需要校验权限的url
* 每个路由 都做为一个权限点 List<String> urlList = menuService.queryNeedCheckPermissionsUrl();
* TODO listen from定义的api urlList.forEach(url -> SaRouter.match(url, r -> StpUtil.checkPermission(url)));
* from menu数据表已选择的api
*/
UrlConfig.AUTH_URL_LIST.forEach(url -> SaRouter.match(url, r -> StpUtil.checkPermission(url)));
}).isAnnotation(false); }).isAnnotation(false);
} }
} }

View File

@ -1,13 +1,9 @@
package net.lab1024.sa.admin.interceptor; package net.lab1024.sa.admin.interceptor;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.NumberWithFormat;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.lab1024.sa.common.common.domain.RequestUser; import net.lab1024.sa.common.common.domain.RequestUser;
import net.lab1024.sa.common.common.enumeration.UserTypeEnum; import net.lab1024.sa.common.common.enumeration.UserTypeEnum;
import net.lab1024.sa.common.common.interceptor.AbstractInterceptor; import net.lab1024.sa.common.common.interceptor.AbstractInterceptor;
import net.lab1024.sa.common.common.util.SmartEnumUtil;
import net.lab1024.sa.common.module.support.token.TokenService;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -23,6 +19,24 @@ import java.util.List;
@Configuration @Configuration
public class AdminInterceptor extends AbstractInterceptor { public class AdminInterceptor extends AbstractInterceptor {
/**
* 此处可根据需要
* 自行查询用户信息
*/
@Override
public RequestUser getDevUser(Long userId) {
RequestUser requestUser = new RequestUser();
requestUser.setUserId(userId);
requestUser.setUserName("dev");
requestUser.setUserType(this.getUserType());
return requestUser;
}
@Override
public UserTypeEnum getUserType() {
return UserTypeEnum.ADMIN_EMPLOYEE;
}
/** /**
* 配置拦截路径 * 配置拦截路径
* *
@ -33,27 +47,16 @@ public class AdminInterceptor extends AbstractInterceptor {
return Lists.newArrayList("/**"); return Lists.newArrayList("/**");
} }
@Override /**
public void checkSaToken() { * 如果没有需要处理的业务
StpUtil.checkLogin(); * 那就没有必要重写了 可以删除这个方法
} *
* @param request
@Override * @param response
public RequestUser getRequestUser() { * @param handler
// 获取额外数据 * @return
Integer userType = ((NumberWithFormat) StpUtil.getExtra(TokenService.EXTRA_KEY_USER_TYPE)).intValue(); * @throws Exception
UserTypeEnum userTypeEnum = SmartEnumUtil.getEnumByValue(userType, UserTypeEnum.class); */
String userName = (String) StpUtil.getExtra(TokenService.EXTRA_KEY_USER_NAME);
String loginId = (String) StpUtil.getLoginId();
// 当前请求对象
RequestUser requestUser = new RequestUser();
requestUser.setUserId(TokenService.getUserId(loginId));
requestUser.setUserName(userName);
requestUser.setUserType(userTypeEnum);
return requestUser;
}
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean isHandle = super.preHandle(request, response, handler); boolean isHandle = super.preHandle(request, response, handler);

View File

@ -1,17 +1,24 @@
package net.lab1024.sa.admin.module.system.employee.service; package net.lab1024.sa.admin.module.system.employee.service;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
import net.lab1024.sa.admin.module.system.employee.dao.EmployeeDao; import cn.dev33.satoken.stp.StpUtil;
import net.lab1024.sa.admin.module.system.employee.domain.entity.EmployeeEntity; import com.google.common.collect.Lists;
import net.lab1024.sa.admin.module.system.menu.constant.MenuTypeEnum; import net.lab1024.sa.admin.module.system.menu.constant.MenuTypeEnum;
import net.lab1024.sa.admin.module.system.menu.domain.entity.MenuEntity;
import net.lab1024.sa.admin.module.system.menu.domain.vo.MenuVO; import net.lab1024.sa.admin.module.system.menu.domain.vo.MenuVO;
import net.lab1024.sa.admin.module.system.role.dao.RoleMenuDao;
import net.lab1024.sa.admin.module.system.role.service.RoleEmployeeService; import net.lab1024.sa.admin.module.system.role.service.RoleEmployeeService;
import net.lab1024.sa.admin.module.system.role.service.RoleMenuService; import net.lab1024.sa.admin.module.system.role.service.RoleMenuService;
import net.lab1024.sa.common.common.enumeration.UserTypeEnum;
import net.lab1024.sa.common.module.support.token.TokenService; import net.lab1024.sa.common.module.support.token.TokenService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -33,7 +40,22 @@ public class EmployeePermissionService implements StpInterface {
private RoleMenuService roleMenuService; private RoleMenuService roleMenuService;
@Autowired @Autowired
private EmployeeDao employeeDao; private RoleMenuDao roleMenuDao;
/**
* 员工关联权限 缓存key
*/
private static final String USER_ROLE_CACHE_KEY = "RoleList";
/**
* 角色关联功能点 缓存key
*/
private static final String ROLE_CACHE_KEY = "role:";
/**
* 角色关联功能点 缓存key
*/
private static final String ROLE_PERMISSION_CACHE_KEY = "PermissionList";
/** /**
* 查询用户拥有的前端菜单项 用于登陆返回 前端动态路由配置 * 查询用户拥有的前端菜单项 用于登陆返回 前端动态路由配置
@ -48,18 +70,61 @@ public class EmployeePermissionService implements StpInterface {
@Override @Override
public List<String> getPermissionList(Object loginId, String loginType) { public List<String> getPermissionList(Object loginId, String loginType) {
Long employeeId = TokenService.getUserId((String) loginId); // 权限集合
List<String> permissionList = new ArrayList<>();
// TODO listen 待做权限缓存 // 遍历角色列表查询拥有的权限
EmployeeEntity employeeEntity = employeeDao.selectById(employeeId); List<String> roleList = this.getRoleList(loginId, loginType);
List<MenuVO> menuList = this.getEmployeeMenuAndPointsList(employeeId, employeeEntity.getAdministratorFlag()); for (String roleId : roleList) {
return menuList.stream() // 查询缓存
.filter(e -> MenuTypeEnum.POINTS.equalsValue(e.getMenuType())) SaSession roleSession = SaSessionCustomUtil.getSessionById(ROLE_CACHE_KEY + roleId);
.map(MenuVO::getApiPerms).distinct().collect(Collectors.toList()); List<String> list = roleSession.get(ROLE_PERMISSION_CACHE_KEY, () -> {
// 从数据库查询这个角色所拥有的权限列表
return roleMenuDao.selectMenuListByRoleIdList(Lists.newArrayList(Long.parseLong(roleId)), false)
.stream()
.filter(e -> MenuTypeEnum.POINTS.equalsValue(e.getMenuType()))
.map(MenuEntity::getApiPerms).filter(Objects::nonNull).distinct()
.collect(Collectors.toList());
});
permissionList.addAll(list);
}
// 返回权限集合
return permissionList;
} }
@Override @Override
public List<String> getRoleList(Object loginId, String loginType) { public List<String> getRoleList(Object loginId, String loginType) {
return null; SaSession session = StpUtil.getSessionByLoginId(loginId);
// 查询员工关联角色缓存
Long employeeId = TokenService.getUserId((String) loginId);
return session.get(USER_ROLE_CACHE_KEY, () -> {
// 数据库中查询员工角色
return roleEmployeeService.getRoleIdList(employeeId).stream().map(String::valueOf).collect(Collectors.toList());
});
}
/**
* 清理角色关联权限 缓存
* TODO listen 待调用
*
* @param roleId
*/
public static void clearRoleCache(Long roleId) {
SaSessionCustomUtil.deleteSessionById(ROLE_CACHE_KEY + roleId);
}
/**
* 清理 员工关联角色 缓存
* TODO listen 待调用
*
* @param employeeId
*/
public static void clearUserRoleCache(Long employeeId) {
String loginId = TokenService.generateLoginId(employeeId, UserTypeEnum.ADMIN_EMPLOYEE);
SaSession session = StpUtil.getSessionByLoginId(loginId, false);
if (null == session) {
return;
}
session.delete(USER_ROLE_CACHE_KEY);
} }
} }

View File

@ -73,7 +73,7 @@ public class LoginController {
return loginService.getCaptcha(); return loginService.getCaptcha();
} }
// TODO listen // TODO listen 测试待删除
@NoNeedLogin @NoNeedLogin
@ApiOperation("测试权限 @listen") @ApiOperation("测试权限 @listen")
@GetMapping("/listen/test") @GetMapping("/listen/test")

View File

@ -1,6 +1,8 @@
package net.lab1024.sa.admin.module.system.menu.constant; package net.lab1024.sa.admin.module.system.menu.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.lab1024.sa.common.common.enumeration.BaseEnum; import net.lab1024.sa.common.common.enumeration.BaseEnum;
/** /**
@ -12,6 +14,8 @@ import net.lab1024.sa.common.common.enumeration.BaseEnum;
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net * @Copyright 1024创新实验室 https://1024lab.net
*/ */
@Getter
@AllArgsConstructor
public enum MenuTypeEnum implements BaseEnum { public enum MenuTypeEnum implements BaseEnum {
/** /**
* 目录 * 目录
@ -26,23 +30,7 @@ public enum MenuTypeEnum implements BaseEnum {
*/ */
POINTS(3, "功能点"); POINTS(3, "功能点");
private Integer value; private final Integer value;
private String desc; private final String desc;
MenuTypeEnum(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String getDesc() {
return desc;
}
} }

View File

@ -0,0 +1,52 @@
package net.lab1024.sa.admin.module.system.menu.service;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.SneakyThrows;
import net.lab1024.sa.admin.module.system.menu.constant.MenuTypeEnum;
import net.lab1024.sa.admin.module.system.menu.dao.MenuDao;
import net.lab1024.sa.admin.module.system.menu.domain.entity.MenuEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 功能菜单业务
*
* @author Turbolisten
* @date 2023/7/20 19:04
*/
@Service
public class MenuCacheService {
@Autowired
private MenuDao menuDao;
private static final Cache<String, List<String>> MENU_URL_CACHE = CacheBuilder.newBuilder().build();
/**
* 查询数据表中 需要校验权限的url
*
* @return
*/
@SneakyThrows
public List<String> queryNeedCheckPermissionsUrl() {
return MENU_URL_CACHE.get("MENU_URL_CACHE", () -> {
// TODO listen 待确定哪个字段做为url
return menuDao.queryMenuByType(MenuTypeEnum.POINTS.getValue(), false, false)
.stream()
.map(MenuEntity::getApiPerms)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
});
}
public static void clearCache() {
MENU_URL_CACHE.invalidateAll();
}
}

View File

@ -65,6 +65,12 @@ public class MenuService {
menuEntity.setApiPerms(perms); menuEntity.setApiPerms(perms);
} }
menuDao.insert(menuEntity); menuDao.insert(menuEntity);
// 清除权限缓存
if (MenuTypeEnum.POINTS.equalsValue(menuEntity.getMenuType())) {
MenuCacheService.clearCache();
}
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
@ -102,6 +108,11 @@ public class MenuService {
menuEntity.setApiPerms(perms); menuEntity.setApiPerms(perms);
} }
menuDao.updateById(menuEntity); menuDao.updateById(menuEntity);
// 清除权限缓存
if (MenuTypeEnum.POINTS.equalsValue(menuEntity.getMenuType())) {
MenuCacheService.clearCache();
}
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
@ -118,8 +129,11 @@ public class MenuService {
return ResponseDTO.userErrorParam("所选菜单不能为空"); return ResponseDTO.userErrorParam("所选菜单不能为空");
} }
menuDao.deleteByMenuIdList(menuIdList, employeeId, Boolean.TRUE); menuDao.deleteByMenuIdList(menuIdList, employeeId, Boolean.TRUE);
//子节点也需要删除 // 子节点也需要删除
this.recursiveDeleteChildren(menuIdList, employeeId); this.recursiveDeleteChildren(menuIdList, employeeId);
// 清除权限缓存
MenuCacheService.clearCache();
return ResponseDTO.ok(); return ResponseDTO.ok();
} }

View File

@ -60,11 +60,6 @@
<artifactId>sa-token-redis-jackson</artifactId> <artifactId>sa-token-redis-jackson</artifactId>
</dependency> </dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
</dependency>
<!-- sa-token end --> <!-- sa-token end -->
<dependency> <dependency>

View File

@ -26,6 +26,11 @@ public class StringConst {
*/ */
public static final String HORIZONTAL = "-"; public static final String HORIZONTAL = "-";
/**
* 全局通用 冒号
*/
public static final String COLON = ":";
/** /**
* 全局通用分隔符 * 全局通用分隔符
*/ */

View File

@ -31,13 +31,7 @@ public class RequestUser {
*/ */
private UserTypeEnum userType; private UserTypeEnum userType;
/**
* TODO listen 准备移除
* 为了其他地方不报错
*/
@Deprecated
private String ip; private String ip;
@Deprecated
private String userAgent; private String userAgent;
} }

View File

@ -19,28 +19,28 @@ public enum SystemEnvEnum implements BaseEnum {
/** /**
* dev * dev
*/ */
DEV(SystemEnvironmentNameConst.DEV, "开发环境"), DEV(EnvConst.DEV, "开发环境"),
/** /**
* test * test
*/ */
TEST(SystemEnvironmentNameConst.TEST, "测试环境"), TEST(EnvConst.TEST, "测试环境"),
/** /**
* pre * pre
*/ */
PRE(SystemEnvironmentNameConst.PRE, "预发布环境"), PRE(EnvConst.PRE, "预发布环境"),
/** /**
* prod * prod
*/ */
PROD(SystemEnvironmentNameConst.PROD, "生产环境"); PROD(EnvConst.PROD, "生产环境");
private final String value; private final String value;
private final String desc; private final String desc;
public static final class SystemEnvironmentNameConst { public static final class EnvConst {
public static final String DEV = "dev"; public static final String DEV = "dev";
public static final String TEST = "test"; public static final String TEST = "test";
public static final String PRE = "pre"; public static final String PRE = "pre";

View File

@ -1,5 +1,8 @@
package net.lab1024.sa.common.common.enumeration; package net.lab1024.sa.common.common.enumeration;
import lombok.AllArgsConstructor;
import lombok.Getter;
/** /**
* 用户类型 * 用户类型
* *
@ -9,29 +12,16 @@ package net.lab1024.sa.common.common.enumeration;
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net 2012-2022 * @Copyright 1024创新实验室 https://1024lab.net 2012-2022
*/ */
@Getter
@AllArgsConstructor
public enum UserTypeEnum implements BaseEnum { public enum UserTypeEnum implements BaseEnum {
/** /**
* 管理端 员工用户 * 管理端 员工用户
*/ */
ADMIN_EMPLOYEE(1, "员工"); ADMIN_EMPLOYEE(1, "管理端-员工");
private Integer type; private final Integer value;
private String desc; private final String desc;
UserTypeEnum(Integer type, String desc) {
this.type = type;
this.desc = desc;
}
@Override
public Integer getValue() {
return type;
}
@Override
public String getDesc() {
return desc;
}
} }

View File

@ -1,14 +1,24 @@
package net.lab1024.sa.common.common.interceptor; package net.lab1024.sa.common.common.interceptor;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.lab1024.sa.common.common.annoation.NoNeedLogin; import net.lab1024.sa.common.common.annoation.NoNeedLogin;
import net.lab1024.sa.common.common.code.UserErrorCode; import net.lab1024.sa.common.common.code.UserErrorCode;
import net.lab1024.sa.common.common.constant.RequestHeaderConst;
import net.lab1024.sa.common.common.domain.RequestUser; import net.lab1024.sa.common.common.domain.RequestUser;
import net.lab1024.sa.common.common.domain.ResponseDTO; import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.common.domain.SystemEnv;
import net.lab1024.sa.common.common.enumeration.SystemEnvEnum;
import net.lab1024.sa.common.common.enumeration.UserTypeEnum;
import net.lab1024.sa.common.common.util.SmartRequestUtil; import net.lab1024.sa.common.common.util.SmartRequestUtil;
import net.lab1024.sa.common.module.support.token.TokenService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -29,17 +39,21 @@ import java.util.List;
* @date 2023-07-12 21:56:14 * @date 2023-07-12 21:56:14
*/ */
public abstract class AbstractInterceptor implements HandlerInterceptor { public abstract class AbstractInterceptor implements HandlerInterceptor {
/**
* 校验 token @Autowired
*/ private SystemEnv systemEnv;
public abstract void checkSaToken();
/** /**
* Token 获取当前请求用户信息 * 校验 token
* *
* @return * @param userId
*/ */
public abstract RequestUser getRequestUser(); public abstract RequestUser getDevUser(Long userId);
/**
* 校验 当前服务用户类型
*/
public abstract UserTypeEnum getUserType();
/** /**
* 拦截路径 * 拦截路径
@ -83,8 +97,8 @@ public abstract class AbstractInterceptor implements HandlerInterceptor {
if (!isHandler) { if (!isHandler) {
return true; return true;
} }
// 校验 token 获取当前请求用户信息 // 校验 token
ResponseDTO<RequestUser> res = this.checkTokenAndGetUser(); ResponseDTO<RequestUser> res = this.checkTokenAndGetUser(request);
if (res.getOk()) { if (res.getOk()) {
SmartRequestUtil.setUser(res.getData()); SmartRequestUtil.setUser(res.getData());
return true; return true;
@ -99,7 +113,6 @@ public abstract class AbstractInterceptor implements HandlerInterceptor {
} }
/** /**
* 校验 sa token
* 判断 sa-token 未登录场景值 * 判断 sa-token 未登录场景值
* 自己根据业务在下面 switch 添加分支判断 * 自己根据业务在下面 switch 添加分支判断
* NotLoginException.NOT_TOKEN 无token * NotLoginException.NOT_TOKEN 无token
@ -111,13 +124,28 @@ public abstract class AbstractInterceptor implements HandlerInterceptor {
* <p> * <p>
* ps :之所以没有在全局异常里处理 是因为后续还有操作 * ps :之所以没有在全局异常里处理 是因为后续还有操作
*/ */
public ResponseDTO<RequestUser> checkTokenAndGetUser() { public ResponseDTO<RequestUser> checkTokenAndGetUser(HttpServletRequest request) {
/**
* 处理非生产环境的测试 token 便于开发调试
* 如不需要 可以删除此段判断代码
*/
if (SystemEnvEnum.PROD != systemEnv.getCurrentEnv()) {
String tokenValue = StpUtil.getTokenValue();
if (NumberUtils.isDigits(tokenValue)) {
RequestUser user = this.getDevUser(NumberUtils.createLong(tokenValue));
this.handleRequestIpAndAgent(user, request);
// sa token 登录身份临时切换
StpUtil.switchTo(TokenService.generateLoginId(user.getUserId(), user.getUserType()));
return ResponseDTO.ok(user);
}
}
try { try {
/** /**
* sa-token 会从当前请求header or body 中获取token * sa-token 会从当前请求 header or body 中获取token
* 检验当前会话是否已经登录, 如果未登录则抛出异常`NotLoginException` * 检验当前会话是否已经登录, 如果未登录则抛出异常`NotLoginException`
*/ */
this.checkSaToken(); StpUtil.checkLogin();
} catch (NotLoginException e) { } catch (NotLoginException e) {
switch (e.getType()) { switch (e.getType()) {
case NotLoginException.BE_REPLACED: case NotLoginException.BE_REPLACED:
@ -129,8 +157,13 @@ public abstract class AbstractInterceptor implements HandlerInterceptor {
return ResponseDTO.error(UserErrorCode.LOGIN_STATE_INVALID); return ResponseDTO.error(UserErrorCode.LOGIN_STATE_INVALID);
} }
} }
RequestUser requestUser = this.getRequestUser(); // 校验token的用户类型
return ResponseDTO.ok(requestUser); UserTypeEnum systemUserTypeEnum = this.getUserType();
RequestUser user = this.buildCurrentUser(request);
if (null == user || systemUserTypeEnum != user.getUserType()) {
return ResponseDTO.error(UserErrorCode.LOGIN_STATE_INVALID);
}
return ResponseDTO.ok(user);
} }
/** /**
@ -147,8 +180,35 @@ public abstract class AbstractInterceptor implements HandlerInterceptor {
response.flushBuffer(); response.flushBuffer();
} }
@Override /**
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { * build 当前请求用户
SmartRequestUtil.remove(); *
* @param request
* @return
*/
public RequestUser buildCurrentUser(HttpServletRequest request) {
// 获取额外数据
SaSession session = StpUtil.getSession();
UserTypeEnum userTypeEnum = (UserTypeEnum) session.get(TokenService.EXTRA_KEY_USER_TYPE);
String userName = session.getString(TokenService.EXTRA_KEY_USER_NAME);
// 当前请求对象
RequestUser user = new RequestUser();
user.setUserId(TokenService.getUserId((String) StpUtil.getLoginId()));
user.setUserName(userName);
user.setUserType(userTypeEnum);
this.handleRequestIpAndAgent(user, request);
return user;
}
/**
* 设置 当前请求ip agent
*
* @param requestUser
* @param request
*/
private void handleRequestIpAndAgent(RequestUser requestUser, HttpServletRequest request) {
requestUser.setUserAgent(ServletUtil.getHeaderIgnoreCase(request, RequestHeaderConst.USER_AGENT));
requestUser.setIp(ServletUtil.getClientIP(request));
} }
} }

View File

@ -1,6 +1,7 @@
package net.lab1024.sa.common.common.util; package net.lab1024.sa.common.common.util;
import lombok.extern.slf4j.Slf4j; import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import net.lab1024.sa.common.common.domain.RequestUser; import net.lab1024.sa.common.common.domain.RequestUser;
/** /**
@ -12,26 +13,28 @@ import net.lab1024.sa.common.common.domain.RequestUser;
* @Email lab1024@163.com * @Email lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net * @Copyright 1024创新实验室 https://1024lab.net
*/ */
@Slf4j
public class SmartRequestUtil { public class SmartRequestUtil {
private static final ThreadLocal<RequestUser> LOCAL = new ThreadLocal<>(); private static final String STORAGE_KEY = "user";
public static void setUser(RequestUser requestUser) { public static void setUser(RequestUser user) {
LOCAL.set(requestUser); SaStorage storage = SaHolder.getStorage();
storage.set(STORAGE_KEY, user);
} }
/**
* 获取 当前 token 请求用户
*
* @return
*/
public static RequestUser getUser() { public static RequestUser getUser() {
return LOCAL.get(); SaStorage storage = SaHolder.getStorage();
return storage.getModel(STORAGE_KEY, RequestUser.class);
} }
public static Long getUserId() { public static Long getUserId() {
RequestUser requestUser = getUser(); RequestUser user = getUser();
return null == requestUser ? null : requestUser.getUserId(); return null != user ? user.getUserId() : null;
}
public static void remove() {
LOCAL.remove();
} }

View File

@ -1,6 +1,7 @@
package net.lab1024.sa.common.config; package net.lab1024.sa.common.config;
import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.interceptor.SaInterceptor;
import com.google.common.collect.Sets;
import net.lab1024.sa.common.common.interceptor.AbstractInterceptor; import net.lab1024.sa.common.common.interceptor.AbstractInterceptor;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -10,7 +11,9 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* web相关配置 * web相关配置
@ -33,8 +36,10 @@ public class MvcConfig implements WebMvcConfigurer {
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
// 先注册 登录拦截器 // 先注册 登录拦截器
Set<String> ignoreUrlSet = Sets.newHashSet();
if (CollectionUtils.isNotEmpty(interceptorList)) { if (CollectionUtils.isNotEmpty(interceptorList)) {
interceptorList.forEach(e -> { interceptorList.forEach(e -> {
ignoreUrlSet.addAll(e.getIgnoreUrlList());
registry.addInterceptor(e).addPathPatterns(e.pathPatterns()).excludePathPatterns(e.getIgnoreUrlList()); registry.addInterceptor(e).addPathPatterns(e.pathPatterns()).excludePathPatterns(e.getIgnoreUrlList());
}); });
} }
@ -42,7 +47,7 @@ public class MvcConfig implements WebMvcConfigurer {
// 后注册 sa-token 权限拦截器 不需要可以删除 // 后注册 sa-token 权限拦截器 不需要可以删除
if (CollectionUtils.isNotEmpty(saInterceptorList)) { if (CollectionUtils.isNotEmpty(saInterceptorList)) {
saInterceptorList.forEach(i -> { saInterceptorList.forEach(i -> {
registry.addInterceptor(i).addPathPatterns("/**"); registry.addInterceptor(i).addPathPatterns("/**").excludePathPatterns(new ArrayList<>(ignoreUrlSet));
}); });
} }
} }

View File

@ -198,7 +198,7 @@ public class SwaggerConfig implements EnvironmentAware, BeanDefinitionRegistryPo
Parameter token = new ParameterBuilder().name(RequestHeaderConst.TOKEN) Parameter token = new ParameterBuilder().name(RequestHeaderConst.TOKEN)
.description("token") .description("token")
.modelRef(new ModelRef("string")) .modelRef(new ModelRef("string"))
.parameterType("header").defaultValue("1") .parameterType("header").defaultValue("0")
.required(false) .required(false)
.build(); .build();
return Lists.newArrayList(token); return Lists.newArrayList(token);

View File

@ -34,8 +34,6 @@ public class UrlConfig {
@Autowired @Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping; private RequestMappingHandlerMapping requestMappingHandlerMapping;
public static List<String> AUTH_URL_LIST = Lists.newArrayList();
/** /**
* 获取每个方法的请求路径 * 获取每个方法的请求路径
* *
@ -81,8 +79,6 @@ public class UrlConfig {
Set<String> urlSet = entry.getValue(); Set<String> urlSet = entry.getValue();
List<RequestUrlVO> requestUrlList = this.buildRequestUrl(method, urlSet); List<RequestUrlVO> requestUrlList = this.buildRequestUrl(method, urlSet);
authUrlList.addAll(requestUrlList); authUrlList.addAll(requestUrlList);
AUTH_URL_LIST.addAll(urlSet);
} }
log.info("需要权限校验的URL{}", authUrlList.stream().map(RequestUrlVO::getUrl).collect(Collectors.toList())); log.info("需要权限校验的URL{}", authUrlList.stream().map(RequestUrlVO::getUrl).collect(Collectors.toList()));
return authUrlList; return authUrlList;

View File

@ -1,29 +0,0 @@
package net.lab1024.sa.common.config.satoken;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* sa token 配置
*
* @author: listen
* @date: 2023/7/12 20:46
*/
@Configuration
public class SaTokenCommonConfig {
/**
* 整合 jwt
*
* @return
* @see <a>https://sa-token.cc/doc.html#/plugin/jwt-extend</a>
*/
@Bean
public StpLogic getStpLogicJwt() {
// Simple 简单模式
return new StpLogicJwtForSimple();
}
}

View File

@ -1,5 +1,6 @@
package net.lab1024.sa.common.module.support.token; package net.lab1024.sa.common.module.support.token;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import net.lab1024.sa.common.common.constant.StringConst; import net.lab1024.sa.common.common.constant.StringConst;
@ -37,31 +38,34 @@ public class TokenService {
/** /**
* 设置登录模式参数 * 设置登录模式参数
* 具体参数 @see SaLoginModel 属性 * 具体参数 {@link SaLoginModel } 属性
* 已经写的挺清楚的了 * 已经写的挺清楚的了
*/ */
SaLoginModel loginModel = new SaLoginModel(); SaLoginModel loginModel = new SaLoginModel();
// 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型 // 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
loginModel.setDevice(String.valueOf(loginDeviceEnum.getDesc())); loginModel.setDevice(String.valueOf(loginDeviceEnum.getDesc()));
// 扩展参数 只在 jwt 模式下 有效
loginModel.setExtra(EXTRA_KEY_USER_NAME, userName);
loginModel.setExtra(EXTRA_KEY_USER_TYPE, userTypeEnum.getValue());
// 登录
String loginId = generateLoginId(userId, userTypeEnum); String loginId = generateLoginId(userId, userTypeEnum);
StpUtil.login(loginId, loginModel); StpUtil.login(loginId, loginModel);
// 扩展参数 放入会话中 redis session
SaSession session = StpUtil.getSession();
session.set(EXTRA_KEY_USER_NAME, userName);
session.set(EXTRA_KEY_USER_TYPE, userTypeEnum);
return StpUtil.getTokenValue(); return StpUtil.getTokenValue();
} }
public static String generateLoginId(Long userId, UserTypeEnum userType) { public static String generateLoginId(Long userId, UserTypeEnum userType) {
return userType.getValue() + StringConst.HORIZONTAL + userId; return userType.getValue() + StringConst.COLON + userId;
} }
public static Long getUserId(String loginId) { public static Long getUserId(String loginId) {
return Long.valueOf(loginId.substring(loginId.indexOf(StringConst.HORIZONTAL) + 1)); return Long.valueOf(loginId.substring(loginId.indexOf(StringConst.COLON) + 1));
} }
public static Integer getUserType(String loginId) { public static Integer getUserType(String loginId) {
return Integer.valueOf(loginId.substring(0, loginId.indexOf(StringConst.HORIZONTAL))); return Integer.valueOf(loginId.substring(0, loginId.indexOf(StringConst.COLON)));
} }
/** /**