5

🍃【Spring专题】「实战系列」Spring Security技术实战之通过注解表达式控制方法权限

 2 years ago
source link: https://my.oschina.net/liboware/blog/5439832
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Spring Security权限控制机制

Spring Security中可以通过表达式控制方法权限,其中有四个支持使用表达式的注解,分别是@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter。其中前两者可以用来在方法调用前或者调用后进行权限检查,后两者可以用来对集合类型的参数或者返回值进行过滤。

启动Security机制的配置

它们的定义能够对我们的方法的调用产生影响我们需要设置global-method-security元素的pre-post-annotations=”enabled”,默认为disabled。

xml配置方式

<security:global-method-security pre-post-annotations="disabled"/>

JavaConfig配置方式

类上增加@EnableGlobalMethodSecurity(securedEnabled = true)注解,secureEnabled默认为false,使其赋值为true才能使用相关注解。

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class XXXApplication {
    public static void main(String[] args) {
        SpringApplication.run(XXXApplication.class, args);
    }
}

@PreAuthorize和@PostAuthorize进行访问控制

@PreAuthorize 注解,顾名思义是进入方法前的权限验证,@PreAuthorize 声明这个方法所需要的权限表达式,例如:@PreAuthorize("hasAuthority('sys:dept:delete')"),@PreAuthorize 表示访问方法或类在执行之前先判断权限,一般都是使用这个注解,注解的参数和access()方法参数取值相同,都是权限表达式。若有多个权限,可用逗号隔开。

根据这个注解所需要的权限,再和当前登录的用户角色所拥有的权限对比,如果用户的角色权限集Set中有这个权限,则放行;没有,拒绝

@GetMapping("/query")
//以下几个注解都表示ADMIN用户具有访问"/query"接口的权限
@PreAuthorize("hasAnyRole('ADMIN')")
@Secured("ROLE_ADMIN")//以ROLE_开头,不能缺少
@PostAuthorize("hasAnyRole('ADMIN')")
 public String query(){
        return "querySuccess";
 }

定义相关的接口上

public interface UserService {
    public boolean ifhaveuser(String username,String password);
    List<User> findAllUsers();
    User findById(int id);
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    void updateUser(User user);
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    void deleteUser(int id);
}
@Service
public class UserServiceImpl implements UserService {undefined

   @PreAuthorize("hasRole('ROLE_ADMIN')")
   public void addUser(User user) {undefined
      System.out.println("addUser................" + user);
   }

   @PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
   public User find(int id) {undefined
      System.out.println("find user by id............." + id);
      return null;
   }
}

在上面的代码中我们定义了只有拥有角色ROLE_ADMIN的用户才能访问adduser()方法,而访问find()方法需要有ROLE_USER角色或ROLE_ADMIN角色。使用表达式时我们还可以在表达式中使用方法参数。

PreAuthorize的EL表达式

public class UserServiceImpl implements UserService {undefined
   /**
    * 限制只能查询Id小于10的用户
   */
   @PreAuthorize("#id<10")
   public User find(int id) {undefined
      System.out.println("find user by id........." + id);
      return null;
   }

   /**
    * 限制只能查询自己的信息
    */
   @PreAuthorize("principal.username.equals(#username)")
   public User find(String username) {undefined
      System.out.println("find user by username......" + username);
      return null;
   }

   /**
    * 限制只能新增用户名称为abc的用户
    */
   @PreAuthorize("#user.name.equals('abc')")
   public void add(User user) {undefined
      System.out.println("addUser............" + user);
   }
}

在上面代码中我们定义了调用find(int id)方法时,只允许参数id小于10的调用;调用find(String username)时只允许username为当前用户的用户名;定义了调用add()方法时只有当参数user的name为abc时才可以调用。

  • @Secured使用时必须要加上ROLE_前缀,不可省略。@PreAuthorize 也可加上ROLE_前缀,不过其可以省略。

用户的角色权限Set,是什么时候存入的,其流程如下

PostAuthorize的EL表达式

  • @PostAuthorize表示方法或类执行结束后判断权限,很少使用。

有时候可能你会想在方法调用完之后进行权限检查,这种情况比较少,但是如果你有的话,Spring Security也为我们提供了支持,通过@PostAuthorize可以达到这一效果。使用@PostAuthorize时我们可以使用内置的表达式returnObject表示方法的返回值。

下面这一段示例代码:

@PostAuthorize("returnObject.id%2==0")
   public User find(int id) {undefined
      User user = new User();
      user.setId(id);
      return user;
}

上面这一段代码表示将在方法find()调用完成后进行权限检查,如果返回值的id是偶数则表示校验通过,否则表示校验失败,将抛出AccessDeniedException。

注意的是@PostAuthorize是在方法调用完成后进行权限检查,它不能控制方法是否能被调用,只能在方法调用完成后检查权限决定是否要抛出AccessDeniedException。

@PreFilter和@PostFilter进行过滤

使用@PreFilter和@PostFilter可以对集合类型的参数或返回值进行过滤。使用@PreFilter和@PostFilter时,Spring Security将移除使对应表达式的结果为false的元素。

 @PostFilter("filterObject.id%2==0")
  public List<User> findAll() {undefined
      List<User> userList = new ArrayList<User>();
      User user;
      for (int i=0; i<10; i++) {undefined
         user = new User();
         user.setId(i);
         userList.add(user);
      }
      return userList;
   }

上述代码表示将对返回结果中id不为偶数的user进行移除。filterObject是使用@PreFilter和@PostFilter时的一个内置表达式,表示集合中的当前对象。当@PreFilter标注的方法拥有多个集合类型的参数时,需要通过@PreFilter的filterTarget属性指定当前@PreFilter是针对哪个参数进行过滤的。

https://www.jianshu.com/p/3ddd2b31cb86


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK