Spring Security配置权限:应用限制
@[toc](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
用户Jane调用端点/hello
用户Jane调用端点/ciao
用户John调用端点/ciao
上面的配置类还有点瑕疵,这样默认除过/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而不进行身份验证
使用GET请求调用路径/a而不进行身份验证时
可以看到,此时是不能访问该路径的,因为我们在配置中让其必须身份验证之后才能访问Get请求的/a路径。
使用有效的身份进行验证
但是用户John被禁止调用路径/a/b,所以用他的凭据对这个调用进行身份验证会生成403 Forbidden
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匹配器测试
使用/hello/结尾的路径调用端点
调用http://localhost:8080/hello并进行身份验证。
调用http://localhost:8080/hello/并进行身份验证
结果都是我们所期待的。但是如果使用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
不进行身份验证访问http://localhost:8080/hello/
注意,此时居然访问成功了,所以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,如下所示:
调用端点/video端点并验证用户John的FR区域和French语言,如下所示:
下面使用用户Jane调用,该用户具有高级权限
http://localhost:8080/video/us/en
http://localhost:8080/video/fr/fr
正则表达式是功能强大的工具,可以使用它们指向任何指定需求的路径。但是由于正则表达式难以阅读,并且可能变得很长,因此它们是我们的最后选择。只有当MVC和Ant表达式不能为所面临的问题提供解决方案时,才使用它们。
4、总结
- 在实际场景中,经常会对不同的请求应用不同的授权规则。
- 可以指定基于路径和HTTP方法配置授权规则的请求。为此,需要使用匹配器方法,它有3种风格:MVC、Ant和正则表达式。
- MVC和Ant匹配器是类似的,通常可以选择其中一个选项指向应用授权限制的请求。
- 当需求太复杂而无法使用Ant或MVC表达式解决问题时,则可以使用更强大的正则表达式实现它们。
- 点赞
- 收藏
- 关注作者
评论(0)