- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第1部分 Spring 的核心
- 第1章 Spring 之旅
- 第2章 装配 Bean
- 第3章 高级装配
- 第4章 面向切面的 Spring
- 第2部分 Web 中的 Spring
- 第5章 构建 Spring Web 应用程序
- 第6章 渲染 Web 视图
- 第7章 Spring MVC 的高级技术
- 第8章 使用 Spring Web Flow
- 第9章 保护 Web 应用
- 第3部分 后端中的 Spring
- 第10章 通过 Spring 和 JDBC 征服数据库
- 第11章 使用对象-关系映射持久化数据
- 第12章 使用 NoSQL 数据库
- 第13章 缓存数据
- 第14章 保护方法应用
- 第4部分 Spring 集成
- 第15章 使用远程服务
- 第16章 使用 Spring MVC 创建 REST API
- 第17章 Spring消息
- 第18章 使用 WebSocket 和 STOMP 实现消息功能
- 第19章 使用 Spring 发送 Email
- 第20章 使用 JMX 管理 Spring Bean
- 第21章 借助 Spring Boot 简化 Spring 开发
14.2.1 表述方法访问规则
到目前为止,我们已经看到@Secured和@RolesAllowed能够限制只有用户具备所需的权限才能触发方法的执行。但是,这两个注解的不足在于它们只能基于用户授予的权限来做出决策。
Spring Security还提供了两个注解,@PreAuthorize和@PostAuthorize,它们能够基于表达式的计算结果来限制方法的访问。在定义安全限制方面,表达式带了极大的灵活性。通过使用表达式,只要我们能够想象得到,就可以定义任意允许访问或不允许访问方法的条件。
@PreAuthorize和@PostAuthorize之间的关键区别在于表达式执行的时机。@PreAuthorize的表达式会在方法调用之前执行,如果表达式的计算结果不为true的话,将会阻止方法执行。与之相反,@PostAuthorize的表达式直到方法返回才会执行,然后决定是否抛出安全性的异常。
在方法调用前验证权限
@PreAuthorize乍看起来可能只是添加了SpEL支持的@Secured和@RolesAllowed。实际上,你可以基于用户所授予的角色,使用@PreAuthorize来限制访问:
如果按照这种方式的话,@PreAuthorize相对于@Secured和@RolesAllowed并没有什么优势。如果用户具有ROLE_SPITTER角色的话,允许方法调用。否则,将会抛出安全性异常,方法也不会执行。
但是,@PreAuthorize的功能并不限于这个简单例子所展现的。@PreAuthorize的String类型参数是一个SpEL表达式。借助于SpEL表达式来实现访问决策,我们能够编写出更高级的安全性约束。例如,Spittr应用程序的一般用户只能写140个字以内的Spittle,而付费用户不限制字数。
虽然@Secured和@RolesAllowed在这里无能为力,但是@PreAuthorize注解恰好能够适用于这种场景:
表达式中的#spittle部分直接引用了方法中的同名参数。这使得Spring Security能够检查传入方法的参数,并将这些参数用于认证决策的制定。在这里,我们深入到Spitter的文本内容中,保证不超过Spittr标准用户的长度限制。如果是付费用户,那么就没有长度限制了。
在方法调用之后验证权限
在方法调用之后验证权限并不是比较常见的方式。事后验证一般需要基于安全保护方法的返回值来进行安全性决策。这种情况意味着方法必须被调用执行并且得到了返回值。
例如,假设我们想对getSpittleById()方法进行保护,确保返回的Spittle对象属于当前的认证用户。我们只有得到Spittle对象之后,才能判断它是否属于当前用户。因此,getSpittleById()方法必须要先执行。在得到Spittle之后,如果它不属于当前用户的话,将会抛出安全性异常。
除了验证的时机之外,@PostAuthorize与@PreAuthorize的工作方式差不多,只不过它会在方法执行之后,才会应用安全规则。此时,它才有机会在做出安全决策时,考虑到返回值的因素。
例如,要保护上面描述的getSpittleById()方法,我们可以按照如下的方式使用@PostAuthorize注解:
为了便利地访问受保护方法的返回对象,Spring Security在SpEL中提供了名为returnObject的变量。在这里,我们知道返回对象是一个Spittle对象,所以这个表达式可以直接访问其spittle属性中的username属性。
在对比表达式双等号的另一侧,表达式到内置的principal对象中取出其username属性。principal是另一个Spring Security内置的特殊名称,它代表了当前认证用户的主要信息(通常是用户名)。
在Spittle对象所包含Spitter中,如果username属性与principal的username属性相同,这个Spittle将返回给调用者。否则,会抛出一个AccessDeniedException异常,而调用者也不会得到Spittle对象。
有一点需要注意,不像@PreAuthorize注解所标注的方法那样,@PostAuthorize注解的方法会首先执行然后被拦截。这意味着,你需要小心以保证如果验证失败的话不会有一些负面的结果。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论