访问控制
anyRequest()
anyRequest()
表示匹配所有的请求,一般情况下此方法都会使用,设置全部内容都需要进行认证
// 推荐将anyRequest()放在最后使用
.anyRequest().authenticated();
1
2
2
antMatchers()
public C antMatchers(String... antPatterns)
1
参数是可变参数,每个参数都是一个 ant
表达式,用于匹配 URL 规则
规则如下
?
:匹配一个字符*
:匹配 0 个或多个字符**
:匹配 0 个或多个目录
在实际项目中经常需要进行放行所有静态资源,下面演示放行 js 文件夹下所有脚本文件。
.antMatchers("/js/**", "/css/**").permitAll();
1
还有一种配置方式只要是 .js
文件都放行
.antMatchers("/**/*.js").permitAll();
1
regexMatchers()
regexMatchers()
运行我们使用正则表达式匹配路径
public C regexMatchers(String... regexPatterns)
1
放行所有 .png
文件
.regexMatchers(".+[.]png$").permitAll();
1
方法定义
public C regexMatchers(HttpMethod method, String... regexPatterns)
1
允许我们在指定路径的同时指定请求的方式
基于角色或权限进行访问控制
hasAuthority 方法
如果当前的主体具有指定的权限,则返回 true,否则返回 false。
注意:此方法只能设置一个权限,多次设置后面的会覆盖前面的;使用逗号不会进行分割而是看作一个整体。
修改配置类
// 设置指定的请求需要的权限
http.authorizeRequests()
.antMatchers("/admin")
.hasAuthority("admin"); // 如果用户没有admin权限则无法访问
1
2
3
4
2
3
4
Controller
@GetMapping("/admin")
public String admin(){
return "hello admin";
}
1
2
3
4
2
3
4
hasAnyAuthority 方法
如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回 true
注意:只需要拥有指定的权限中的一个权限就行,不需要全部拥有。
修改配置类
// #####设置指定的请求需要的权限, 多个权限之间可以使用逗号分割#####
http.authorizeRequests()
.antMatchers("/anyAdmin")
.hasAnyAuthority("admin", "manager"); // 如果用户拥有admin或者manager权限中的一个就可以访问
1
2
3
4
2
3
4
Controller
@GetMapping("/anyAdmin")
public String anyAdmin(){
return "hello anyAdmin";
}
1
2
3
4
2
3
4
hasRole 方法
如果用户具备给定角色就允许访问,否则出现 403。 如果当前主体具有指定的角色,则返回 true。
底层源码
private static String hasRole(String role) {
Assert.notNull(role, "role cannot be null");
Assert.isTrue(!role.startsWith("ROLE_"),
() -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
return "hasRole('ROLE_" + role + "')";
}
1
2
3
4
5
6
2
3
4
5
6
可以看到最后返回时会拼接 ROLE_
前缀,因此我们在给用户设置角色时需要加上这个前缀,与权限进行区分。
给用户添加角色
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
/**
* 作用: 进行用户名的判断
* 运行流程: 这个方法会传入一个用户名, 我们可以根据用户名进行查询, 并将查询结果交给SpringSecurity,
* SpringSecurity会帮我们进行密码的判断操作, 我们需要负责用户名的判断。
* 总结: SpringSecurity会根据我们返回的UserDetails对象进行密码的判断, 但是并不会判断用户名,
* 因此我们需要编写用户名的判断操作
* @param username 登录时传入的用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 调用userMapper方法, 根据用户名查询数据库
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
User user = userMapper.selectOne(queryWrapper);
// 判断
if(user == null){
throw new UsernameNotFoundException("用户不存在");
}
// 设置用户拥有的权限, 多个权限之间使用逗号分割
List<GrantedAuthority> auths =
AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_sale,ROLE_admin");
return new org.springframework.security.core.userdetails.User(user.getUsername(),
new BCryptPasswordEncoder().encode(user.getPassword()),
// BCryptPasswordEncoder在对密码进行对比时, 一定时拿明文和密文进行对别,
// 所有我们这里进行手动加密,因为我们数据库中存的就是明文,
// 如果数据库中存的是密文就不需要我们手动进行加密了;
auths);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
修改配置类
注意配置时是不需要添加 ROLE_
,因为上述的底层代码会自动添加与之进行匹配。
// 设置指定的请求需要的角色, 与hasAuthority类似
http.authorizeRequests()
.antMatchers("/role")
.hasRole("sale,admin");
1
2
3
4
2
3
4
Controller
@GetMapping("/role")
public String role(){
return "hello role";
}
1
2
3
4
2
3
4
hasAnyRole 方法
表示用户具备任何一个条件都可以访问。
给用户添加角色:
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
/**
* 作用: 进行用户名的判断
* 运行流程: 这个方法会传入一个用户名, 我们可以根据用户名进行查询, 并将查询结果交给SpringSecurity,
* SpringSecurity会帮我们进行密码的判断操作, 我们需要负责用户名的判断。
* 总结: SpringSecurity会根据我们返回的UserDetails对象进行密码的判断, 但是并不会判断用户名,
* 因此我们需要编写用户名的判断操作
* @param username 登录时传入的用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 调用userMapper方法, 根据用户名查询数据库
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
User user = userMapper.selectOne(queryWrapper);
// 判断
if(user == null){
throw new UsernameNotFoundException("用户不存在");
}
// 设置用户拥有的权限, 多个权限之间使用逗号分割
List<GrantedAuthority> auths =
AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_sale,ROLE_admin");
return new org.springframework.security.core.userdetails.User(user.getUsername(),
new BCryptPasswordEncoder().encode(user.getPassword()),
// BCryptPasswordEncoder在对密码进行对比时, 一定时拿明文和密文进行对别,
// 所有我们这里进行手动加密,因为我们数据库中存的就是明文,
// 如果数据库中存的是密文就不需要我们手动进行加密了;
auths);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
修改配置类
// 设置指定的请求需要的角色, 与hasAuthority类似
http.authorizeRequests()
.antMatchers("/role")
.hasRole("sale,admin");
1
2
3
4
2
3
4
Controller
@GetMapping("/anyRole")
public String anyRole(){
return "hello anyRole";
}
1
2
3
4
2
3
4
基于 IP 地址进行访问控制
配置
http.authorizeRequests()
.antMatchers("/hasIpAddress")
.hasIpAddress("192.168.200.130");
1
2
3
2
3
Controller
@GetMapping("/hasIpAddress")
public String hasIpAddress(){
return "hello hasIpAddress";
}
1
2
3
4
2
3
4
自定义 403 页面
修改 SecurityConfigTest 配置类
@Override
protected void configure(HttpSecurity http) throws Exception {
// 自定义403访问页面
http.exceptionHandling().accessDeniedPage("/unauth.html");
}
1
2
3
4
5
2
3
4
5
unauth.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>对不起, 您没有权限访问此页面</h1>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
上次更新: 2024/03/29, 08:56:31