Board logo

标题: 使用 Spring Security 保护 Web 应用的安全(4) [打印本页]

作者: look_w    时间: 2018-9-19 19:52     标题: 使用 Spring Security 保护 Web 应用的安全(4)

服务层方法保护之前章节中介绍的是在 URL 这个粒度上的安全保护。这种粒度的保护在很多情况下是不够的。比如相同的 URL 对应的页面上,不同角色的用户所能看到的内容和执行的操作是有可能不同的。在第一个示例应用中,系统中记录了每个员工的工资收入。所有员工都可以查看自己的工资,但是只有员工的直接经理才可以修改员工的工资。这就涉及到对应用中服务层的方法进行相应的权限控制,从而避免安全漏洞。
保护服务层方法涉及到对应用中的方法调用进行拦截。通过 Spring 框架提供的良好面向方面编程(AOP)的支持,可以很容易的对方法调用进行拦截。Spring Security 利用了 AOP 的能力,允许以声明的方式来定义调用方式时所需的权限。中给出了对方法调用进行保护的配置文件示例。
清单 6. 对方法调用进行保护
1
2
3
4
5
6
7
8
9
10
11
<bean id="userSalarySecurity"
   class="org.springframework.security.access.intercept.aspectj.
       AspectJMethodSecurityInterceptor">
   <property name="authenticationManager" ref="authenticationManager" />
   <property name="accessDecisionManager" ref="accessDecisionManager" />
   <property name="securityMetadataSource">
       <value>
           mycompany.service.UserService.raiseSalary=ROLE_MANAGER
       </value>
   </property>
</bean>




如 所示,通过 mycompany.service.UserService.raiseSalary=ROLE_MANAGER声明了 mycompany.service.UserService类的 raiseSalary方法只有具有角色 ROLE_MANAGER的用户才能执行。这就使得只具有角色 ROLE_USER的用户无法调用此方法。
不过仅对方法名称进行权限控制并不能解决另外的一些问题。比如在第一个示例应用中的增加工资的实现是通过发送 HTTP POST 请求到 salary.do这个 URL 来完成的。salary.do对应的控制器 mycompany.controller.SalaryController会调用 mycompany.service.UserService类的 raiseSalary方法来完成增加工资的操作。存在的一种安全漏洞是具有 ROLE_MANAGER角色的用户可以通过其它工具(如 cURL 或 Firefox 扩展 Poster 等)来创建 HTTP POST 请求来更改其它员工的工资。为了解决这个问题,需要对 raiseSalary的调用进行更加细粒度的控制。通过 Spring Security 提供的 AspectJ 支持就可以编写相关的控制逻辑,如 所示。
清单 7. 使用 AspectJ 进行细粒度的控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public aspect SalaryManagementAspect {
   private AspectJMethodSecurityInterceptor securityInterceptor;

   private UserDao userDao;

   pointcut salaryChange(): target(UserService)
       && execution(public void raiseSalary(..)) &&!within(SalaryManagementAspect);

   Object around(): salaryChange() {
       if (this.securityInterceptor == null) {
           return proceed();
       }
       AspectJCallback callback = new AspectJCallback() {
           public Object proceedWithObject() {
               return proceed();
           }
       };
       Object[] args = thisJoinPoint.getArgs();
       String employee = (String) args[0]; // 要修改的员工的用户名
       User user = userDao.getByUsername(employee);
       String currentUser = UsernameHolder.getAuthenticatedUsername(); // 当前登录用户
       if (!currentUser.equals(user.getManagerId())) {
           throw new AccessDeniedException
               ("Only the direct manager can change the salary.");
       }

       return this.securityInterceptor.invoke(thisJoinPoint, callback);
   }
}




如 所示,定义了一个切入点(pointcut)salaryChange和对应的环绕增强。当方法 raiseSalary被调用的时候,会比较要修改的员工的经理的用户名和当前登录用户的用户名是否一致。当不一致的时候就会抛出 AccessDeniedException异常。
在介绍了如何保护方法调用之后,下面介绍如何通过访问控制列表来保护领域对象。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0