Spring Security配置权限:应用限制


1、匹配器说明

要选择应用授权配置的请求,可以使用匹配器方法。Spring Security提供了3种类型的匹配方法。

  • MVC匹配器:将MVC表达式用于路径以便选择端点。

  • Ant匹配器:将Ant表达式用于路径以便选择端点。

  • regex匹配器:将正则表达式(regex)用于路径以便选择端点。

2、使用匹配器方法选择端点

首先看一个简单的示例,我们要创建一个暴露两个端点的应用程序,这两个端点是/hello和/ciao。我们希望确保只有ADMIN角色的用户才能调用/hello端点。类似地,只有MANAGER角色的用户才能调用/ciao端点。

2.1 控制器类的定义

 @RestController
 public class HelloController {
 
     @GetMapping("/hello")
     public String hello() {
         return "Hello!";
    }
 
     @GetMapping("/ciao")
     public String ciao() {
         return "Ciao!";
    }
 
 }

2.2 配置类的定义

 @Configuration
 public class ProjectConfig extends WebSecurityConfigurerAdapter {
 
 
     @Override
     @Bean
     public UserDetailsService userDetailsService() {
         var manager = new InMemoryUserDetailsManager();
 
         var user1 = User.withUsername("john")
                .password("12345")
                .roles("ADMIN")
                .build();
 
         var user2 = User.withUsername("jane")
                .password("12345")
                .roles("MANAGER")
                .build();
 
         manager.createUser(user1);
         manager.createUser(user2);
 
         return manager;
    }
 
     @Bean
     public PasswordEncoder passwordEncoder() {
         return NoOpPasswordEncoder.getInstance();
    }
 
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.httpBasic();
 
         http.authorizeRequests()
            //只有当用户具有ADMIN角色时才能调用路径/hello
                .mvcMatchers("/hello").hasRole("ADMIN")
            //只有当用户具有Manager角色时才能调用路径/ciao
                .mvcMatchers("/ciao").hasRole("MANAGER")
    }
 
 }

该配置类中声明了一个InMemoryUserDetailsManager作为UserDetailsService实例,并且添加了两个具有不同角色的用户。用户John具有ADMIN角色,而Jane具有MANAGER角色。为了指定只有具有ADMIN角色的用户才能在授权请求时调用端点/hello,需要使用mvcMatchers()方法。

2.3 测试

用户John调用端点/hello

Spring Security配置权限:应用限制

用户Jane调用端点/hello

Spring Security配置权限:应用限制

用户Jane调用端点/ciao

Spring Security配置权限:应用限制

用户John调用端点/ciao

Spring Security配置权限:应用限制

上面的配置类还有点瑕疵,这样默认除过/hello和/ciao之外的其他路径都是可以被访问的,即使是未经身份验证的用户。

2.4 补充:让所有经过身份验证的用户都可以访问其他请求

 @Override
 protected void configure(HttpSecurity http) throws Exception {
     http.httpBasic();
 
     http.authorizeRequests()
            .mvcMatchers("/hello").hasRole("ADMIN")
            .mvcMatchers("/ciao").hasRole("MANAGER")
            .anyRequest().authenticated();
 }

当使用匹配器指向请求时,规则的顺序应该是从特殊到一般。这就是为什么在更具体的匹配器方法(例如mvcMatches())之前不能调用anyRequest()方法的原因。

3、三种匹配器

3.1 使用MVC匹配器选择用于授权的请求

这个匹配器使用标准的MVC语法指向路径。改语法与我们在编写带有@RequestMapping、@GetMapping、@PostMapping等注解的端点映射时所使用的语法相同。可声明用于MVC匹配器的两种方法如下。

  • mvcMatchers(HttpMethod method,String… patterns):允许指定要应用限制的HTTP方法和路径。如果希望对同一路径的不同HTTP方法应用不同的限制,此方法非常有用。

  • mvcMatchers(String… patterns):如果只需应用基于路径的授权限制,那么这个方法使用起来会更加简单。这些限制可以自动应用于该路径一起使用的任何HTTP方法。

在默认情况下,Spring Security应用了防止跨站点请求伪造(CSRF)的保护。这里为了更加简单,并且能够调用所有端点,包括那些POST、PUT、或DELETE公开的端点,需要在onfigure()方法中禁用CSRF保护。

注意:这里禁用CSRF保护只是为了更好的专注于mvc匹配器方法,实际开发中不要这样做。

 http.csrf().disable();

3.1.1 为其配置授权的4个端点的定义

  • /a使用HTTP方法GET

  • /a使用HTTP方法POST

  • /a/b使用HTTP方法GET

  • /a/b/c使用HTTP方法GET

 @RestController
 public class TestController {
 
     @PostMapping("/a")
     public String postEndpointA() {
         return "Works!";
    }
 
     @GetMapping("/a")
     public String getEndpointA() {
         return "Works!";
    }
 
     @GetMapping("/a/b")
     public String getEnpointB() {
         return "Works!";
    }
 
     @GetMapping("/a/b/c")
     public String getEnpointC() {
         return "Works!";
    }
 }

我们还需要几个具有不同角色的用户。为了保持简单,将继续使用InMemoryUserDetailsManager。


3.1.2 UserDetailsService定义

 @Configuration
 public class ProjectConfig extends WebSecurityConfigurerAdapter {
 
     @Override
     @Bean
     public UserDetailsService userDetailsService() {
         var manager = new InMemoryUserDetailsManager();
 
         var user1 = User.withUsername("john")
                .password("12345")
                .roles("ADMIN")
                .build();
 
         var user2 = User.withUsername("bill")
                .password("12345")
                .roles("MANAGER")
                .build();
 
         manager.createUser(user1);
         manager.createUser(user2);
 
         return manager;
    }
 
     @Bean
     public PasswordEncoder passwordEncoder() {
         return NoOpPasswordEncoder.getInstance();
    }
 
    //...
 }

3.1.3 用于第一个场景/a的授权配置

 @Configuration
 public class ProjectConfig extends WebSecurityConfigurerAdapter {
 
     @Override
     @Bean
     public UserDetailsService userDetailsService() {
         var manager = new InMemoryUserDetailsManager();
 
         var user1 = User.withUsername("john")
                .password("12345")
                .roles("ADMIN")
                .build();
 
         var user2 = User.withUsername("bill")
                .password("12345")
                .roles("MANAGER")
                .build();
 
         manager.createUser(user1);
         manager.createUser(user2);
 
         return manager;
    }
 
     @Bean
     public PasswordEncoder passwordEncoder() {
         return NoOpPasswordEncoder.getInstance();
    }
 
     //用于第一个场景/a的授权配置
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.httpBasic();
 
         http.authorizeRequests()
             //对于使用HTTP Get方法调用的路径/a的请求,应用程序需要对用户进行身份验证
                .mvcMatchers(HttpMethod.GET, "/a").authenticated()
             //允许任何人通过HTTP POST方法调用路径/a的请求
                .mvcMatchers(HttpMethod.POST, "/a").permitAll()
             //拒绝对其他任何路径的其他任何请求
                .anyRequest().denyAll();
 
         //暂时禁用CSRF保护,以启用使用HTTP POST方法对/a路径的调用
         http.csrf().disable();
    }
 }

3.1.4 调用测试

使用POST请求调用路径/a而不进行身份验证

Spring Security配置权限:应用限制

使用GET请求调用路径/a而不进行身份验证时

Spring Security配置权限:应用限制

可以看到,此时是不能访问该路径的,因为我们在配置中让其必须身份验证之后才能访问Get请求的/a路径。

使用有效的身份进行验证

Spring Security配置权限:应用限制

但是用户John被禁止调用路径/a/b,所以用他的凭据对这个调用进行身份验证会生成403 Forbidden

Spring Security配置权限:应用限制

3.1.5 其他配置

如果希望确保相同的规则适用于以/a/b开始的所有路径请求。为了实现这一点,需要使用**操作符(Spring MVC从Ant中借用了路径匹配语法)。

对多个路径的配置类进行的更改

   @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.httpBasic();
 
         http.authorizeRequests()
                .mvcMatchers( "/a/b/**").authenticated()
                .anyRequest().permitAll();
 
         http.csrf().disable();
    }

用于使用MVC匹配器进行路径匹配器的通用表达式如下表

表达式 描述
/a 进匹配路径/a
/a/* 操作符*会替换一个路径名。在这种情况下,它将匹配/a/b或/a/c,而不是/a/b/c
/a/** 操作符**会替换多个路径名。在这种情况下,/a以及/a/b和/a/b/c都是这个表达式的匹配项
/a/{param} 这个表达式适用于具有给定路径参数的路径/a
/a/{param:regex} 只有当参数的值与给定正则表达式匹配时,此表达式才应用于具有给定路径参数的路径/a

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

使用Ant匹配器的3种方法如下:

  • antMatchers(HttpMethod method,String patterns):允许指定应用限制的HTTP方法和指向路径的Ant模式。如果希望对同一组路径的不同HTTP方法应用不同的限制,则此方法非常有用。

  • antMatchers(String patterns):如果只需要应用基于路径的授权限制,则这一方法使用起来更加简单。这些限制会自动适用于任何HTTP方法。

  • antMatchers(HttpMethod method):他等同于antMatchers(httpMethod,”/**”),允许特定的HTTP方法,而不考虑路径。

MVC匹配器与Ant匹配器哪个好用?

MVC匹配器指的是Spring应用程序如何理解将请求与控制器相匹配。有时多个路径可以被Spring解析为匹配相同的操作。

例如:如果再路径之后添加一个/,那么指向相同操作的任何路径(例如/hello)都可以由Spring解析。在这种情况下,/hello和/hello/会调用相同的方法。如果使用MVC匹配器并且为/hello路径配置安全性,则它会自动使用相同的规则保护/hello/路径/。这会产生巨大的影响!开发人员如果不知道这一点,并且使用Ant匹配器,则可能会在毫不知情的情况下让路径不受保护。

下面以一个示例说明。

3.2.1 控制器类种/hello端点的定义

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

3.2.2 使用MVC匹配器的配置类

 @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.httpBasic();
 
         http.authorizeRequests()
                .mvcMatchers( "/hello").authenticated();
                 //.antMatchers( "/hello").authenticated();
    }

3.2.3 mvc匹配器测试

http://localhost:8080/hello

Spring Security配置权限:应用限制

使用/hello/结尾的路径调用端点

http://localhost:8080/hello/

Spring Security配置权限:应用限制

调用http://localhost:8080/hello并进行身份验证。

Spring Security配置权限:应用限制

调用http://localhost:8080/hello/并进行身份验证

Spring Security配置权限:应用限制

结果都是我们所期待的。但是如果使用Ant匹配器结果就会改变。实际上,Ant匹配器会为模式精确地应用给定的Ant表达式,但它无法触及Spring MVC的精细功能。在本示例中,/hello不会作为Ant表达式应用于/hello路径。如果还想保护/hello/路径,则必须单独添加它,或者编写一个匹配它的Ant表达式。


3.2.4 使用Ant匹配器的配置类

  @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.httpBasic();
 
         http.authorizeRequests()
                .antMatchers( "/hello").authenticated();
    }

3.2.4 Ant匹配器测试

不进行身份验证访问http://localhost:8080/hello

Spring Security配置权限:应用限制

不进行身份验证访问http://localhost:8080/hello/

Spring Security配置权限:应用限制

注意,此时居然访问成功了,所以Ant匹配器的粒度比较细,会出现我们不期待的结果。还需要为该路径再配置限制,所以平时使用MVC匹配器就可以了。

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

可以使用正则表达式表示字符串的任何格式,因此它们提供了无限的可能性。但缺点是难以阅读,即使应用于简单的场景也是如此。

实现正则表达式匹配器的两种方法如下:

  • regexMatchers(HttpMethod method,String regex):同时指定应用限制的HTTP方法和指向路径的正则表达式。如果希望对同一组路径的不同HTTP方法应用不同的限制,则此方法非常有用。

  • regexMatchers(String regex):如果只需要应用基于路径的授权限制,该方法使用起来会更加简单。这些限制将会自动适用于任何HTTP方法。

    以一个简单的示例说明

3.3.1 控制器类端点的定义

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

3.3.2 使用正则表达式匹配器的配置类

 @Configuration
 public class ProjectConfig extends WebSecurityConfigurerAdapter {
 
     @Override
     @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");
    }
 }

3.3.3 测试

运行和测试端点就可以确认应用程序是否正确地应用了授权配置。用户John可以使用国家代码US和语言en来调用端点,但由于配置的限制,他不能调用国家代码FR和语言fr的端点

调用/video端点并验证US地区和English语言的用户John,如下所示:

Spring Security配置权限:应用限制

调用端点/video端点并验证用户John的FR区域和French语言,如下所示:

Spring Security配置权限:应用限制

下面使用用户Jane调用,该用户具有高级权限

http://localhost:8080/video/us/en

Spring Security配置权限:应用限制

http://localhost:8080/video/fr/fr

Spring Security配置权限:应用限制

正则表达式是功能强大的工具,可以使用它们指向任何指定需求的路径。但是由于正则表达式难以阅读,并且可能变得很长,因此它们是我们的最后选择。只有当MVC和Ant表达式不能为所面临的问题提供解决方案时,才使用它们。

4、总结

  • 在实际场景中,经常会对不同的请求应用不同的授权规则。

  • 可以指定基于路径和HTTP方法配置授权规则的请求。为此,需要使用匹配器方法,它有3种风格:MVC、Ant和正则表达式。

  • MVC和Ant匹配器是类似的,通常可以选择其中一个选项指向应用授权限制的请求。

  • 当需求太复杂而无法使用Ant或MVC表达式解决问题时,则可以使用更强大的正则表达式实现它们。



原文始发于微信公众号(全栈开发那些事):Spring Security配置权限:应用限制

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/91749.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!