大家面试的时候有没有被问过这样一个问题:
有一个需求涉及到权限控制,如果是你,你会怎么设计表?
首先我们要知道系统访问控制模型的目的和目标是确保系统的安全性、完整性和可用性。通过实施适当的访问控制模型,可以防止未经授权的访问、修改或破坏系统资源,从而保护系统的安全性。
常见的系统访问控制模型有:自主访问控制(DAC)、强制访问控制(MAC)、基于角色访问控制(RBAC)、基于属性访问控制(ABAC)。今天我们重点讲一下RBAC。
很多同学在被问到这个问题时,往往都不知道怎么回答这个问题,其实这个问题的核心就是这个RBAC。只要答到RBAC这个问题大体方向也是对的:
RBAC模型是基于角色的访问控制模型,它将用户分配到不同的角色中,每个角色都有一组权限。用户通过被分配到的角色来获得访问系统资源的权限。RBAC模型的工作原理如下:
确定用户的身份。当用户登录系统时,系统会验证用户的身份,确定用户是谁。
确定用户所属的角色。系统根据用户的身份,确定用户所属的角色。一个用户可以拥有多个角色,每个角色都有一组权限。
确定用户的权限。系统根据用户所属的角色,确定用户的权限。用户只能访问其所拥有的权限范围内的系统资源。
执行访问控制。系统根据用户的权限,决定是否允许用户访问某个资源。如果用户没有访问该资源的权限,则系统会拒绝用户的访问请求。
看完你会发现这个设计方式其实很熟悉,我大概用自己的语言讲一下:
我们需要在数据库大致设计这4张表(当然也可能是5张表,视具体的项目而定):
- 用户表(user):存储用户的基本信息。
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `role_id` int(11) NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
用户表定义了用户的帐号,密码,角色,比如
id username password role_id 1 admin 12345(最好加密) 管理员 2 lisi 12345 普通用户 - 角色表(role):存储角色的基本信息。
CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
角色表定义了id,角色姓名
id name 1 管理员 2 普通用户 - 权限表(permission):存储权限的基本信息
CREATE TABLE `permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
权限表主要字段:id,权限名
id | name |
1 | 新增 |
2 | 删除 |
3 | 查看 |
4.角色权限关联表(role_permission):存储角色与权限之间的关联关系。
CREATE TABLE `role_permission` (
`role_id` int(11) NOT NULL,
`permission_id` int(11) NOT NULL,
PRIMARY KEY (`role_id`, `permission_id`),
FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
角色权限关联表字段:角色id,权限id,利用外键role_id,permisson_id关联角色表和权限表
role_id | permisson_id |
1(管理员) | 1(新增) |
1(管理员) | 2(删除) |
2(普通用户) | 3(查看) |
当然管理员也同样可以查看,但普通用户只可以查看
这样我们可以联想一个场景:
//当用户登录之后,获取帐号(因为你可能会保存用户的登录信息)或者你可能设计了根据
//帐号或者密码生成加密token,这样你也可以根据token解密然后分割拿到对应的帐号和密码
//拿到帐号,取数据库的用户表查询role_id
userMapper.selectByAccount(String useraccount);
//拿到了role_id
可以跟据role_id去角色权限关联查询权限(permisson)
xxxMapper.queryByroleId(int roleId);
//拿到对应的permisson_id,就可以写判断条件了决定是否可以走接下来的逻辑
//这里如果平时规范写代码,我们不用再拿着这里的permisson_id去查权限表了,
//反正我个人的习惯会把权限写成一个Enum(枚举类)
public Enum permisson{
private String message;
private int code;
/**
*新增
*/
save("新增",1),
/**
*删除
*/
delete("删除",2),
/**
*查询
*/
select("查询",3),
}
接上面的判断
if(permissonId == permisson.save.getcode){
//说明有新增权限,允许这个用户新增
}
//其余的同理
当然上述代码只是一种场景,还有很多场景,比如结合springsecurity做权限控制。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/teacher/**").hasRole("TEACHER")
.antMatchers("/student/**").hasRole("STUDENT")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
好了,看到这里大家也对RBAC应该也有一个大概的印象了,现在回到开始,你可以自信的向面试官回答出这个问题了吗?