前言
SpringBoot整合Shiro配置后台管理项目权限,普遍的方式是通过添加shiro标签在html文件里面,然后判断当前用户是否拥有当前权限,来确认是否展示当前菜单,shiro标签类似如下:
没有权限
权限与判断
这种方式缺点是需要手写所有菜单标签,也要加上对应的权限标识,在菜单列表比较多的时候,index页面会显得很臃肿,而且自己也容易混淆其中的权限标识。上次看了同事管理权限的方式,感觉很精妙,他是通过在数据库里面创建菜单表,关联权限表用户表,然后在进入index页面的时候,通过freemarker模板引擎,循环创建菜单表,这样一来,进入index页面之后,只会显示当前用户拥有权限的菜单。而且这种方式,在搭建好了后台配置之后,只需要手动的在数据库里面写入菜单名,index页面就会直接显示,这样不容易搞混淆。
后台搭建
数据库表
1.user表
2.user_role表
3.role表
4.permission表
5.role_permission表
6.menu表
代码
1.Controller
@GetMapping("/index")
public String login(Model model) {
//获取当前用户名得到菜单
Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()) {
return "/login";
}
//根据当前登录账号来获取当前当前账号所拥有权限的菜单列表
String username = subject.getPrincipal().toString();
List menuTree = menuService.findMenuTreeByUsername(username);
model.addAttribute("menuTree",menuTree);
return "index";
}
2.Service
public interface MenuService extends IService
List findMenuTreeByUsername(String username);
}
3.ServiceImpl
@Service
public class MenuServiceImpl extends ServiceImpl implements MenuService {
@Autowired
private MenuMapper menuMapper;
@Override
public List findMenuTreeByUsername(String username) {
return menuMapper.findMenuTreeByUsername(username);
}
}
4.Mapper
public interface MenuMapper extends BaseMapper
/***
* 根据用户名获取菜单树
* @param username
* @return
*/
List findMenuTreeByUsername(@Param("username")String username);
}
5.Xml
select pm.id,pm.menu_name,pm.menu_icon,m.id mid,m.menu_name mname,m.menu_url murl
from user u
inner join user_role ur on u.id=ur.user_id
inner join role role on ur.role_id=role.id
inner join role_permission rp on role.id=rp.role_id
inner join permission p on rp.permission_id=p.id
inner join menu m on p.menu_id=m.id
inner join menu pm on m.menu_id=pm.id
where u.username=#{username}
6.VO对象
@Data
public class Menus {
private Long menuId;
private String menuName;
private String menuUrl;
private String menuIcon;
private List subMenus;
}
前端页面生成菜单列表
//此处是通过freemarker模板引擎生成,用thymeleaf的话也是可以的
menuTree
${menus.menuName}
-
${s.menuName}
#list><
/dl>
#list>
Shiro配置
1.ShiroConfig添加权限标识
ShiroConfig文件里面,通过查询在数据库里配置的全选标识,给所有的需要权限的资源添加权限标识,同时,通过这种方式,直接省略了在每个接口上面添加@RequiresPermissions("user:add")类似的注解。
/**
* 定义shiroFilter过滤器并注入securityManager
* @param manager
* @return
*/
@Bean("shiroFilter") //必须叫这个。
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置securityManager
bean.setSecurityManager(manager);
//设置登录页面
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/auth.html");
//定义过滤器
LinkedHashMap filterChainDefinitionMap = new LinkedHashMap<>();
//配置记住我或认证通过可以访问的地址
filterChainDefinitionMap.put("/index", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger-resources", "anon");
filterChainDefinitionMap.put("/swagger-resources/configuration/security", "anon");
filterChainDefinitionMap.put("/swagger-resources/configuration/ui", "anon");
filterChainDefinitionMap.put("/api/**", "anon");
//通过查询在数据库里面的权限标识,循环给自己设定的字段添加标识权限
List list = permissionMapper.getAll();
for (Permission permission : list) {
filterChainDefinitionMap.put(permission.getResource(), "perms["+permission.getSn()+"]");
}
//需要登录访问的资源 , 一般将/**放在最下边
filterChainDefinitionMap.put("/**", "anon"); // , 不需要认证。
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
permissionMapper
public interface PermissionMapper extends BaseMapper {
List getPermissionsByUserName(@Param("username")String username);
List getAll();
}
Xml
select * from permission
2.AuthRealm授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("Shiro开始授权操作");
String username = SecurityUtils.getSubject().getPrincipal().toString();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
Set roles=new HashSet<>();
//得到一个用户的所有角色
List rolesList = roleMapper.getRolesByUserName(username);
for (Role role : rolesList) {
roles.add(role.getRoleName());
}
//得到一个用户的所有权限,给当前用户授权
List permissionsList = permissionMapper.getPermissionsByUserName(username);
for (Permission permission : permissionsList) {
authorizationInfo.addStringPermission(permission.getSn());
}
authorizationInfo.setRoles(roles);
return authorizationInfo;
}
Mapper
public interface RoleMapper extends BaseMapper {
List getRolesByUserName(@Param("username")String username);
}
Xml
select
r.id,
r.role_name,
r.remake
from role r
left join user_role ur on ur.role_id=r.id
left join user u on u.id=ur.user_id
where u.username=#{username}
Xml
select
p.id id,
p.permission_name,
p.resource resource,
p.sn sn,
p.menu_id
from permission p
left join role_permission rp on rp.permission_id=p.id
left join role r on r.id=rp.role_id
left join user_role ur on ur.role_id=r.id
left join user u on u.id=ur.user_id
where u.username=#{username}
总结
参考表数据
menu
permission表
role_permission
role
role_user
user
前端页面效果