SpringSecurity配置权限:限制访问
@toc
1、概述
这里我们将讨论授权。授权是系统决定已识别的客户端是否有权限访问所请求得资源得过程。
在Spring Security中,一旦应用程序结束身份验证流程,它就会将请求委托给一个授权过滤器。该过滤器会根据所配置得授权规则来允许或拒绝请求。
这里将按照以下步骤讨论授权得所有必要细节。
- 了解权限是什么,并基于用户的权限对所有端点应用访问规则。
- 了解如何按角色对权限进行分组,以及如何基于用户的角色应用授权规则。
2、基于权限和角色限制访问
这里将介绍授权和角色得概念。可以使用它们保护应用程序得所有端点。其中不同的用户具有不同的权利。根据用户拥有的权利,他们只能执行特定的操作。应用程序会将权利作为权限和角色来提供。
在身份验证过程中,UserDetailsService会获取关于用户的所有详情,其中包括权限。应用程序在成功地对用户进行身份验证之后,将使用由GrantedAuthority接口表示的权限对用户进行授权。
下面代码显示了GrantedAuthority接口地定义。==权限就是用户可以使用系统资源执行的操作。一个权限具有一个名称,对象的getAuthority()行为会将该名称作为String返回。在定义自定义授权规则时,可以使用权限的名称。授权规则通常是“Jane被允许删除(delete)产品记录”或“John被允许读取(read)文档记录”。在这种情况下,删除和读取就是被授予的权限。==
public interface GrantedAuthority extends Serializable {
String getAuthority();
}
UserDetails是Spring Security中描述用户的接口,它有一个GrantedAuthority实例的集合,如上图所示。可以允许用户拥有一个或多个权限。getAuthorities()方法会返回GrantedAuthority()实例的集合。可以在如下代码中查看这个方法。之所以实现这个方法,是为了让它返回授予用户的所有权限。身份验证结束之后,权限就会已登录用户的详细信息的一部分,应用程序可以使用它授予权限。
2.1 基于用户权限限制所有端点的访问
可以使用下面这些方法配置访问。
- hasAuthority():仅接收应用程序为其配置限制的一个权限作为参数。只有拥有该权限的用户才能调用端点。
- hasAnyAuthority():可以接收应用程序为其配置限制的多个权限。用户必须拥有至少一个指定的权限才能发出请求。
- access():为配置访问提供了无限的可能性,因为应用程序会基于Spring Expression Language(SpEL)构建授权规则。但是它会让代码更难阅读和调试。
2.1.1 项目依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.1.2 添加一个端点来测试授权配置
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello!";
}
}
2.1.3 声明UserDetailsService并指定用户
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
public UserDetailsService userDetailsService() {
var manager = new InMemoryUserDetailsManager();
var user1 = User.withUsername("john")
.password("12345")
.authorities("READ")
.build();
var user2 = User.withUsername("jane")
.password("12345")
.authorities("WRITE")
.build();
//用户是由UserDetailsService添加和管理的
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()
.anyRequest()
.hasAuthority("WRITE"); //指定用户可以访问端点的条件
}
}
在该配置类中,我们声明了一个InMemoryUserDetailsManager作为UserDetailsService,并添加了两个用户,John和Jane,他们会由这个实例管理。
authorizeRequests()方法允许我们继续在端点上指定授权规则。anyRequest()方法表明,该规则适用于所有请求,无论使用的是URL还是HTTP方法。permitAll()方法允许访问所有请求,无论是否经过身份验证。这里我们配置的是只有拥有WRITE权限的用户才能访问所有端点。
测试:
john用户调用失败是因为他没有WRITE权限。
2.1.4 应用hasAnyAuthority方法
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
http.authorizeRequests()
.anyRequest()
.hasAnyAuthority("WRITE", "READ");//允许具有WRITE和READ权限的用户请求
}
测试:
这次两个用户都可以访问了。
2.1.5 使用access()方法配置端点访问
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
//使用WRITE权限对来自用户的请求进行授权
http.authorizeRequests().anyRequest().access("hasAuthority('WRITE')");
}
3、基于用户角色限制所有端点的访问
角色是表示用户做什么的另一种方式。SpringSecurity将权限视为对其应用限制的细粒度权利。角色就像是用户的徽章。它们为用户提供一组操作的权利。有些应用程序总是为特定用户提供相同的权限组。
为角色提供的名称与为权限提供的名称类似,这取决于我们自己。与权限相比,可以认为角色是细粒度的。无论如何,在后台,角色都是使用Spring Security中的相同接口表示的,即GrantedAuthority。在定义角色时,其名称应该以ROLE_前缀开头。在实现层面,这个前缀表明了角色和权限之间的区别。
3.1 为用户设置角色
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
public UserDetailsService userDetailsService() {
var manager = new InMemoryUserDetailsManager();
var user1 = User.withUsername("john")
.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;
}
//...
}
要为用户设置角色,可以使用以下方法之一。
- hasRole():作为参数接收的角色名,应用程序会为其请求授权。
- hasAnyRole():作为参数接收的角色名,应用程序会批准其请求。
- access():使用Spring表达式指定一个或多个角色,应用程序会为其请求授权。就角色而言,可以使用hasRole()或hasAnyRole()作为SpEL表达式。
3.2 配置应用程序以便只接受来自管理员的请求
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
http.authorizeRequests().anyRequest().hasRole("ADMIN");
}
使用ROLE_前缀是为了声明角色,但是在使用角色时,只会用到其名称。
3.3 测试
在测试应用程序时,会发现用户John可以访问端点,而Jane则会接收到HTTP 403 Forbidden。
John调用:
Jane调用:
在使用User构建器类构建用户时,可以使用roles()方法指定角色。此方法会创建GrantedAuthority对象,并自动将ROLE_前缀添加到所提供的名称中。
==要确保为roles()方法提供的参数不包含ROLE_前缀。如果该前缀无意中包含在role()参数中,则该方法会抛出异常。简而言之,在使用authorities()方法时,要包含前缀ROLE_。而在使用roles()方法时,不要包含ROLE_前缀。==
3.4 使用roles()方法设置角色
@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().anyRequest().hasRole("ADMIN");
}
}
4、限制对所有端点的访问
可以使用permitAll()方法允许对所有请求的访问。denyAll()方法正好与permitAll()方法相反。
4.1 使用denyAll()方法限制对端点的访问
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
//...
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
//使用denyAll()限制对每一个人的访问
http.authorizeRequests().anyRequest().denyAll();
}
}
5、总结
- 授权是应用程序决定是否允许通过身份验证的请求的过程。授权总是在身份验证之后发生。
- 可以根据经过身份验证的用户的权限和角色来配置如何授权请求。
- 在应用程序中,还可以指定某些请求可以用于未经身份验证的用户。
- 可以配置应用程序使用denyAll()方法拒绝所有请求,或者使用permitAll()方法允许任何请求。
- 点赞
- 收藏
- 关注作者
评论(0)