本学习日志是使用Springboot和Vue来搭建的后台管理系统:
演示地址:http://118.31.68.110:8081/index.html
账号:root
密码:123
所有代码可以在gitbub上找到,切换到相应分支即可。【代码传送门】
正篇
第一节 spring boot 模块化构建项目
第二节 整合mybatisplus完成用户增删改查
第三节 整合springsecurity实现基于RBAC的用户登录
第四节 springsecurity结合jwt实现前后端分离开发
第五节 使用ResponseBodyAdvice格式化接口输出
第六节 springboot结合redis实现缓存策略
第七节 springboot结合rabbitmq实现队列消息
第八节 springboot结合rabbitmq实现异步邮件发送
第九节 利用springboot的aop实现行为日志管理
第十节 利用Quartz实现数据库定时备份
第十一节 springboot配置log输出到本地文件
第十二节 使用flyway对数据库进行版本管理
第十三节 springboot配合VbenAdmin实现前端登录
第十四节 springboot配合VbenAdmin实现用户CURD
第十五节 基于RBAC的权限管理VbenAdmin前端实现
第十六节 springboot 打包vue代码实现前后端统一部署
番外
2.1 数据库设计原则
3.1 配置apifox自动获取登录的token
13.1 springboot 全局捕捉filter中的异常
14.1 springsecurity整合mybatisplus出现isEnable的问题和解决方案
权限认证中还是spring security 用的比较多,不过spring boot 2.7以后有些配置有改动。
RBAC就不多介绍了,没听过的同学可以看下底下的参考文档。加上之前建立的user表共五张表,如下(建表sql语句可以到文章最后的代码链接中找到):
以下只贴核心代码,完整代码可以到文章最后的代码链接中找到
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
User 需实现 UserDetails 以及 Role 和 Permission
@TableField(exist = false)
private List roles;
@Override
public Collection extends GrantedAuthority> getAuthorities() {
List authorities = new ArrayList<>();
for (Role role: roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
UserServiceImpl 需要实现UserDetailsService
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",username);
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
if (user == null){
throw new UsernameNotFoundException("账户不存在");
}
user.setRoles(userMapper.getUserRolesByUid(user.getId()));
System.out.println(user);
return user;
}
UserMapper 要继承 BaseMapper
//根据用户id,获得他所有的角色
List<Role> getUserRolesByUid(BigInteger id);
//获取用户列表,及其包含的角色信息
List<User> getUsersWithRoles();
如果要在mapper中自定查询方法,需要在admin-mapper中创建resources文件,然后创建xml
主配置文件 SecurityConfiguration
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O object) {
object.setSecurityMetadataSource(cfisms());
object.setAccessDecisionManager(cadm());
return object;
}
})
.and()
.formLogin()
.loginProcessingUrl("/login").permitAll()
.and()
.csrf().disable()
.userDetailsService(userService);
return http.build();
}
根据请求路径筛选角色配置
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) object;
String requestUrl =filterInvocation.getRequestUrl(); // 请求url
String method = filterInvocation.getHttpRequest().getMethod(); // 请求的方法
List<Permission> allPermission = permissionMapper.getAllPermissions();
List<String> roleArr = new ArrayList<String>();
for (Permission permission : allPermission) {
if(antPathMatcher.match(permission.getPattern(),requestUrl)){ // 先判断URL路径是否符合
if ("ANY".equals(permission.getMethod())
|| method.equals(permission.getMethod())){ // 再判断方法是否符合
List<Role> roles = permission.getRoles();
for (int i = 0; i < roles.size(); i++) {
roleArr.add(roles.get(i).getName());
}
}
}
}
if (roleArr.size() > 0){ // 找到匹配的角色
String[] roleNames = new String[roleArr.size()];
roleArr.toArray(roleNames);
return SecurityConfig.createList(roleNames);
}
return SecurityConfig.createList("ROLE_LOGIN");
}
匹配登录角色配置
public void decide(Authentication auth, Object object, Collection<ConfigAttribute> ca) throws AccessDeniedException, InsufficientAuthenticationException {
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();// 当前用户拥有的角色
for (ConfigAttribute configAttribute : ca) { // 当前URL需要的角色
if ("ROLE_LOGIN".equals(configAttribute.getAttribute()) && auth instanceof UsernamePasswordAuthenticationToken){
return; // 无需权限的直接访问
}
for (GrantedAuthority authority : authorities) {
if (configAttribute.getAttribute().equals(authority.getAuthority())){ // 找到匹配项
return;
}
}
}
throw new AccessDeniedException("权限不足");
}
这里由于还没有集成jwt,测试的时候还是基于Cookie的。可以先用浏览器访问登陆界面获得到Cookie值,然后放到apifox中测试POST接口。
使用 user/123 登陆,获得Cookie。
由于user这个用户并没有新增用户的权限,所以POST的新增请求返回的是403。
如果使用 admin/123 登陆,则可以顺利新增成功。
如此,基于RBAC的标准restful请求已完成。
代码
参考文档:
RBAC简介
如何使用postman测试springsecurity的代码