6.2 方法级授权与会话状态


6.2 方法级授权与会话状态

本节摘要:授权有两道关口:请求级规则拦路径,方法级注解拦调用。本节写两类规则并解释它们的分工边界;然后讨论身份凭证怎么在多次请求间保持——会话与令牌两种机制的原理、代价与选择。

两道授权关口

请求级规则(6.1 节)按路径粒度判断,优点是集中、一眼看全,缺点是管不到方法内部的条件判断。方法级注解补上这一层:

@Service public class DocumentService { @PreAuthorize("hasRole('EDITOR') or #doc.ownerId == principal.id") public void edit(Document doc) { // 只有编辑角色或文档本人能改:路径规则表达不了这种条件 } @PostAuthorize("returnObject.ownerId == principal.id") public Document view(Long id) { // 返回后再核对:适合"查出来才知道归谁"的场景 } }

启用需要 @EnableMethodSecurity 注解。看到 #docprincipal 这些引用就该明白了:方法级授权能访问方法参数与返回值,表达力远超路径规则。它的实现正是第 4 章的代理——注解由方法安全拦截器在代理层核对,也因此自调用时方法级注解同样失效,这条在第 4.1 节立的规矩在安全域一样有效。

分工边界我的习惯用法:路径级拦大类(哪些 URL 家族需要登录),方法级管细节(这条业务规则谁满足)。路径规则薄、方法规则厚,比把所有角色判断堆进一份集中配置可维护得多。

身份怎么保持:会话与令牌

认证一次之后,后续请求如何证明"还是我"?两种答案,先看会话式:

http.sessionManagement(session -> session .maximumSessions(1) // 同账号最多一处在线 .maxSessionsPreventsLogin(false)); // 新登录踢掉旧会话

服务端保存会话记录,浏览器携带会话标识。优点是天然支持主动失效(踢人下线)、配置简单;代价是服务端有状态——多实例部署要共享会话存储,移动端与跨域场景下小甜饼机制别扭。

令牌式则是无状态答案:认证通过后签发一个自带签名与过期时间的令牌,之后每个请求在头部携带,服务端验签即认身份,不存任何会话。代价也直接:无法主动作废一个已签发的令牌,只能等它过期;要实现"改密码后全端下线",必须引入令牌版本号或黑名单——状态又悄悄回来了。下表把取舍摆在一起:

维度 会话式 令牌式
状态位置 服务端 客户端持有
主动失效 容易(删会话即失效) 难(需黑名单或版本号)
水平扩展 需共享会话存储 天然友好
典型场景 传统网页应用 接口服务、移动端、微服务

图 6-2 两种身份保持机制的请求往来

图 6-2 两种身份保持机制的请求往来

💡 选型不必二选一到底:网页端用会话、开放接口用令牌,同一应用内两套过滤器按路径分流是常见做法,配置里配多条安全链即可。

实战:用方法级注解收口一次越权事故

背景:内部系统里"查看工资单"接口只做了前端隐藏入口,没有服务端校验,员工改地址栏就能看别人的工资单——水平越权的教科书案例。操作三步。第一步,给接口补方法级注解:

@PreAuthorize("#userId == principal.id or hasRole('HR')") public SalaryVO salaryOf(Long userId) { return repo.findByUserId(userId); // 参数与本人一致,或 HR 角色才放行 }

第二步,用两个测试账号验证:普通员工查自己通过、查别人被拒返回 403;HR 账号查任意人通过。第三步,回归前端:隐藏入口只是体验优化,安全校验永远以服务端为准。结果:越权路径封死。解读:路径规则写不了"参数等于本人"这种条件,这正是方法级注解的存在理由;条件里引用参数与当前身份的能力,让授权从"哪类接口"细化到"哪条数据"。

变式:把注解临时删掉、只在过滤器层做登录校验,重放越权请求——请求畅通无阻,直观体会"认证不等于授权,登录不等于越权检查"。顺带一提,这类注解在单元测试里可以脱容器验证:直接调用会绕过代理,必须用安全测试工具或切片测试模拟调用,这一点在第 8 章会接上。

会话与令牌之外的一点提醒

身份体系的演进常常走着走着就混合了:网页端会话、移动端令牌、第三方回调各来一套,此时最容易出问题的不是某一套机制,而是它们对"当前用户"的表达不一致——同一概念在三处有三种字段名与三种取值口径。治理办法是尽早统一内部的主体模型,各端机制只做"解析凭证、还原主体"这一件事。另一个常被忽视的点:退出登录在令牌体系下只是客户端删凭证,服务端毫不知情,若业务要求"退出即失效",得靠短有效期加刷新令牌的组合,把损失窗口压到分钟级。安全章节学完的标准自检:拿自己的项目回答"哪个请求未经认证、哪个数据只做了前端隐藏",两问答得出,本章就没白学。

本节要点回顾

  • 两道授权关口:路径规则拦大类,方法注解管条件,后者能引用参数与返回值
  • 方法级授权走代理,自调用失效规则照旧成立
  • 会话易失效难扩展,令牌易扩展难失效,按产品形态选
  • 两种机制可并存,多条安全链按路径分流

读完自测

三问收束:为什么水平越权必须靠方法级注解而路径规则无能为力;改密码后要"全端下线",令牌方案要补什么设计;自调用场景下方法注解失效的根因与第 4 章哪一节呼应。最后一问答得上来,说明全书的代理主线已经串起来了。

补一个与本节主题相关的数字感:会话的有效期、令牌的有效期、刷新窗口三者构成身份体系的时间参数,默认值往往不适配业务——支付类应用把令牌压到分钟级加刷新,内容类应用放宽到天级。上线前把这三个参数列出来过一遍,是安全评审里耗时最短、性价比最高的一项检查。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U