文章目录
1.基本使用
1.pom依赖
<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.编写Controller测试
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello World!";
}
}
3.启动项目,访问 http://localhost:9999/hello
用户名为 user
,密码在启动项目的控制台可以看到:
输入后显示文字:
2.自定义配置用户名与密码
方式一:在配置文件里配置
spring.security.user.name=admin
spring.security.user.password=123456
spring.security.user.roles=admin
方式二:配置类里配置
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode("123456");
auth.inMemoryAuthentication().withUser("admin").password(password).roles("admin");
}
@Bean
PasswordEncoder password() {
return new BCryptPasswordEncoder();
}
}
方式三:动态地从数据库中查
1.建表
CREATE TABLE `db_user`(
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(255) NULL,
PRIMARY KEY (`id`)
);
2.pom
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
3.配置数据源
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/db_user?useSSL=false&serverTimezone=Asia/Shanghai
4.编写实体类
@Data
@TableName("tb_user")
public class LoginUser {
private Integer id;
private String username;
private String password;
}
5.Mapper
@Mapper
public interface UserMapper extends BaseMapper<LoginUser> {
}
6.编写Service
@Service("userDetailsService")
public class UserDeatilsServiceImpl implements UserDetailsService {
@Resource
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//1.构建查询条件,从数据库查询用户
QueryWrapper<LoginUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",s);
LoginUser loginUser = userMapper.selectOne(queryWrapper);
//2.将用户信息转换为springsecurity中的User
List<GrantedAuthority> authority = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_admin,ROLE_sale");
User user = new User(loginUser.getUsername(),
new BCryptPasswordEncoder().encode(loginUser.getPassword()),
authority);
return user;
}
}
7.将Service注册到SpringSecurity中
@Configuration
public class MyWebSecurityConfig02 extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Bean
PasswordEncoder password() {
return new BCryptPasswordEncoder();
}
}
3.自定义登录页面
@Configuration
public class MyWebSecurityConfig02 extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/test/index").permitAll()
.and().authorizeRequests()
.antMatchers("/","/test/hello","/user/login").permitAll()
.and().authorizeRequests()
.anyRequest().authenticated()
.and().csrf().disable();
}
@Bean
PasswordEncoder password() {
return new BCryptPasswordEncoder();
}
}
/static/login.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>登录</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
4.基于权限访问控制
SpringSecurity判断时是从UserDetailsService中获取用户,进而判断用户是否满足角色或者权限。
@Service("userDetailsService")
public class UserDeatilsServiceImpl implements UserDetailsService {
@Resource
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//1.构建查询条件,从数据库查询用户
QueryWrapper<LoginUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",s);
LoginUser loginUser = userMapper.selectOne(queryWrapper);
//2.将用户信息转换为springsecurity中的User
List<GrantedAuthority> authority = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_admin,ROLE_sale");
User user = new User(loginUser.getUsername(),
new BCryptPasswordEncoder().encode(loginUser.getPassword()),
authority);
return user;
}
}
1.在配置类里配置
@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()
.and().authorizeRequests()
.antMatchers("/test/hello","/admin2")
.hasAnyRole("sale,admin")
.and().authorizeRequests()
.anyRequest().authenticated()
.and().csrf().disable();
}
(1)hasAuthority方法:
.antMatchers("/admin","/admin2").hasAuthority("admin")
(2)hasAnyAuthority方法:满足其中一个就放行,多个权限用逗号隔开
.antMatchers("/admin","/admin2").hasAnyAuthority("admin,teacher")
.antMatchers("/admin","/admin2").hasAnyAuthority("admin","teacher")
(3)hasRole方法:
*注意:UserDetailsService中返回的用户里的角色是带
ROLE_
前缀的,但在配置里不需要写前缀。因为hasRole方法最终会帮我们加入前缀。
.hasRole("admin")
hasAnyRole:使用方法同hasAnyAuthority
.hasAnyRole("sale,admin")
5.自定义403
当权限验证没通过的时候就会报403,但是默认返回的页面我们不是很满意,就可以在配置类里自定义去进行更改。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().accessDeniedPage("/unauth");
}
其中/auth
可以自己定义,可以写为一个url,这样403的时候就会跳转到对应url的Controller的方法里:
@RequestMapping("/unauth")
public String unauth() {
return "权限不足,请联系管理员";
}
也可以写网页的路径,如/403.html
,然后在 resource/static
文件夹下创建网页。
6.使用注解
@Secured、@PreAuthorize、@PostAuthorize
1.首先在启动类上开启
想启用哪个注解就开哪个:
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
}
2.在方法上标注解
-
@Secured 判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。
@ResponseBody @Secured({"ROLE_normal","ROLE_admin"}) public String helloUser() { return "hello,user"; }
-
@PreAuthorize:注解适合进入方法前的权限验证, @PreAuthorize 可以将登录用户的 roles/permissions 参数传到方法中。
@RequestMapping("/preAuthorize") @ResponseBody //@PreAuthorize("hasRole('ROLE_管理员')") @PreAuthorize("hasAnyAuthority('menu:system')") public String preAuthorize(){ System.out.println("preAuthorize"); return "preAuthorize"; } /** * 限制只能查询自己的信息 */ @PreAuthorize("principal.username.equals(#username)") public User find(String username) { return null; }
权限表达式:https://docs.spring.io/spring-security/site/docs/5.3.4.RELEASE/reference/html5/#overview-2
-
@PostAuthorize @PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限.
@RequestMapping("/testPostAuthorize") @ResponseBody @PostAuthorize("hasAnyAuthority('menu:system')") public String preAuthorize(){ System.out.println("test--PostAuthorize"); return "PostAuthorize"; }
@PostFilter、@PreFilter
@PostFilter : 权限验证之后对数据进行过滤 留下用户名是 admin1 的数据
表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素
@RequestMapping("getAll")
@PreAuthorize("hasRole('ROLE_管理员')")
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<UserInfo> getAllUser(){
ArrayList<UserInfo> list = new ArrayList<>();
list.add(new UserInfo(1l,"admin1","6666"));
list.add(new UserInfo(2l,"admin2","888"));
return list;
}
3.7.5 @PreFilter
@PreFilter: 进入控制器之前对数据进行过滤
@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_管理员')")
@PreFilter(value = "filterObject.id%2==0")
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo> list){
list.forEach(t-> {
System.out.println(t.getId()+"\t"+t.getUsername());
});
return list;
}