mirror of
https://github.com/RemainderTime/spring-boot-base-demo.git
synced 2026-06-09 11:39:58 +08:00
优化调整sa-token深入集成
This commit is contained in:
11
pom.xml
11
pom.xml
@@ -126,6 +126,17 @@
|
|||||||
<version>${sverlet.version}</version> <!-- 根据需要选择合适的版本 -->
|
<version>${sverlet.version}</version> <!-- 根据需要选择合适的版本 -->
|
||||||
<scope>provided</scope> <!-- 在Web服务器环境中由服务器提供 -->
|
<scope>provided</scope> <!-- 在Web服务器环境中由服务器提供 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||||
|
<version>${sa-token.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-redis-jackson</artifactId>
|
||||||
|
<version>${sa-token.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -23,6 +23,39 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
public class GlobalExceptionHandler {
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token: not login exception
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(cn.dev33.satoken.exception.NotLoginException.class)
|
||||||
|
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||||
|
public GenericResponse<Void> handleNotLoginException(cn.dev33.satoken.exception.NotLoginException e,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
log.warn("Not logged in [URL:{}]: {}", request.getRequestURI(), e.getMessage());
|
||||||
|
return new GenericResponse<>(401, null, "Please login first");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token: not permission exception
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(cn.dev33.satoken.exception.NotPermissionException.class)
|
||||||
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||||
|
public GenericResponse<Void> handleNotPermissionException(cn.dev33.satoken.exception.NotPermissionException e,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
log.warn("No permission [URL:{}]: {}", request.getRequestURI(), e.getMessage());
|
||||||
|
return new GenericResponse<>(403, null, "No permission to access this resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sa-Token: not role exception
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(cn.dev33.satoken.exception.NotRoleException.class)
|
||||||
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||||
|
public GenericResponse<Void> handleNotRoleException(cn.dev33.satoken.exception.NotRoleException e,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
log.warn("No role [URL:{}]: {}", request.getRequestURI(), e.getMessage());
|
||||||
|
return new GenericResponse<>(403, null, "Insufficient role privileges");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理登录/认证异常
|
* 处理登录/认证异常
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package cn.xf.basedemo.config;
|
package cn.xf.basedemo.config;
|
||||||
|
|
||||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
|
import cn.xf.basedemo.interceptor.SaTokenContextInterceptor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
@@ -14,10 +16,16 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SaTokenContextInterceptor saTokenContextInterceptor;
|
||||||
|
|
||||||
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
|
||||||
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
|
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
|
||||||
|
// 注册上下文注入拦截器,兼容旧业务代码
|
||||||
|
registry.addInterceptor(saTokenContextInterceptor).addPathPatterns("/**");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ package cn.xf.basedemo.controller.business;
|
|||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
|
import cn.dev33.satoken.annotation.SaMode;
|
||||||
import cn.xf.basedemo.common.model.LoginUser;
|
import cn.xf.basedemo.common.model.LoginUser;
|
||||||
import cn.xf.basedemo.common.model.RetObj;
|
import cn.xf.basedemo.common.model.RetObj;
|
||||||
import cn.xf.basedemo.interceptor.SessionContext;
|
import cn.xf.basedemo.interceptor.SessionContext;
|
||||||
@@ -38,10 +39,38 @@ public class UserController {
|
|||||||
|
|
||||||
@Operation(summary = "用户信息", description = "用户信息")
|
@Operation(summary = "用户信息", description = "用户信息")
|
||||||
@PostMapping("/info")
|
@PostMapping("/info")
|
||||||
@SaCheckPermission("user:info") //权限校验
|
@SaCheckPermission("user:info") // 权限校验
|
||||||
public RetObj info(){
|
public RetObj info() {
|
||||||
LoginUser loginUser = SessionContext.getInstance().get();
|
LoginUser loginUser = SessionContext.getInstance().get();
|
||||||
return RetObj.success(loginUser);
|
return RetObj.success(loginUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "注解示例-角色校验", description = "必须具有 'super-admin' 角色才能访问")
|
||||||
|
@PostMapping("/check-role")
|
||||||
|
@SaCheckRole("super-admin")
|
||||||
|
public RetObj checkRole() {
|
||||||
|
return RetObj.success("您拥有 super-admin 角色,验证通过");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "注解示例-权限组合(OR)", description = "只要拥有 user:add 或 user:update 其中一个权限即可")
|
||||||
|
@PostMapping("/check-permission-or")
|
||||||
|
@SaCheckPermission(value = { "user:add", "user:update" }, mode = SaMode.OR)
|
||||||
|
public RetObj checkPermissionOr() {
|
||||||
|
return RetObj.success("您拥有 user:add 或 user:update 权限,验证通过");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "注解示例-权限组合(AND)", description = "必须同时拥有 user:delete 和 user:export 权限")
|
||||||
|
@PostMapping("/check-permission-and")
|
||||||
|
@SaCheckPermission(value = { "user:delete", "user:export" }, mode = SaMode.AND)
|
||||||
|
public RetObj checkPermissionAnd() {
|
||||||
|
return RetObj.success("您同时拥有 user:delete 和 user:export 权限,验证通过");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "注解示例-忽略鉴权", description = "无需登录即可访问(常用于注册、验证码等公开接口)")
|
||||||
|
@PostMapping("/public-api")
|
||||||
|
@SaIgnore
|
||||||
|
public RetObj publicApi() {
|
||||||
|
return RetObj.success("这是一个公开接口,@SaIgnore 生效");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cn.xf.basedemo.interceptor;
|
package cn.xf.basedemo.interceptor;
|
||||||
|
|
||||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
@@ -17,18 +16,26 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class InterceptorConfig implements WebMvcConfigurer {
|
public class InterceptorConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Bean
|
@org.springframework.beans.factory.annotation.Autowired
|
||||||
public TokenInterceptor tokenInterceptor() {
|
private SaTokenContextInterceptor saTokenContextInterceptor;
|
||||||
return new TokenInterceptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(tokenInterceptor()) //登录逻辑拦截类
|
// 注册 Sa-Token 拦截器,定义详细认证规则
|
||||||
.addPathPatterns("/**") //需要拦截的请求(设置的全部拦截)
|
registry.addInterceptor(new SaInterceptor(handler -> {
|
||||||
.excludePathPatterns("/user/login", "/web/**"); //忽略的请求
|
// 指定一条 match 规则
|
||||||
}
|
cn.dev33.satoken.stp.StpUtil.checkLogin();
|
||||||
|
}))
|
||||||
|
.addPathPatterns("/**")
|
||||||
|
.excludePathPatterns("/user/login", "/web/**", "/swagger-resources/**", "/webjars/**", "/v3/**",
|
||||||
|
"/doc.html");
|
||||||
|
|
||||||
|
// 注册 Context 拦截器,用于注入 SessionContext
|
||||||
|
registry.addInterceptor(saTokenContextInterceptor)
|
||||||
|
.addPathPatterns("/**")
|
||||||
|
.excludePathPatterns("/user/login", "/web/**", "/swagger-resources/**", "/webjars/**", "/v3/**",
|
||||||
|
"/doc.html");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 放行Knife4j请求
|
* 放行Knife4j请求
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package cn.xf.basedemo.interceptor;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.xf.basedemo.common.model.LoginUser;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: Sa-Token 上下文拦截器
|
||||||
|
* 用于将 Sa-Token Session 中的用户信息注入到 SessionContext (ThreadLocal)
|
||||||
|
* 以兼容旧的业务代码 (SessionContext.getInstance().get())
|
||||||
|
* @Author: xiongfeng
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SaTokenContextInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||||
|
throws Exception {
|
||||||
|
// 如果已登录,尝试从 Session 中获取用户信息并注入 ThreadLocal
|
||||||
|
if (StpUtil.isLogin()) {
|
||||||
|
// 从 Sa-Token Session 中读取 loginUser (需确保登录时已存入)
|
||||||
|
LoginUser loginUser = (LoginUser) StpUtil.getSession().get("loginUser");
|
||||||
|
if (loginUser != null) {
|
||||||
|
SessionContext.getInstance().set(loginUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
|
||||||
|
ModelAndView modelAndView) throws Exception {
|
||||||
|
// 请求结束后清理 ThreadLocal,防止内存泄漏
|
||||||
|
SessionContext.getInstance().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package cn.xf.basedemo.interceptor;
|
package cn.xf.basedemo.interceptor;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpInterface;
|
import cn.dev33.satoken.stp.StpInterface;
|
||||||
import cn.xf.basedemo.common.utils.ApplicationContextUtils;
|
|
||||||
import cn.xf.basedemo.mappers.SysPermissionMapper;
|
import cn.xf.basedemo.mappers.SysPermissionMapper;
|
||||||
import cn.xf.basedemo.mappers.SysRoleMapper;
|
import cn.xf.basedemo.mappers.SysRoleMapper;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -18,19 +18,23 @@ import java.util.List;
|
|||||||
@Component
|
@Component
|
||||||
public class StpInterfaceImpl implements StpInterface {
|
public class StpInterfaceImpl implements StpInterface {
|
||||||
|
|
||||||
private SysPermissionMapper sysPermissionMapper = ApplicationContextUtils.getBean(SysPermissionMapper.class);
|
@Autowired
|
||||||
private SysRoleMapper sysRoleMapper = ApplicationContextUtils.getBean(SysRoleMapper.class);
|
private SysPermissionMapper sysPermissionMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SysRoleMapper sysRoleMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getPermissionList(Object userId, String s) {
|
public List<String> getPermissionList(Object userId, String s) {
|
||||||
//获取登录用户权限数据
|
// 获取登录用户权限数据
|
||||||
Long aLong = Long.valueOf(userId.toString());
|
Long uId = Long.valueOf(userId.toString());
|
||||||
List<String> permissionList = sysPermissionMapper.getPermissionListByRoleId(aLong);
|
List<String> permissionList = sysPermissionMapper.getPermissionListByUserId(uId);
|
||||||
return permissionList;
|
return permissionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getRoleList(Object userId, String s) {
|
public List<String> getRoleList(Object userId, String s) {
|
||||||
//获取用户角色数据
|
// 获取用户角色数据
|
||||||
return sysRoleMapper.getRoleListByUserId((Long) userId);
|
return sysRoleMapper.getRoleListByUserId((Long) userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author xiongfeng
|
* @author xiongfeng
|
||||||
* @description 针对表【sys_permission(系统权限表 sys_permission)】的数据库操作Mapper
|
* @description 针对表【sys_permission(系统权限表 sys_permission)】的数据库操作Mapper
|
||||||
* @createDate 2025-08-19 21:22:03
|
* @createDate 2025-08-19 21:22:03
|
||||||
* @Entity cn.xf.basedemo.model.domain.SysPermission
|
* @Entity cn.xf.basedemo.model.domain.SysPermission
|
||||||
*/
|
*/
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface SysPermissionMapper extends BaseMapper<SysPermission> {
|
public interface SysPermissionMapper extends BaseMapper<SysPermission> {
|
||||||
|
|
||||||
List<String> getPermissionListByRoleId(Long useId);
|
List<String> getPermissionListByUserId(Long userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cn.xf.basedemo.service.impl;
|
package cn.xf.basedemo.service.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.xf.basedemo.common.model.EsBaseModel;
|
|
||||||
import cn.xf.basedemo.common.model.LoginInfo;
|
import cn.xf.basedemo.common.model.LoginInfo;
|
||||||
import cn.xf.basedemo.common.model.LoginUser;
|
import cn.xf.basedemo.common.model.LoginUser;
|
||||||
import cn.xf.basedemo.common.model.RetObj;
|
import cn.xf.basedemo.common.model.RetObj;
|
||||||
@@ -56,7 +55,8 @@ public class UserServiceImpl implements UserService {
|
|||||||
}
|
}
|
||||||
String loginJson = "";
|
String loginJson = "";
|
||||||
try {
|
try {
|
||||||
loginJson = RSAUtils.privateDecryption(res.getEncryptedData(), RSAUtils.getPrivateKey(globalConfig.getRsaPrivateKey()));
|
loginJson = RSAUtils.privateDecryption(res.getEncryptedData(),
|
||||||
|
RSAUtils.getPrivateKey(globalConfig.getRsaPrivateKey()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("解密失败------", e);
|
log.error("解密失败------", e);
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ public class UserServiceImpl implements UserService {
|
|||||||
if (!StringUtils.isEmpty(loginInfo.check())) {
|
if (!StringUtils.isEmpty(loginInfo.check())) {
|
||||||
return RetObj.error(loginInfo.check());
|
return RetObj.error(loginInfo.check());
|
||||||
}
|
}
|
||||||
//校验登录账号密码
|
// 校验登录账号密码
|
||||||
QueryWrapper queryWrapper = new QueryWrapper();
|
QueryWrapper queryWrapper = new QueryWrapper();
|
||||||
queryWrapper.eq("account", loginInfo.getAccount());
|
queryWrapper.eq("account", loginInfo.getAccount());
|
||||||
queryWrapper.eq("password", loginInfo.getPwd());
|
queryWrapper.eq("password", loginInfo.getPwd());
|
||||||
@@ -84,13 +84,16 @@ public class UserServiceImpl implements UserService {
|
|||||||
loginUser.setName(user.getName());
|
loginUser.setName(user.getName());
|
||||||
loginUser.setPhone(user.getPhone());
|
loginUser.setPhone(user.getPhone());
|
||||||
|
|
||||||
String token = JwtTokenUtils.createToken(user.getId());
|
// 登录成功 写入sa-token中
|
||||||
loginUser.setToken(token);
|
|
||||||
|
|
||||||
redisTemplate.opsForValue().set("token:" + token, JSONObject.toJSONString(loginUser), 3600, TimeUnit.SECONDS);
|
|
||||||
redisTemplate.opsForValue().set("user_login_token:" + user.getId(), token, 3600, TimeUnit.SECONDS);
|
|
||||||
//登录成功 写入sa-token中
|
|
||||||
StpUtil.login(user.getId());
|
StpUtil.login(user.getId());
|
||||||
|
|
||||||
|
// 将用户信息缓存到 Session 中,以便后续获取
|
||||||
|
StpUtil.getSession().set("loginUser", loginUser);
|
||||||
|
|
||||||
|
// 获取 Sa-Token 生成的 token 值
|
||||||
|
cn.dev33.satoken.stp.SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||||
|
loginUser.setToken(tokenInfo.tokenValue);
|
||||||
|
|
||||||
return RetObj.success(loginUser);
|
return RetObj.success(loginUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,3 +24,26 @@ spring:
|
|||||||
import:
|
import:
|
||||||
- nacos:${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
- nacos:${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||||
|
|
||||||
|
# Sa-Token Configuration
|
||||||
|
sa-token:
|
||||||
|
# token name (frontend needs to use this name, e.g., Authorization: Bearer xxxx, or just satoken: xxxx)
|
||||||
|
token-name: Authorization
|
||||||
|
# token validity period (seconds), -1 means never expire
|
||||||
|
timeout: 2592000
|
||||||
|
# token temporary validity (seconds), -1 means never expire
|
||||||
|
activity-timeout: -1
|
||||||
|
# allow concurrent login
|
||||||
|
is-concurrent: true
|
||||||
|
# share token api
|
||||||
|
is-share: true
|
||||||
|
# token style
|
||||||
|
token-style: uuid
|
||||||
|
# log
|
||||||
|
is-log: false
|
||||||
|
# read from cookie
|
||||||
|
is-read-cookie: false
|
||||||
|
# read from header
|
||||||
|
is-read-header: true
|
||||||
|
# read from body
|
||||||
|
is-read-body: false
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
update_time,update_by
|
update_time,update_by
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="getPermissionListByRoleId" resultType="java.lang.String">
|
<select id="getPermissionListByUserId" resultType="java.lang.String">
|
||||||
select code
|
select code
|
||||||
from sys_permission p
|
from sys_permission p
|
||||||
left join sys_role_permission rp on p.id = rp.permission_id
|
left join sys_role_permission rp on p.id = rp.permission_id
|
||||||
|
|||||||
Reference in New Issue
Block a user