只有管理员开放指定 period 之后,才能对指定 period 的业务表数据进行增删改查(增删改已经做限制,采用下面第二种方法。),否则都不允许(返回空或提示权限不足) 管理员无视上面一条要求
1
cnhongwei 153 天前
用 mybatis 不清楚,用 spring security+spring data jpa 有这个查询增加条件和结果集检查功能,但只是看文档中有,没有实践过,不知道对性能有多少影响。但如果用到 spring security 的话,可以看看 spring security 的文档,在 mybatis 中能不能实现。
|
2
Musong 153 天前
我前端啊,后端不懂。有个小小问题,权限是不是应该在查询之前,在代码逻辑中就给拦截了?(我可完全不懂啊,问错了不要笑话我)
|
3
billbur 153 天前 2
第一种就行了,数据库没你想象的那么脆弱,一个是要注意覆盖索引,另一个是分页或者说限制一次能查询的数量
|
5
dong568789 153 天前
我们是加了个 middleware,所有请求经过中间件,都会判断是否要加这个 period 条件判断,如果是就注入到 query 里,然后拼装 sql 的地方,会解析这个 period
|
6
tongjiann OP @Musong #2 你说的没问题,部分请求中存在这个参数,确实可以在查询之前进行拦截。但是吧,有部分情况是,比如和描述中一样,select * from table a where id = 'xxx'。这个时候我并不知道查询出来的数据的 period 是啥。还得查出在之后再进行校验看看是不是合法数据。不过这个方法可行,确实可以加两层,前面先过滤掉一批,这样可以适当的减轻数据库的压力
|
7
paopjian 153 天前
我也不懂后端, 这个应该和用户权限校验走一起的逻辑吧, 在用户权限校验处加一个操作权限判断, 这样以后还有其他的新控制字段也可以再加入
|
8
tongjiann OP @paopjian #7 是的,从代码设计上来说没问题。但是我们目前的系统为了偷懒,允许前端通过构建类似于 SQL 查询条件的方式,直接请求通用接口查询数据库中的数据,可以直接跳过对应字段的校验。但是已经是一坨了,只能往上面再来一坨了
|
9
leejinhong 153 天前
在代码层加这种判断有点不太好,如果后期继续加权限逻辑岂不是得改很多。如果有使用 ORM 的话,适当改装一下 ORM ,业务层面通过 ORM 操作,对于本来业务的侵入会比较少。
|
10
theOneMe 153 天前 1
1. 逻辑上应该是第一种,第二种的话,分页查询如果查询之后过滤可能出现为空的问题,导致缺页;
2. 数据量如果百万以下,不用考虑太多,直接操作就行 |
11
tongjiann OP @dong568789 #5 这个 idea 很好,和我的第一种想法类似,不过你的可能更加完善。我验证一下这个方法在我们代码中的可行性
|
12
tongjiann OP @leejinhong #9 这个需求是针对系统的需求,和业务无关,最终实现肯定是在一个 ORM 层进行实现,并不是在每个业务实现类中进行操作
|
13
2tongW 153 天前 1
我也做过类似的需求,就是采用的第一种方法。如果担心性能的话,是不是可以考虑新增一个“是否可操作”的字段,来维护。
代价就是管理员开放指定 period 的时候需要去批量更新这个字段。 |
14
Jasckcc 153 天前
可考虑使用布隆过滤器或 redis 做前置条件验证。
|
15
needpp 153 天前
新建一个 视图 ,在视图里面查询
|
16
uselessVisitor 153 天前 via Android
Mybatis 插件动态添加 period 字段的筛选
|
17
andy2415 153 天前 1
之前写的<=的逻辑, 只处理了删改查你可以试试改成 in,
```java @Slf4j public class Demo extends JsqlParserSupport implements InnerInterceptor { @Override public void beforePrepare( StatementHandler sh, Connection connection, Integer transactionTimeout) { PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); MappedStatement ms = mpSh.mappedStatement(); SqlCommandType commandType = ms.getSqlCommandType(); if (commandType == SqlCommandType.SELECT || commandType == SqlCommandType.UPDATE || commandType == SqlCommandType.DELETE) { mpSh.mPBoundSql().sql(parserMulti(mpSh.mPBoundSql().sql(), null)); } } @Override protected void processSelect(Select select, int index, String sql, Object obj) { PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); MinorThanEquals minorThanEquals = getMinorThanEquals(); Expression where = plainSelect.getWhere(); plainSelect.setWhere( where == null ? minorThanEquals : new AndExpression(where, minorThanEquals)); } @Override protected void processUpdate(Update update, int index, String sql, Object obj) { .... } @Override protected void processDelete(Delete delete, int index, String sql, Object obj) { .... } @NotNull private static MinorThanEquals getMinorThanEquals() { MinorThanEquals minorThanEquals = new MinorThanEquals(); minorThanEquals.setLeftExpression(new Column("column_name")); minorThanEquals.setRightExpression(new LongValue(4)); return minorThanEquals; } } ``` |
18
28Sv0ngQfIE7Yloe 153 天前
就算是 1 也没必要 select *吧?只要 select count (*)是不是就能满足业务需要了?
|
22
xibeifeng 153 天前 1
|
23
SoviaPhilo 153 天前 1
巧了, 我手上有个项目有类似的需求, 而且也用上面的方式做了统一处理
然后一年以后我就不再用这玩意儿了。 一个问题是管理员需要操作全量数据, 意味着要维护 selectIgnore 另一个问题是出现了相似的相对含义字段,类比一下就是业务增加了第二个 会计期间,而且要基于这个做逻辑 第三个问题是业务性条件,非显式地填充事实上增加了潜在成本 考虑到 PO 的视野根本看不了这么远, 直接用你的方案 1 算了 |
24
MaxYang666 153 天前
如果操作的表不是太多的话,方案 1 就可以,如果操作的表数量比较多,还是想办法抽一个中间层出来比较好
|
25
BiChengfei 153 天前
方案 1 ,加个 Mybatis 拦截器就行
|
26
meeop 153 天前
如果 period 只是表示数据是否可查询的话,可以考虑做两张表,一张草稿标,一张在线查询表,在线表都是可查的,草稿表则是没进入发布态的数据,可发布时刻再插入在线表
|
27
NX2023 153 天前 via iPhone
第一个想到的是 casbin👀
|
28
lmq2582609 153 天前
这个看着和若依的数据权限处理有点像,不过若依的是按部门分数据权限,每个表都有 deptId 字段,可以参考看看。
原理还是 mybatis 拦截器修改 sql 语句来实现的,这样分页功能不会受到影响。 |
29
lmq2582609 153 天前
http://doc.ruoyi.vip/ruoyi/document/htsc.html#%E6%95%B0%E6%8D%AE%E6%9D%83%E9%99%90
|
30
boqiqita 153 天前
补充下数据库里的数据量和 QPS 呗
|
32
tongjiann OP @boqiqita #30 目前已有总数据量千万吧,月度数据增量大概百万,QPS 不高,可能就月底用一下?属于绩效系统,从外部取数,然后算出绩效,面向的对象是主要还是绩效专员
|
33
ningmengzhensuan 153 天前
第一种的话,你要维护所有的表修改,改动量太大,而且后期你们产品再添加新的类似需求的时候,那你还得改全部的 SQL 查询,第二种确实耗费性能
你可以把这两个实现聚合起来,用拦截器+注解,新增一个注解,给需要设定权限限制的 SQL 查询添加上 在拦截器那里添加前置拦截,判断权限,后置处理数据可以再进行特殊判断,添加一些权限、角色配置 这样的话,对代码的渗透较低,可以扩充变更 |
34
nealHuang 153 天前
我们都用 kjqj 来表示 会计期间
|
35
xxmaqzas 153 天前
查询走视图
|
36
zhazi 153 天前
|
37
MMDeJeVS3GtMVLeu 153 天前
我是前端,如果对一个请求做前、后的处理,用 axios 有拦截器这个概念。
搜了一下 sql 也有类似的概念,mybatis 拦截器 https://juejin.cn/post/7116757450274897957 除非项目特别简单、后期不动了,否则强烈不推荐第一种方式: 1 、每个地方加,找起来很痛苦 2 、文档不够好的话,对后面开发的开发来说就是灾难了,因为别人很容易遗漏这个条件 |
38
tongjiann OP @justyeh #37 我描述的不够准确,首先,肯定不可能在每个业务类中修改代码实现这个功能,最起码也要抽一层出来,尽可能减少与业务的关联性,保证后续新增业务表也不需要修改这里的代码。我最终的实现方案是参照 17L 的方式,写一个拦截器,统一在 SQL 执行前进行拦截,然后判断,如果需要就注入 SQL
|
39
crz 153 天前
刚好最近看过,postgres 有 row level security ,数据库层级对访问/操作进行控制,就是对应这种需求的,不知道你们用的数据库有没有类似实现
|
40
tongjiann OP 感谢大家的回复,最终根据 17L 的建议进行了修改
|
41
iMoutai 153 天前
MyBatis-Plus 自带的数据权限插件不符合要求吗?
|
42
m1ch3ng 152 天前
@andy2415 #19 试了下,oracle 分页遇到了一个问题,sql 打印:
==> Preparing: SELECT * FROM (SELECT TMP.*, ROWNUM ROW_ID FROM (SELECT id, serial_number, command, sap_no, product_no, voucher_id, voucher_detail_id, voucher_details_id, record_status, remark, created_time, updated_time, pro_factory, batch_no, product_date, expire_date, product_num, pro_unit_cost, rx_flag, operator_name, operate_date, total_amount, price, old_voucher_id, old_voucher_details_id, spp_hsbl, settlement_time FROM POS_INVENTORY_UPLOAD_RECORD WHERE (command = ?)) TMP WHERE ROWNUM <= ?) WHERE ROW_ID > ? AND created_time >= {d '2024-07-26'} ==> Parameters: SALE_OR_REFUND_SALE(String), 4(Long), 2(Long) <== Total: 0 其中 created_time >= {d '2024-07-26'} 是我自定义拦截器加的 GreaterThanEquals ,然而 mybatis-plus 在 oracle 场景下把它放在了最外层查询里面,如果想要实现放在 WHERE (command = ? AND created_time >= {d '2024-07-26'}),请问该如何调整? P.S. 我配置的自定义拦截器顺序是放在分页插件后面的 |
43
m1ch3ng 152 天前 1
自己研究了下解决了,代码如下:
@Override protected void processSelect(Select select, int index, String sql, Object obj) { // 默认情况下,直接加到最外层查询的 where 后面 PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); if (select.toString().contains("ROWNUM")) { // oracle 分页的情况,需要找到最内层的查询,然后加到 where 后面 plainSelect = getInnerSelect(plainSelect); } // 给查询添加条件 GreaterThanEquals greaterThanEquals = getGreaterThanEquals(); Expression where = plainSelect.getWhere(); plainSelect.setWhere(where == null ? greaterThanEquals : new AndExpression(where, greaterThanEquals)); } private static PlainSelect getInnerSelect(PlainSelect select) { if (select.getFromItem() instanceof SubSelect) { PlainSelect fromSelect = (PlainSelect) ((SubSelect) select.getFromItem()).getSelectBody(); return getInnerSelect(fromSelect); } return select; } @NotNull private static GreaterThanEquals getGreaterThanEquals() { GreaterThanEquals greaterThanEquals = new GreaterThanEquals(); greaterThanEquals.setLeftExpression(new Column("created_time")); greaterThanEquals.setRightExpression(new DateValue("'2024-07-26'")); return greaterThanEquals; } |