方法级别的安全

我们在hello-world-test的基础上,我们新建了一个名为method-security的 Gradle 项目。

本项目用于演示方法级别的安全设置。

build.gradle

修改 build.gradle 文件,让我们的method-security项目成为一个新的项目。

修改内容也比较简单,修改项目名称及版本即可。

  1. jar {
  2. baseName = 'method-security'
  3. version = '1.0.0'
  4. }

编写服务类

创建com.waylau.spring.boot.security.service包,用于放置服务类。创建了 UserService 接口。接口比较简单,主要是用于查询和删除:

  1. public interface UserService {
  2. void removeUser(Long id);
  3. List<User> listUsers();
  4. }

UserServiceImpl 是 UserService 接口的实现,在内存里面模拟了 User 的存储库。

  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. private static final Map<Long,User> userRepository = new ConcurrentHashMap<>();
  4. public UserServiceImpl(){
  5. userRepository.put(1L, new User(1L, "waylau", 30));
  6. userRepository.put(2L, new User(2L,"老卫", 29));
  7. userRepository.put(3L, new User(3L,"doufe", 109));
  8. }
  9. @Override
  10. public void removeUser(Long id) {
  11. userRepository.remove(id);
  12. }
  13. @Override
  14. public List<User> listUsers() {
  15. List<User> users = null;
  16. users = new ArrayList<User>(userRepository.values());
  17. return users;
  18. }
  19. }

控制器

在 UserController 中,增加了删除用户的方法:

  1. @PreAuthorize("hasAuthority('ROLE_ADMIN')") // 指定角色权限才能操作方法
  2. @GetMapping(value = "delete/{id}")
  3. public ModelAndView delete(@PathVariable("id") Long id, Model model) {
  4. userService.removeUser(id);
  5. model.addAttribute("userList", userService.listUsers());
  6. model.addAttribute("title", "删除用户");
  7. return new ModelAndView("users/list", "userModel", model);
  8. }

其中,我们使用了 @PreAuthorize注解。

配置类

在配置类上,我们加上了一个注解 @EnableGlobalMethodSecurity

  1. @EnableWebSecurity
  2. @EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法安全设置
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  4. ......
  5. }

注意:@EnableGlobalMethodSecurity 可以配置多个参数:

  • prePostEnabled :决定 Spring Security 的前注解是否可用@PreAuthorize@PostAuthorize
  • secureEnabled : 决定是否Spring Security的保障注解 @Secured是否可用
  • jsr250Enabled :决定 JSR-250 注解@RolesAllowed等是否可用.

配置方式分别如下:

  1. @EnableGlobalMethodSecurity(securedEnabled = true)
  2. public class MethodSecurityConfig {
  3. // ...
  4. }
  5. @EnableGlobalMethodSecurity(jsr250Enabled = true)
  6. public class MethodSecurityConfig {
  7. // ...
  8. }
  9. @EnableGlobalMethodSecurity(prePostEnabled = true)
  10. public class MethodSecurityConfig {
  11. // ...
  12. }

在同一个应用程序中,可以启用多个类型的注解,但是只应该设置一个注解对于行为类的接口或者类。如果将2个注解同事应用于某一特定方法,则只有其中一个将被应用。

@Secured

此注释是用来定义业务方法的安全配置属性的列表。您可以在需要安全角色/权限等的方法上指定 @Secured,并且只有那些角色/权限的用户才可以调用该方法。如果有人不具备要求的角色/权限但试图调用此方法,将会抛出 AccessDenied 异常。

@Secured 源于 Spring之前版本.它有一个局限就是不支持 Spring EL 表达式。可以看看下面的例子:

如果你想指定AND(和)这个条件,我的意思说deleteUser 方法只能被同时拥有ADMIN & DBA 。但是仅仅通过使用@Secured注解是无法实现的。

但是你可以使用Spring的新的注解@PreAuthorize/@PostAuthorize(支持Spring EL),使得实现上面的功能成为可能,而且无限制。

@PreAuthorize/@PostAuthorize

Spring的 @PreAuthorize/@PostAuthorize 注解更适合方法级的安全,也支持Spring EL 表达式语言,提供了基于表达式的访问控制。

  • @PreAuthorize 注解:适合进入方法前的权限验证, @PreAuthorize 可以将登录用户的角色/权限参数传到方法中。
  • @PostAuthorize 注解:使用并不多,在方法执行后再进行权限验证。

运行

运行项目,我们查看下最终的效果:

方法级别的安全 - 图1

我们用“USER”角色权限可以查看用户列表,但如果想删除用户,则提示“访问拒绝”。

根据权限来显示或者隐藏操作

实际上,如果用户不具备某个操作的权限,那么那个操作按钮就不应该显示出来。我们可以使用sec:authorize属性能达到这个目的。

修改list.html 中的表格:

  1. ......
  2. <table class="table table-hover">
  3. <thead>
  4. <tr>
  5. <td>ID</td>
  6. <td>Age</td>
  7. <td>Name</td>
  8. <td sec:authorize="hasRole('ADMIN')">Operation</td>
  9. </tr>
  10. </thead>
  11. <tbody>
  12. <tr th:if="${userModel.userList.size()} eq 0">
  13. <td colspan="3">没有用户信息!!</td>
  14. </tr>
  15. <tr th:each="user : ${userModel.userList}">
  16. <td th:text="${user.id}">1</td>
  17. <td th:text="${user.age}">11</td>
  18. <td th:text="${user.name}">waylau</a></td>
  19. <td sec:authorize="hasRole('ADMIN')">
  20. <div >
  21. <a th:href="@{'/users/delete/' + ${user.id}}">
  22. <i class="fa fa-times" aria-hidden="true"></i>
  23. </a>
  24. </div>
  25. </td>
  26. </tr>
  27. </tbody>
  28. </table>
  29. ......

我们使用了<td sec:authorize="hasRole('ADMIN')">这样,只要是具备“ADMIN”权限的用户就能看到删除操作列,否则就看不到那一列的删除操作。效果如下:

不具备“ADMIN”权限的用户:

方法级别的安全 - 图2

具备“ADMIN”权限的用户:

方法级别的安全 - 图3