说明:yudao 是一款集成了许多基础功能的 Java 开发的开源框架,有 Spring Boot、Spring Cloud 版本,可以在此基础上开发自己的业务代码。
本文介绍在 yudao 中接口如何忽略鉴权,yudao 更多内容,参看下面这个专栏:
忽略Token鉴权
想让某个接口不需要携带 Token 访问,只需要在接口上打上 @PermitAll
注解即可,如下:
@GetMapping("/hello")
@PermitAll
public String hello() {
return "hello";
}
访问,不需要携带 token
忽略多租户校验
yudao 是支持多租户的,通过配置文件的这个配置项设置
所谓多租户,即不同的客户使用同一套系统,所有相关的数据库表增加 tenant_id
字段,区分不同的客户,做到客户级别的数据隔离,让客户方看起来感觉是自己在独享系统。
在 yudao 中,多租户的实现是基于 SQL 拼接,当开启多租户模式时,框架会在对应的数据库操作 SQL 里拼接 tenant-id = xxx
条件,而 tenant-id 取自当前用户的上下文中,也是来自于前端传递请求头。
如果不传,接口就会报下面这个错误。
如何让某个接口不进行租户校验,有两种办法,第一种办法是在接口上打上 @TenantIgnore
注解,标识该接口忽略租户校验
@GetMapping("/hello")
@PermitAll
@TenantIgnore
public String hello() {
return "hello";
}
第二种办法是在配置文件中指定,如下:
(可以看到这里还有排除数据库表的设置ignore-tables
,如果有数据库表不需要设计成多租户,可以在这里进行配置,排除这些表)
值得注意的是,如果你的接口有路径参数,也就是路径末尾是参数值,那么需要考虑参数未传的情况,所以在排除时,路径末尾补充 /*
特殊情况
如果希望开放某个接口,不校验 token,只是跳过了校验,而如果系统多租户配置是开启状态的话,不校验 tenant-id,是查不出数据的,如下:
(根据 userId 查询用户,忽略多租户校验)
@GetMapping("/hello/{userId}")
@PermitAll
@TenantIgnore
public CommonResult<Boolean> hello(@PathVariable("userId") Long userId) {
return userDemoService.hello(userId);
}
(service)
@Override
public CommonResult<Boolean> hello(Long userId) {
// 查询用户
AdminUserDO adminUserDO = userMapper.selectById(userId);
if (adminUserDO == null) {
throw exception(new ErrorCode(10001, "用户不存在"));
}
// 查询用户所拥有的角色
List<UserRoleDO> userRoleDOList = userRoleMapper.selectRoleListByUserId(userId);
if (CollUtil.isEmpty(userRoleDOList)) {
throw exception(new ErrorCode(10002, "用户角色不存在"));
}
return success(Boolean.TRUE);
}
虽能查出数据,但如果接口里有跨服务的 RPC 调用查询,那么跨服务的查询,是无法查询出数据的。
虽然官方提供了一个工具类,可以针对单个查询排除租户,如下:
使用如下
但问题是,如果该接口查询链路比较深(还有跨服务的 RPC 调用),调用的查询方法比较多,其中有些方法还是有多方调用的,总不能沿着链路,把所有路径上的查询都改成忽略租户的吧?
这里我想到有两种办法
(1)让前端写死一个 tenant-id,调用接口时,请求头携带 tenant-id 访问;
(2)接口传入一个 tenant-id 参数,接口接收到参数后,将 tenant-id 存入到上下文中;
第一种方式不用说了,说第二种方式,如下,接口接收租户参数,接收后写入到上下文中
@GetMapping("/hello/{userId}")
@PermitAll
@TenantIgnore
public CommonResult<Boolean> hello(@PathVariable("userId") Long userId,
@RequestParam("tenantId") Long tenantId) {
TenantContextHolder.setTenantId(tenantId);
return userDemoService.hello(userId);
}
更近一步,可以查出用户后,将查出来的 AdminUserDO 中的 tenantId 写入到上下问中,这样前端也不用传租户参数了,就以传入的用户 ID 所对应的租户为当前接口查询其他数据库表的租户条件。
// 查询用户
AdminUserDO adminUserDO = userMapper.selectById(userId);
if (adminUserDO == null) {
throw exception(new ErrorCode(10001, "用户不存在"));
}
TenantContextHolder.setTenantId(adminUserDO.getTenantId());
查询是 OK 的