您的位置 首页 java

Spring Security 配置授权(1):限制访问-在端点上应用授权规则

根据用户角色限制所有端点的访问

在本节中,我们将讨论基于角色限制对端点的访问。角色是指用户可以做什么的另一种方式( 图 5 )。您在现实世界的应用程序中也可以找到这些内容,因此理解角色以及角色和权限之间的区别非常重要。在本节中,我们将应用几个使用角色的示例,以便您了解应用程序使用角色的所有实际场景,以及如何为这些情况编写配置。

图 5

角色是粗粒度的。具有特定角色的每个用户只能执行该角色授予的操作。在授权中应用此原理时,将根据用户在系统中的用途允许请求。只有具有特定角色的用户才能调用特定的端点。

Spring Security 将权限理解为我们对其应用限制的细粒度特权。角色就像用户的徽章。这为用户提供了一组操作的特权。有些应用程序总是向特定用户提供相同的权限组。想象一下,在您的应用程序中,一个用户既可以只有读权限,也可以拥有全部权限:读、写和删除权限。在这种情况下,可以认为那些只能阅读的用户具有名为 READER 的角色,而其他用户具有角色 ADMIN。拥有 ADMIN 角色意味着应用程序授予您读、写、更新和删除权限。你可以有更多的角色。例如,如果在某些时候请求指定您还需要一个只允许读写的用户,那么您可以为您的应用程序创建名为 MANAGER 的第三个角色。

你给角色起的名字就像给权限起的名字一样——这是你自己的选择。我们可以说,与权限相比,角色是粗粒度的。无论如何,在背后,角色在 Spring Security 中使用相同的接口表示,即 GrantedAuthority 。定义角色时,其名称应该以 ROLE_ 前缀开头。在实现级别,该前缀指定角色和权限之间的区别。在下一个清单中,看一下我对上一个示例所做的更改。

清单 9 为用户设置角色

 @Configuration
public class ProjectConfig  extends  WebSecurityConfigurerAdapter {

  @Bean
  public UserDetailsService userDetailsService() {
    var manager = new InMemoryUserDetailsManager();

    var user1 = User.withUsername("tom")
                    .password("12345")
                    .authorities("ROLE_ADMIN") // 有了ROLE_前缀,GrantedAuthority 现在代表一个角色。
                    .build();

    var user2 = User.withUsername("jane")
                    .password("12345")
                    .authorities("ROLE_MANAGER")
                    .build();

    manager.createUser(user1);
    manager.createUser(user2);

    return manager;
  }

  // Omitted code

}  

要为用户角色设置约束,可以使用以下方法之一:

  • hasRole() 接收应用程序授权请求的角色名作为参数。
  • hasAnyRole() 接收应用程序批准请求的角色名作为参数。
  • access() 使用 Spring 表达式指定应用程序授权请求的一个或多个角色。至于角色,您可以使用 hasRole() hasAnyRole() 作为 SpEL 表达式。

正如您所看到的,这些名称类似于上一篇文章 ( ) 中介绍的方法。我们以同样的方式使用这些配置,但是要为角色而不是权限应用配置。我的建议也类似:使用 hasRole() hasAnyRole() 方法作为您的第一个选项,并且只有在前两个不适用时才使用 access() 。在下一个清单中,您可以看到 configure() 方法现在是什么样子。

清单 10 将应用配置为仅接受来自管理员的请求

 @Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

  // Omitted code

  @Override
  protected  void  configure(HttpSecurity http) throws  Exception  {
    http.httpBasic();

    http.authorizeRequests()
         .any Request ().hasRole("ADMIN"); //现在指定了允许访问端点的角色。注意,这里没有出现 ROLE_ 前缀
  }
}  

注意

要观察的关键是,我们仅使用 ROLE_ 前缀来声明角色。 但是,当我们使用角色时,我们仅按其名称进行操作。

在测试应用程序时,您应该注意到用户 Tom 可以访问端点,而 Jane 接收到HTTP 403 Forbidden。要调用用户 注意 Tom 的端点,请使用:

 curl -u tom:12345   

响应体:

 Hello!  

要调用用户 Jane 的端点,请使用:

 curl -u jane:12345   

响应体:

 {
  "status":403,
  "error":"Forbidden",
  "message":"Forbidden",
  "path":"/hello"
}  

当使用 User 构建器类构建用户时,就像我们在本节的示例中所做的那样,您可以使用 roles() 方法指定角色。此方法创建 GrantedAuthority 对象,并自动将 ROLE_ 前缀添加到您提供的名称中。

注意

确保为 roles() 方法提供的参数不包含 ROLE_ 前缀。如果该前缀无意中包含在 role() 形参中,该方法将抛出异常。简而言之,在使用 authorities() 方法时,要包含 ROLE_ 前缀。当使用 roles() 方法时,不要包含 ROLE_ 前缀。

在下面的清单中,您可以看到在基于角色设计访问时使用 roles() 方法而不是 authorities() 方法的正确方式。

清单 11 使用 roles() 方法设置角色

 @Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

  @Bean
  public UserDetailsService userDetailsService() {
    var manager = new InMemoryUserDetailsManager();

    var user1 = User.withUsername("tom")
                    .password("12345")
                    .roles("ADMIN") //指定用户的角色
                    .build();

    var user2 = User.withUsername("jane")
                    .password("12345")
                    .roles("MANAGER")
                    .build();

    manager.createUser(user1);
    manager.createUser(user2);

        return manager;
  }

  // Omitted code
}  

关于 access() 方法的更多信息

在前面一文和本节中,您学习了如何使用 access() 方法应用引用权限和角色的授权规则。通常,在应用程序中,授权限制与权限和角色相关。但是重要的是要记住 access() 方法是 泛型 的。在我所提供的示例中,我主要是教你如何将它应用于权威和角色,但在实践中,它接收任何SpEL表达式。它不需要与权限和角色相关。

一个简单的示例是将对端点的访问配置为只允许在中午12点之后访问。要解决类似的问题,你可以使用下面的 SpEL 表达式:

 T(java.time.LocalTime).now().isAfter(T(java.time.LocalTime).of(12, 0))  

有关 SpEL 表达式的更多信息,请参阅 Spring Framework 文档:

 #expressions  

我们可以说,通过 access() 方法,基本上可以实现任何类型的规则。有无限的可能。只是不要忘记,在应用程序中,我们总是努力保持语法尽可能简单。只有当你没有其他选择时,你才会把你的配置复杂化。

限制对所有端点的访问

在本节中,我们将讨论对所有请求的访问限制。您在前面文章学习了通过使用 permitAll() 方法,可以允许对所有请求的访问。您还了解了可以基于权限和角色应用访问规则。但是您还可以拒绝所有请求。 denyAll() 方法正好与 permitAll() 方法相反。在下一个清单中,您可以看到如何使用 denyAll() 方法。

清单 12 使用 denyAll() 方法限制对端点的访问

 @Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

  // Omitted code

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic();

    http.authorizeRequests()
         .anyRequest().denyAll(); //使用 denyAll() 限制所有人的访问
  }
}  

那么,在哪里可以使用这种限制呢?您不会发现它和其他方法使用得一样多,但是在某些情况下,需求使它成为必要的。让我向你展示几个例子来澄清这一点。

让我们假设您有一个端点作为路径变量接收电子邮件地址。你想要的是允许以 .com 结尾的可变地址值的请求。您不希望应用程序接受任何其他格式的电子邮件地址。(在下一节中,您将了解如何基于路径和 HTTP 方法对一组请求甚至对路径变量应用限制。)对于这个需求,您可以使用一个 正则表达式 来对匹配您的规则的请求进行分组,然后使用 denyAll() 方法来指示应用程序拒绝所有这些请求 ( 图 6 )。

图 6

当用户使用以 .com 结尾的参数值调用端点时,应用程序将接受该请求。 当用户调用端点并提供以 .net 结尾的电子邮件地址时,应用程序将拒绝该呼叫。 要实现这种行为,可以对所有参数值不以 .com 结尾的端点使用 denyAll() 方法。

您还可以想象一个如图 7 所示的应用程序。一些服务实现了应用程序的用例,可以通过调用在不同路径上可用的端点来访问这些用例。但是要调用端点,客户端需要请求另一个服务,我们可以将其称为网关。在这个架构中,有两个这种类型的独立服务。在图 7 中,我将它们称为网关 A 和网关 B 。如果客户端想访问 /products 路径,则请求网关 A。但是对于 /articles 路径,客户端必须请求网关 B。每个网关服务都被设计为拒绝对这些路径不服务的其他路径的所有请求。这个简化的场景可以帮助您轻松地理解 denyAll() 方法。在生产应用程序中,您可以在更复杂的体系结构中发现类似的情况。

生产中的应用程序面临各种架构要求,有时可能看起来很奇怪。 框架必须为您可能遇到的任何情况提供所需的灵活性。 因此, denyAll() 方法与您在本系列中学习的所有其他选项一样重要。

配置授权之限制访问总结:

  • 授权是应用程序决定是否允许通过身份认证的请求的过程。 授权始终在身份认证之后进行。
  • 您可以根据已认证用户的权限和角色来配置应用程序如何授权请求。
  • 在您的应用程序中,您还可以指定未经身份认证的用户可以执行某些请求。
  • 你可以使用 denyAll() 方法将你的应用配置为拒绝任何请求,或者使用 permitAll() 方法允许任何请求。

文章来源:智云一二三科技

文章标题:Spring Security 配置授权(1):限制访问-在端点上应用授权规则

文章地址:https://www.zhihuclub.com/193602.shtml

关于作者: 智云科技

热门文章

网站地图