本文最后更新于:2022年5月20日 下午
摘要:介绍了SpringSecurity基于角色和用户权限进行访问控制,在没有权限时设置拒绝访问页面,以及基于注解开发进行访问控制。
基于角色或权限进行访问控制
hasAuthority方法
如果当前主题具有指定的权限,则返回true,否则返回false
- 在配置类设置当前访问地址有哪些权限
- .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;
@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() .antMatchers("/test/hello").hasAuthority("admin") .anyRequest().authenticated() .and().csrf().disable(); } }
|
- 在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;
@Service(value = "userDetailsService2") public class MyUserDetailsService2 implements UserDetailsService {
@Autowired private UserMapper userMapper;
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 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); } }
|
- 启动项目查看访问**”/test/hello”**结果
该路径需要admin权限才能访问
hasAnyAuthority方法
如果当前的主体有任何提供的权限,返回rue
- 在【TestController】中添加一个方法
1 2 3 4
| @GetMapping("share") public String share() { return "hello admin or root"; }
|
- 在配置文件中设置访问**/test/share**的权限
1 2
| .antMatchers("/test/share").hasAnyAuthority("admin", "root")
|
hasRole方法
如果当前主题具有指定的角色,返回true
- 在【TestController】中添加一个方法
1 2 3 4
| @GetMapping("role") public String role() { return "hello hasRole"; }
|
- 修改【MyUserDetailsService2】,给用户返回的权限中添加ROLE_user
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"); }
|
- 在配置类中设置访问**/test/role**需要的角色
1 2
| .antMatchers("/test/role").hasRole("admin")
|
hasAnyRole方法
与hasAnyAuthority方法方法类似,不同在于在设置角色时需要加上**ROLE_**前缀
自定义没有权限返回的页面
- 在/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>
|
- 在配置类中配置返回的错误页面
在**configure(HttpSecurity http)**方法中添加如下代码
1 2
| http.exceptionHandling().accessDeniedPage("/403.html");
|
- 使用root账号登录访问**/test/role**路径
基于注解用户授权
@Secured注解
判断是否具有角色,这里的匹配的自负需要添加铅坠ROLE_
- 在启动类加上**@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); } }
|
- 在【TestController】中添加一个方法
1 2 3 4 5
| @GetMapping("/anno") @Secured(value = {"ROLE_root"}) public String annotation() { return "hello annotation"; }
|
@PreAuthorize
进入方法前的权限验证
- 在启动类加上**@EnableGlobalMethodSecurity**注解,并开启prePostEnabled
- 设置注解@EnableGlobalMethodSecurity中的prePostEnabled = true
1 2
| @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) public class SecurityDemoApplication {
|
- 在【TestController】中添加一个方法
1 2 3 4 5
| @GetMapping("/pre") @PreAuthorize(value = "hasAuthority('admin')") public String pre() { return "hello preAuthorize"; }
|
@PostAuthorize
访问方法后验证权限
- 在启动类加上**@EnableGlobalMethodSecurity**注解,并开启prePostEnabled
- 设置注解@EnableGlobalMethodSecurity中的prePostEnabled = true
1 2
| @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) public class SecurityDemoApplication {
|
- 在【TestController】中添加一个方法
1 2 3 4 5 6
| @GetMapping("/post") @PostAuthorize(value = "hasAuthority('admin')") public String post() { System.out.println("TestController.post"); return "hello PostAuthorize"; }
|
- 分别使用root和admin账户登录查看效果
- 使用root账户登录,页面现实没有反问权限,但是控制台会输出内容,说明该方法被访问了
- 使用admin账户登录,可以正常访问,且控制台也会输出内容
@PostFilter
对方法返回数据进行过滤
@PreFilter
对传入方法数据进行过滤