Springboot-security(权限控制)
Springboot-security(权限控制)
- 核心:
- 认证
- 授权
- 自定义配置相关类:
WebSecurityConfigAdapter
- 相关方法:
config(AuthenticationMangerBuilder auth)
:认证configure(HttpSecurity http)
:授权configure(WebSecurity web)
:资源放行-
passwordEncoder passwordEncoder()
:密码加密
- 登录校验流程
- 前端携带用户名密码访问登录接口
- 服务器端在数据库中对账号和密码进行校验
- 如果正确,使用用户名或id生成jwt,并将jwt响应给前端
- 登录后访问其他请求,需要在请求头中携带token
- 根据请求头中的token获取用户ID,如果有权限则允许访问相关资源
- 访问目标资源,返回给前端
- 基础用法
引入依赖
1
2
3
4
5<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.7.3</version>
</dependency>添加测试接口
1
2
3
4
5
6
7
8
public class HelloController(){
public String hello(){
return "Hello";
}
}启动项目测试访问
test
接口,接口会自定跳到security提供的默认登录页面,默认用户名为user
,密码为控制台随机生成配置用户名和密码(由开发人员指定)
1
2
3spring.security.username=root
spring.security.password=1234
spring.security.roles=admin
- 基于内存的认证
自定义类继承
WebSecurityConfigurerAdapter实现
config(AuthenticationMangerBuilder auth)`方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
//密码加密
passwordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//认证
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("1234").roles("ADMIN")
.and()
.withUser("user").password("user").roles("USER")
.and()
.withUser("dba").password("1234").roles("DBA");
}
}
- HttpSecurity授权
自定义类继承
WebSecurityConfigAdapter
实现config(HttpSecurity http)
方法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
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
//密码加密
passwordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//认证
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("1234").roles("ADMIN")
.and()
.withUser("user").password("user").roles("USER")
.and()
.withUser("dba").password("1234").roles("DBA");
}
//授权
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.antMatchers("/user/**")
.hasRole("USER")
.antMatchers("/db/**")
.hasRole("DBA")
.antMatchers("/error/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
}
- 自定义登录页面
1 |
|
- SpringSecurity完整流程
SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器
- BasicAuthenticationFilter实现的是HttpBasic模式的登录认证
- UsernamePasswordAuthenticationFilter实现用户名密码的登录认证
- RememberMeAuthenticationFilter实现登录认证的“记住我”的功能
- SmsCodeAuthenticationFilter实现短信验证码登录认证
- SocialAuthenticationFilter实现社交媒体方式登录认证的处理
- Oauth2AuthenticationProcessingFilter和Oauth2ClientAuthenticationProcessingFilter实现Oauth2的鉴权方式
- 注销登陆配置
1 | .and() |
- 多个HttpSecurity
当业务复杂,开发者可以配置多个HttpSecurity
,实现对WebSecurityConfigurerAdapter
的多次扩展
1 |
|
==代码解释==:配置多个HttpSecurity
时,MultiHttpSecurityConfig
不需要继承WebSecurityConfigurerAdapter
,只用在其中创建多个静态内部类继承WebSecurityConfigurerAdapter
,静态内部类上添加@Configuration
注解和@Order(1)注解
,数字越小优先级越大
- 密码加密
- 配置基于内存的密码加密
1 |
|
- 配置基于数据库的密码加密
1 |
|
- 方法安全
开启基于注解的安全配置:
@EnableGlobalMethodSecurity
```
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true,securedEnabled=true)
public class WebSecurityConfig{}
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
3. 代码解释
- `prePostEnabled=true`会解锁`@PreAuthorize`(方法前执行)和`@PostAuthorize`(方法后执行)两个注解
- `securedEnabled=true`会解锁`@Secured`注解
4. 代码测试
```java
@Service
public class MethodService{
@Secured("ROLE_ADMIN")
public String admin(){
return "hello admin";
}
@PreAuthorize("hasRole('ADMIN') and hasRole('DBA')")
public String dba(){
return "hello dba";
}
@PreAuthorize("hasAnyRole('ADMIN','DBA','USER')")
public String user(){
return "user";
}
}代码解释:
- @Secured(“ROLE_ADMIN”)表示访问该方法需要
ADMIN
角色,角色前一定要添加ROLE_
- @PreAuthorize(“hasRole(‘ADMIN’) and hasRole(‘DBA’)”),访问该方法同时需要
ADMIN和DBA
角色 - @PreAuthorize(“hasAnyRole(‘ADMIN’,’DBA’,’USER’)”),访问该方法需要
三种
角色中任意一个
角色
- @Secured(“ROLE_ADMIN”)表示访问该方法需要
Controller
中注入Service
,调用方法测试
- 基于数据库的认证
设计数据表:角色表,用户表,用户角色关联表
- user(id,username,password,enabled,locked)
- role(id,name,nameZh)
- user_role(id,uid,rid)
创建项目SpringBoot项目
配置数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14#数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url="jdbc:mysql://localhost:3306/spring_security?serverTimezone=GMT%2B8"
spring.datasource.data-username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#开启日志,方便调试
mybatis-plus.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath:com/security/demo/mapper/xml/*.xml
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8创建实体类
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
51
52
53
54
55
56
57
58
59
60
61
62
63
public class User implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
private String id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private Date gmtCreate;
private Date gmtModified;
private List<Role> roles;
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities=new ArrayList<SimpleGrantedAuthority>();
for (Role role:roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return !locked;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return enabled;
}
}创建UserService实现
UserDeatailsService
接口中的loadUserByUsername(String name)
1
2
3
4
5<!--UserMapper.java-->
public interface UserMapper extends BaseMapper<User> {
User loadUserByUserName(String username);
List<Role> getUserRolesByUid(String uid);
}1
2
3
4
5
6
7
8
9
10
11
12
13<!--UserMapper.xml-->
<mapper namespace="com.security.demo.mapper.UserMapper">
<select id="loadUserByUserName" resultType="com.security.demo.entity.User">
select * from user where username=#{username}
</select>
<select id="getUserRolesByUid" resultType="com.security.demo.entity.Role">
select * from role r, user_role ur where r.id=ur.rid and ur.uid=#{uid}
</select>
</mapper>1
2
3
4
5public interface UserService extends IService<User>, UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
public UserMapper userMapper;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user=userMapper.loadUserByUserName(username);
if(user==null){
throw new UsernameNotFoundException("账户不存在");
}
user.setRoles(userMapper.getUserRolesByUid(user.getId()));
return user;
}
}配置Spring Security
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
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
Uservice uservice;
//密码加密
passwordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//认证
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
//授权
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.antMatchers("/user/**")
.hasRole("USER")
.antMatchers("/db/**")
.hasRole("DBA")
.antMatchers("/error/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
}
jquery Mobile