spring security和oauth2 (spring security和shiro哪个好)

使用Ant匹配器选择授权请求

在本节中,我们将讨论 Ant 匹配器,用于选择应用程序应用授权规则的请求。因为 Spring 借用了 MVC 表达式来从 Ant 匹配路径到端点,所以您可以使用 Ant 匹配器的语法与您在 《Spring Security 配置授权之应用限制(1)》节中看到的语法相同。但在这一节中,我将向您展示一个技巧——您应该知道一个重要的区别。因此,我建议您使用 MVC 匹配器而不是 Ant 匹配器。然而,在过去,我看到过 Ant 匹配器在应用程序中大量使用。也因为这个原因,我想让你们知道这个区别。现在您仍然可以在生产应用程序中找到 Ant 匹配器,这使得它们很重要。使用 Ant 匹配器时的三个方法是:

  • antMatchers(HttpMethod method, String patterns) -- 允许您指定应用限制的 HTTP 方法和引用路径的 Ant 模式。如果希望对同一组路径的不同HTTP方法应用不同的限制,则此方法非常有用。
  • antMatchers(String patterns) -- 如果只需要应用基于路径的授权限制,则使用起来更简单、更容易。这些限制自动适用于任何 HTTP 方法。
  • antMatchers(HttpMethod method) , 这相当于 ant Matchers(httpMethod, “/**”) -- 允许您引用特定的 HTTP 方法而忽略路径。

应用这些的方式类似于前上一篇中的 MVC 匹配器。同样,我们用于引用路径的语法是相同的。那么有什么不同呢? MVC 匹配器确切地指的是 Spring 应用程序如何理解将请求与控制器动作匹配。有时候,Spring 可以解释多个路径来匹配相同的操作。下面是我最喜欢的一个简单但对安全性有重大影响的例子:同一个动作的任何路径(例如,/hello)都可以被 Spring 解释,如果您在路径后面附加另一个 / 。在本例中,/hello 和 /hello/ 调用相同的方法。如果你使用 MVC 匹配器并为 /hello 路径配置安全性,它会自动用相同的规则保护 /hello/ 路径。这是巨大的!开发人员如果不知道这一点,并且使用 Ant 匹配器,可能会在没有注意到的情况下使路径不受保护。正如您可以想象的那样,这将为应用程序创建一个重大的安全漏洞。

让我们用一个例子来测试这个行为。下面的清单展示了如何定义控制器。

清单 12 控制器类中的 /hello 端点的定义

@RestController
public class HelloController {

  @GetMapping("/hello")
  public String hello() {
    return "Hello!";
  }
}

清单 13 描述了配置类。在本例中,我使用一个 MVC 匹配器来定义 /hello 路径的授权配置。(接下来我们将其与 Ant 匹配器进行比较。)对该端点的任何请求都需要身份认证。我在示例中省略了 UserDetailsService 和 PasswordEncoder 的定义,因为它们与上篇中的清单 7 中相同。

清单 13 使用MVC匹配器的配置类

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

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

    http.authorizeRequests()
         .mvcMatchers( "/hello")
           .authenticated();
  }
}

如果启动应用程序并测试它,就会发现 /hello 和 /hello/ 路径都需要身份认证。这可能是你期望发生的事情。下面的代码片段显示了使用 cURL 对这些路径发出的请求。调用未认证的 /hello 端点看起来像这样:

curl http://localhost:8080/hello

响应:

{
  "status":401,
  "error":"Unauthorized",
  "message":"Unauthorized",
  "path":"/hello"
}

使用 /hello/ 路径调用 /hello 端点(在末尾有一个 / ),未验证的样子如下:

curl http://localhost:8080/hello/

响应:

{
  "status":401,
  "error":"Unauthorized",
  "message":"Unauthorized",
  "path":"/hello"
}

像 Jane 那样调用 /hello 端点身份验证:

curl -u jane:12345 http://localhost:8080/hello

响应体:

Hello!

然后使用 /hello/ 路径调用 /hello 端点(在末尾有一个 / ),并验证 Jane 的身份,如下所示:

curl -u jane:12345 http://localhost:8080/hello/

响应体:

Hello!

所有这些响应可能都是您所期望的。但是让我们看看如果我们将实现更改为使用 Ant 匹配器会发生什么。如果您只是更改配置类来为相同的表达式使用 Ant 匹配器,那么结果也会更改。如前所述,应用程序没有为 /hello/ 路径应用授权配置。事实上,Ant 匹配器对模式精确地应用给定的 Ant 表达式,但对微妙的 Spring MVC 功能一无所知。在这种情况下,/hello 没有作为 Ant 表达式应用于 /hello/ 路径。如果还想保护 /hello/ 路径,则必须单独添加它或编写一个匹配它的 Ant 表达式。下面的清单显示了在配置类中使用 Ant 匹配器而不是 MVC 匹配器所做的更改。

清单 14 使用 Ant 匹配器的配置类

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

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

    http.authorizeRequests()
          .antMatchers( "/hello").authenticated();
  }
}

下面的代码片段提供了使用 /hello 和 /hello/ 路径调用端点的结果。要调用未经身份认证的 /hello 端点,请使用:

curl http://localhost:8080/hello

响应:

{
  "status":401,
  "error":"Unauthorized",
  "message":"Unauthorized",
  "path":"/hello"
}

要调用未经身份认证的 /hello 端点,但使用路径 /hello/ (在末尾有一个 / ),请使用:

curl http://localhost:8080/hello/

响应:

Hello!

重要

再说一遍:我推荐并更喜欢 MVC 匹配器。使用 MVC 匹配器,可以避免 Spring 将路径映射到动作的一些风险。这是因为您知道为授权规则解释路径的方式与 Spring 本身为将路径映射到端点而解释这些路径的方式相同。当您使用 Ant 匹配器时,请谨慎使用,并确保您的表达式确实匹配您需要应用授权规则的所有内容。

沟通和知识共享的影响

我总是鼓励以各种可能的方式分享知识:书籍、文章、会议、视频等等。有时,即使是一个简短的讨论也可以提出一些问题,从而推动巨大的改进和改变。我将通过几年前我讲授的关于 Spring 的课程中的一个故事来说明我的意思。

培训是为一组为特定项目工作的中级开发人员设计的。它与 Spring Security 并没有直接的关系,但是在某种程度上,我们开始使用匹配器方法作为培训的一部分。

我开始使用 MVC 匹配器配置端点授权规则,而没有首先向参与者传授关于 MVC 匹配器的知识。我认为他们已经在他们的项目中使用了这些;我不认为必须先解释它们。当我进行配置并传授我正在做的事情时,一位与会者问了一个问题。我还记得那位女士害羞的声音说,“你能介绍一下你正在使用的这些 MVC 方法吗?”我们正在用一些 Ant 方法配置我们的端点安全性。”

然后我意识到,与会者可能并不知道他们在使用什么。我是对的。他们确实使用了 Ant 匹配器,但并不理解这些配置,很可能只是机械地使用它们。复制粘贴编程是一种有风险的方法,不幸的是,这种方法经常被使用,特别是被初级开发人员使用。在不了解它的功能之前,千万不要使用它!

当我们讨论这个新主题时,这位女士在他们的实现中发现了 Ant 匹配器被错误应用的情况。训练结束时,他们的团队安排了一个完整的冲刺,以验证和纠正这些错误,这些错误可能会导致他们的应用程序出现非常危险的漏洞。

使用正则表达式匹配器选择授权请求

在本节中,我们将讨论正则表达式 ( regex )。您应该已经知道什么是正则表达式,但您不需要是这方面的专家。在 https://www.regular-expressions.info/books.html 上推荐的任何书籍都是很好的资源,你可以从那里更深入地了解这个主题。为了编写正则表达式,我也经常使用像 https://regexr.com/ 这样的在线生成器 (图 1)

springsecurity授权异常,springsecurity权限控制配置

图 1

让你的猫在键盘上玩并不是生成正则表达式 ( regex ) 的最佳解决方案。要了解如何生成正则表达式,可以使用像 https://regexr.com/ 这样的在线生成器。

您在前面了解到,在大多数情况下,可以使用 MVC 和 Ant 语法来引用应用授权配置的请求。然而,在某些情况下,您可能有更特殊的需求,您无法用 Ant 和 MVC 表达式解决这些需求。这种需求的一个例子是: “当路径包含特定的符号或字符时,拒绝所有请求。” 对于这些场景,需要使用功能更强大的表达式,如 regex。

您可以使用正则表达式来表示字符串的任何格式,因此它们提供了无限的可能性。但它们的缺点是难以阅读,即使应用于简单的场景。出于这个原因,您可能更喜欢使用 MVC 或 Ant 匹配器,只有当您没有其他选择时才会退回到 regex。你可以使用以下两种方法来实现正则表达式匹配器:

  • regexMatchers(HttpMethod method, String regex) -- 指定应用限制的 HTTP 方法和引用路径的正则表达式。如果希望对同一组路径的不同 HTTP 方法应用不同的限制,则此方法非常有用。
  • regexMatchers(String regex) -- 如果只需要应用基于路径的授权限制,则使用起来更简单、更容易。这些限制自动适用于任何HTTP方法。

为了证明正则表达式匹配器是如何工作的,让我们通过一个示例将它们付诸实践:构建一个为用户提供视频内容的应用程序。呈现视频的应用程序通过调用端点 /video/{country}/{language} 获取其内容。出于本例的考虑,应用程序在用户发出请求的两个路径变量中接收国家和语言。我们认为,如果请求来自美国、加拿大或英国,或者使用英语,任何经过身份认证的用户都可以看到视频内容。

我们需要保护的端点有两个路径变量,如下面的清单所示。这使得使用 Ant 或 MVC 匹配器实现需求变得复杂。

清单 15 控制器类的端点的定义

@RestController
public class VideoController {

  @GetMapping("/video/{country}/{language}")
  public String video(@PathVariable String country,
                      @PathVariable String language) {
    return "Video allowed for " + country + " " + language;
  }
}

对于单个路径变量的条件,我们可以直接在 Ant 或 MVC 表达式中编写正则表达式。我们在上一节中提到了这样一个例子,但是当时我没有深入讨论它,因为我们没有讨论 regex。

让我们假设您拥有端点 /email/{email}。您希望仅对以 .com 结尾的地址作为电子邮件参数值发送的请求应用使用匹配器的规则。在这种情况下,编写一个 MVC 匹配器,如下面的代码片段所示。

http.authorizeRequests()
    .mvcMatchers("/email/{email:.*(.+@.+\\.com)}")
       .permitAll()
    .anyRequest()
       .denyAll();

如果测试这样的限制,您会发现应用程序只接受以 .com 结尾的电子邮件。例如,要调用端点 jane@example.com,可以使用以下命令:

curl http://localhost:8080/email/jane@example.com

响应体:

Allowed for email jane@example.com

要将端点调用到 jane@example.net ,可以使用以下命令:

curl http://localhost:8080/email/jane@example.net

响应体:

{
  "status":401,
  "error":"Unauthorized",
  "message":"Unauthorized",
  "path":/email/jane@example.net
}

这是相当简单的,并且更清楚地说明了为什么我们很少遇到正则表达式匹配器。但是,正如我前面所说的,需求有时是复杂的。你会发现使用正则表达式匹配器更方便,当你发现像下面这样的东西:

  • 包含电话号码或电子邮件地址的所有路径的特定配置
  • 具有特定格式的所有路径的特定配置,包括通过所有路径变量发送的内容

回到我们的正则匹配器示例 :当您需要编写更复杂的规则,最终引用更多路径模式和多个路径变量值时,编写正则匹配器会更容易。在清单 16 中,您可以找到配置类的定义,它使用正则表达式匹配器来解决 /video/{country}/{language} 路径的要求。我们还添加了两个具有不同权限的用户来测试实现。

清单 16 使用正则表达式匹配器的配置类

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

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

    var u1 = User.withUsername("john")
                 .password("12345")
                 .authorities("read")
                 .build();

    var u2 = User.withUsername("jane")
                .password("12345")
                .authorities("read", "premium")
                .build();

    uds.createUser(u1);
    uds.createUser(u2);

    return uds;
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return NoOpPasswordEncoder.getInstance();
  }

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

    http.authorizeRequests()
        .regexMatchers(".*/(us|uk|ca)+/(en|fr).*") // 我们使用正则表达式来匹配只需要对用户进行身份认证的路径。
            .authenticated()
        .anyRequest()
            .hasAuthority("premium"); //配置用户需要具有 premium 访问权限的其他路径

  }
}

运行和测试端点可以确认应用程序正确应用了授权配置。用户 使 Tom 可以调用国家代码 US和语言 en 的端点,但由于我们配置的限制,他不能调用国家代码 FR 和语言 FR 的端点。调用 /video 端点并验证美国地区的用户 Tom,如下所示:

curl -u tom:12345 http://localhost:8080/video/us/en

响应体:

Video allowed for us en

调用 /video 端点并验证 FR 区域的用户 Tom,如下所示:

curl -u tom:12345 http://localhost:8080/video/fr/fr

响应体:

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

用户 Jane 具有 premium 权限,两次调用都成功。第一个调用,

curl -u jane:12345 http://localhost:8080/video/us/en

响应体:

Video allowed for us en

第二次调用:

curl -u jane:12345 http://localhost:8080/video/fr/fr

响应体:

Video allowed for fr fr

regex 是功能强大的工具。您可以使用它们来引用任何给定需求的路径。但是由于 regex 很难阅读,而且可能会变得很长,因此它们应该是您的最后选择。只有当 MVC 和 Ant 表达式不能为你的问题提供解决方案时,才使用它们。

在本节中,我使用了我能想到的最简单的示例,因此所需的正则表达式很短。但是对于更复杂的场景,正则表达式可能会变得更长。当然,您会发现专家说任何正则表达式都很容易阅读。例如,用于匹配电子邮件地址的正则表达式可能类似于下一个代码片段中的正则表达式。你能轻松地阅读和理解它吗?

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

配置授权之应用限制总结

  • 在实际场景中,您经常为不同的请求应用不同的授权规则。
  • 您可以根据路径和 HTTP 方法指定为其配置授权规则的请求。为此,您可以使用匹配方法,它有三种风格: MVC、Ant 和 regex。
  • MVC和 Ant 匹配器是类似的,通常情况下,您可以选择其中一个选项来引用您应用授权限制的请求。
  • 当需求过于复杂,无法用 Ant 或 MVC 表达式解决时,可以使用更强大的 regex 来实现它们。

接下来我们将学习讨论 Spring Security 的过滤器。希望大家关注、评论、转发!