spring-security——web权限方案:用户授权

本文最后更新于:2022年5月20日 下午

摘要:介绍了SpringSecurity基于角色和用户权限进行访问控制,在没有权限时设置拒绝访问页面,以及基于注解开发进行访问控制。

基于角色或权限进行访问控制

hasAuthority方法

如果当前主题具有指定的权限,则返回true,否则返回false

  1. 在配置类设置当前访问地址有哪些权限
  • .antMatchers(“/test/hello”) 匹配路径 “/test/hello”
  • .hasAnyAuthority(“admin”) 需要admin权限才能访问
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.shg.securitydemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
* @author: shg
* @create: 2022-05-16 1:44 下午
*/
@Configuration
public class SecurityConfig3 extends WebSecurityConfigurerAdapter {

@Qualifier("userDetailsService2")
@Autowired
private UserDetailsService userDetailsService;

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 自定义自己编写的登录页面
.loginPage("/login.html")
.loginProcessingUrl("/user/login") // 登录访问路径
.defaultSuccessUrl("/test/index").permitAll() // 登录成功后跳转页面
.and().authorizeRequests()
.antMatchers("/", "/user/login").permitAll() // 设置哪些路径可以直接访问不需要认证
// hasAuthority 方法
.antMatchers("/test/hello").hasAuthority("admin") // 设置路径 "/test/hello" 需要admin权限才能访问
.anyRequest().authenticated()
.and().csrf().disable(); // 关闭csrf防护
}
}

  1. 在UserDetailsService的实现类中,设置返回的User对象具有的权限
  • 设置root用户具有root权限
  • 设置其他用户具有admin权限
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
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.shg.securitydemo.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.shg.securitydemo.entity.User;
import com.shg.securitydemo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* @author: shg
* @create: 2022-05-16 1:53 下午
*/
@Service(value = "userDetailsService2")
public class MyUserDetailsService2 implements UserDetailsService {

@Autowired
private UserMapper userMapper;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 调用userMapper中的方法,根据用户名查询数据库
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 设置查询条件
wrapper.eq("name", username);
User user = userMapper.selectOne(wrapper);
// 判断是否查到用户
if (user == null) { // 数据库没有用户名,认证失败
throw new UsernameNotFoundException("用户名不存在!");
}
List<GrantedAuthority> authorities;
if ("root".equals(user.getName())) {
authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("root");
} else {
authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
}
return new org.springframework.security.core.userdetails.User(user.getName(),
new BCryptPasswordEncoder().encode(user.getPassword()),
authorities);
}
}

  1. 启动项目查看访问**”/test/hello”**结果

该路径需要admin权限才能访问

  • 登录root用户返回错误页面
登录root用户返回的错误页面
  • 登录admin用户正常访问
登录admin用户正常访问

hasAnyAuthority方法

如果当前的主体有任何提供的权限,返回rue

  1. 在【TestController】中添加一个方法
1
2
3
4
@GetMapping("share")
public String share() {
return "hello admin or root";
}
  1. 在配置文件中设置访问**/test/share**的权限
1
2
// hasAnyAuthority 方法
.antMatchers("/test/share").hasAnyAuthority("admin", "root")

hasRole方法

如果当前主题具有指定的角色,返回true

  1. 在【TestController】中添加一个方法
1
2
3
4
@GetMapping("role")
public String role() {
return "hello hasRole";
}
  1. 修改【MyUserDetailsService2】,给用户返回的权限中添加ROLE_user
  • 需要加上**ROLE_**前缀
1
2
3
4
5
6
List<GrantedAuthority> authorities;
if ("root".equals(user.getName())) {
authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("root,ROLE_root");
} else {
authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_admin");
}
  1. 在配置类中设置访问**/test/role**需要的角色
1
2
// hasRole 方法
.antMatchers("/test/role").hasRole("admin")

hasAnyRole方法

与hasAnyAuthority方法方法类似,不同在于在设置角色时需要加上**ROLE_**前缀

自定义没有权限返回的页面

  1. 在/resources/static路径下添加一个403.html
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>403</title>
</head>
<body>
<h1>没有访问权限!</h1>
</body>
</html>
  1. 在配置类中配置返回的错误页面

在**configure(HttpSecurity http)**方法中添加如下代码

1
2
// 配置没有访问权限是返回的页面路径
http.exceptionHandling().accessDeniedPage("/403.html");
  1. 使用root账号登录访问**/test/role**路径
image-20220516155654331

基于注解用户授权

@Secured注解

判断是否具有角色,这里的匹配的自负需要添加铅坠ROLE_

  1. 在启动类加上**@EnableGlobalMethodSecurity**注解,并开启@Secured
  • 设置注解@EnableGlobalMethodSecurity中的securedEnabled = true
1
2
3
4
5
6
7
8
@SpringBootApplication
@MapperScan(value = {"com.shg.securitydemo.mapper"})
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityDemoApplication.class, args);
}
}
  1. 在【TestController】中添加一个方法
1
2
3
4
5
@GetMapping("/anno")
@Secured(value = {"ROLE_root"})
public String annotation() {
return "hello annotation";
}

@PreAuthorize

进入方法前的权限验证

  1. 在启动类加上**@EnableGlobalMethodSecurity**注解,并开启prePostEnabled
  • 设置注解@EnableGlobalMethodSecurity中的prePostEnabled = true
1
2
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityDemoApplication {
  1. 在【TestController】中添加一个方法
1
2
3
4
5
@GetMapping("/pre")
@PreAuthorize(value = "hasAuthority('admin')")
public String pre() {
return "hello preAuthorize";
}

@PostAuthorize

访问方法后验证权限

  1. 在启动类加上**@EnableGlobalMethodSecurity**注解,并开启prePostEnabled
  • 设置注解@EnableGlobalMethodSecurity中的prePostEnabled = true
1
2
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityDemoApplication {
  1. 在【TestController】中添加一个方法
  • 在方法中打印添加一句打印方法信息
1
2
3
4
5
6
@GetMapping("/post")
@PostAuthorize(value = "hasAuthority('admin')")
public String post() {
System.out.println("TestController.post");
return "hello PostAuthorize";
}
  1. 分别使用root和admin账户登录查看效果
  • 使用root账户登录,页面现实没有反问权限,但是控制台会输出内容,说明该方法被访问了
root账户登录返回页面 root用户登录控制台输出内容
  • 使用admin账户登录,可以正常访问,且控制台也会输出内容
admin账户登录 admin用户登录控制台输出内容

@PostFilter

对方法返回数据进行过滤

@PreFilter

对传入方法数据进行过滤


spring-security——web权限方案:用户授权
https://shgang97.github.io/posts/spring-security-web1-ce251f78ecfb/
作者
shgang
发布于
2022年5月16日
更新于
2022年5月20日
许可协议