这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战
写在前面
上文我们提到安全访问配置,接下来我们讲一下使用配置方法控制访问权限,而这里就不得不提到配置器
多说一句 欢迎大家点击我头像查看Security专栏(设计模式专栏已完结)
使用配置方法控制访问权限
前几篇文章讨论完权限和角色,让我们回到 HTTP 请求和响应过程。我们知道确保访问安全的手段是对访问进行限制,只有那些具有访问权限的请求才能被服务器处理。
那么问题就来了,如何让 HTTP 请求与权限控制过程关联起来呢?答案还是使用 Spring Security 所提供的配置方法。
Spring Security 提供了三种强大的匹配器(Matcher)来实现这一目标,分别是MVC 匹配器、Ant 匹配器以及正则表达式匹配器。
为了验证这些匹配器的配置方法,我们提供了如下所示的一个 Controller:
@RestController
public class TestController {
@GetMapping("/hello_user")
public String helloUser() {
return "Hello User!";
}
@GetMapping("/hello_admin")
public String helloAdmin() {
return "Hello Admin!";
}
@GetMapping("/other")
public String other() {
return "Other!";
}
}
复制代码
同时,我们也创建两个具有不同角色的用户,如下所示:
UserDetails user1 = User.withUsername("yn1")
.password("12345")
.roles("USER")
.build();
UserDetails user2 = User.withUsername("yn2")
.password("12345")
.roles("ADMIN")
.build();
复制代码
接下来,我们将基于这个 Controller 中暴露的各个 HTTP 端点,对三种不同的匹配器一一展开讲解。
MVC 匹配器
MVC 匹配器的使用方法比较简单,就是基于 HTTP 端点的访问路径进行匹配,如下所示:
http.authorizeRequests()
.mvcMatchers("/hello_user").hasRole("USER")
.mvcMatchers("/hello_admin").hasRole("ADMIN");
复制代码
现在,如果你使用角色为“USER”的用户“yn1”来访问“/hello_admin”端点,那么将会得到如下所示的响应:
{
"status":403,
"error":"Forbidden",
"message":"Forbidden",
"path":"/hello_admin"
}
复制代码
显然,MVC 匹配器已经生效了,因为“/hello_admin”端点只有角色为“ADMIN”的用户才能访问。如果你使用拥有“ADMIN”角色的“jianxiang2”来访问这个端点就可以得到正确的响应结果。
你可能会问,我们通过 MVC 匹配器只指定了这两个端点的路径,那剩下的“/other”路径呢?答案就是:没有被 MVC 匹配器所匹配的端点,其访问不受任何的限制,效果相当于如下所示的配置:
http.authorizeRequests()
.mvcMatchers("/hello_user").hasRole("USER")
.mvcMatchers("/hello_admin").hasRole("ADMIN");
.anyRequest().permitAll();
复制代码
显然,这种安全访问控制策略不是特别合理,更好的做法是对那些没有被 MVC 匹配器所匹配到的请求也加以控制,需要进行认证之后才能被访问,实现方式如下所示:
http.authorizeRequests()
.mvcMatchers("/hello_user").hasRole("USER")
.mvcMatchers("/hello_admin").hasRole("ADMIN");
.anyRequest().authenticated();
复制代码
讲到这里,又出现了一个新问题:如果一个 Controller 中存在两个路径完全一样的 HTTP 端点呢?
这种情况是存在的,因为对于 HTTP 端点而言,就算路径一样,只要所使用的 HTTP 方法不同,那就是不同的两个端点。针对这种场景,MVC 匹配器还提供了重载的 mvcMatchers 方法,如下所示:
mvcMatchers(HttpMethod method, String... patterns)
复制代码
这样,我们就可以把 HTTP 方法作为一个访问的维度进行控制,示例代码如下所示:
http.authorizeRequests()
.mvcMatchers(HttpMethod.POST, "/hello").authenticated()
.mvcMatchers(HttpMethod.GET, "/hello").permitAll()
.anyRequest().denyAll();
复制代码
在上面这段配置代码中,如果一个 HTTP 请求使用了 POST 方法来访问“/hello”端点,那么就需要进行认证。而对于使用 GET 方法来访问“/hello”端点的请求则全面允许访问。最后,其余访问任意路径的所有请求都会被拒绝。
同时,如果我们想要对某个路径下的所有子路径都指定同样的访问控制,那么只需要在该路径后面添加“*”号即可,示例代码如下所示:
http.authorizeRequests()
.mvcMatchers(HttpMethod.GET, "/user/*").authenticated()
复制代码
Ant 匹配器
Ant 匹配器的表现形式和使用方法与前面介绍的 MVC 匹配器非常相似,它也提供了如下所示的三个方法来完成请求与 HTTP 端点地址之间的匹配关系:
-
antMatchers(String patterns)
-
antMatchers(HttpMethod method)
-
antMatchers(HttpMethod method, String patterns)
从方法定义上不难明白,我们可以组合指定请求的 HTTP 方法以及匹配的模式,例如:
http.authorizeRequests()
.antMatchers( "/hello").authenticated();
复制代码
虽然,从使用方式上看,Ant 匹配器和 MVC 匹配器并没有什么区别,但在日常开发过程中,我想推荐你使用 MVC 匹配器而不是 Ant 匹配器,原因就在于 Ant 匹配器在匹配路径上有一些风险,主要体现在对于"/"的处理上。为了更好地说明,我举一个简单的例子。
基于上面的这行配置,如果你发送一个这样的 HTTP 请求:
http://localhost:8080/hello
复制代码
你肯定认为 Ant 匹配器是能够匹配到这个端点的,但结果却是:
{
"status":401,
"error":"Unauthorized",
"message":"Unauthorized",
"path":"/hello"
}
复制代码
现在,如果你把 HTTP 请求调整为这样,请注意,我们在请求地址最后添加了一个”/”符号,那么就会得到正确的访问结果:
http://localhost:8080/hello/
复制代码
显然,Ant 匹配器处理请求地址的方式有点让人感到困惑,而 MVC 匹配器则没有这个问题,无论在请求地址最后是否存在“/”符号,它都能完成正确的匹配。
正则表达式匹配器
最后我要介绍的是正则表达式匹配器,同样,它也提供了如下所示的两个配置方法:
-
regexMatchers(HttpMethod method, String regex)
-
regexMatchers(String regex)
使用这一匹配器的主要优势在于它能够基于复杂的正则表达式对请求地址进行匹配,这是 MVC 匹配器和 Ant 匹配器无法实现的,你可以看一下如下所示的这段配置代码:
http.authorizeRequests()
.mvcMatchers("/email/{email:.*(.+@.+\\.com)}")
.permitAll()
.anyRequest()
.denyAll();
复制代码
可以看到,这段代码就对常见的邮箱地址进行了匹配,只有输入的请求是一个合法的邮箱地址才能允许访问。
总结
一旦我们对某个用户设置了对应的权限和角色,那么就可以通过各种配置方法来有效控制访问权限。为此,Spring Security 也提供了 MVC 匹配器、Ant 匹配器以及正则表达式匹配器来实现复杂的访问控制。
好了 关于安全访问配置就讲到这里了,通过两篇文章讲解。还算可以,自己也感觉学到了东西,继续加油。
我们下期再见
弦外之音
感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。也欢迎有问题我们下面评论交流
加油! 我们下期再见!
给大家分享几个我前面写的几篇骚操作
近期评论