[Shiro] - 基于URL配置动态权限
Posted ukzq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Shiro] - 基于URL配置动态权限相关的知识,希望对你有一定的参考价值。
基于shiro进阶
更改了数据库表
之前的PageController是通过@RequiresPermissions和@RequiresRoles进行是否有权限/是否有角色的判定调用@RequestMapping路径
在PermissionService中加入了两个方法:needInterceptor, listPermissionURLs
needInterceptor表示是否要进行拦截,判断依据是如果访问的某个url,在权限系统里存在,就要进行拦截.
如果不存在就放行了. 这一种策略,也可以切换成另一个,即:
访问的地址如果不存在于权限系统中,就提示没有拦截.这两种做法没有对错之分,取决于业务上希望如何制定权限策略。
listPermissionURLs(User user)用来获取某个用户所拥有的权限地址集合
package com.how2java.service.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.how2java.mapper.PermissionMapper; import com.how2java.mapper.RolePermissionMapper; import com.how2java.pojo.Permission; import com.how2java.pojo.PermissionExample; import com.how2java.pojo.Role; import com.how2java.pojo.RolePermission; import com.how2java.pojo.RolePermissionExample; import com.how2java.service.PermissionService; import com.how2java.service.RoleService; import com.how2java.service.UserService; @Service public class PermissionServiceImpl implements PermissionService { @Autowired PermissionMapper permissionMapper; @Autowired UserService userService; @Autowired RoleService roleService; @Autowired RolePermissionMapper rolePermissionMapper; @Override public Set<String> listPermissions(String userName) { Set<String> result = new HashSet<>(); List<Role> roles = roleService.listRoles(userName); List<RolePermission> rolePermissions = new ArrayList<>(); for (Role role : roles) { RolePermissionExample example = new RolePermissionExample(); example.createCriteria().andRidEqualTo(role.getId()); List<RolePermission> rps = rolePermissionMapper.selectByExample(example); rolePermissions.addAll(rps); } for (RolePermission rolePermission : rolePermissions) { Permission p = permissionMapper.selectByPrimaryKey(rolePermission.getPid()); result.add(p.getName()); } return result; } @Override public void add(Permission u) { permissionMapper.insert(u); } @Override public void delete(Long id) { permissionMapper.deleteByPrimaryKey(id); } @Override public void update(Permission u) { permissionMapper.updateByPrimaryKeySelective(u); } @Override public Permission get(Long id) { return permissionMapper.selectByPrimaryKey(id); } @Override public List<Permission> list() { PermissionExample example = new PermissionExample(); example.setOrderByClause("id desc"); return permissionMapper.selectByExample(example); } @Override public List<Permission> list(Role role) { List<Permission> result = new ArrayList<>(); RolePermissionExample example = new RolePermissionExample(); example.createCriteria().andRidEqualTo(role.getId()); List<RolePermission> rps = rolePermissionMapper.selectByExample(example); for (RolePermission rolePermission : rps) { result.add(permissionMapper.selectByPrimaryKey(rolePermission.getPid())); } return result; } @Override public boolean needInterceptor(String requestURI) { List<Permission> ps = list(); for (Permission p : ps) { if (p.getUrl().equals(requestURI)) return true; } return false; } @Override public Set<String> listPermissionURLs(String userName) { Set<String> result = new HashSet<>(); List<Role> roles = roleService.listRoles(userName); List<RolePermission> rolePermissions = new ArrayList<>(); for (Role role : roles) { RolePermissionExample example = new RolePermissionExample(); example.createCriteria().andRidEqualTo(role.getId()); List<RolePermission> rps = rolePermissionMapper.selectByExample(example); rolePermissions.addAll(rps); } for (RolePermission rolePermission : rolePermissions) { Permission p = permissionMapper.selectByPrimaryKey(rolePermission.getPid()); result.add(p.getUrl()); } return result; } }
注意其中的for循环,是否可以进行更优化的写法
URLPathMatchingFilter, PathMatchingFilter是shiro内置过滤器.URL...继承了Path...
基本思路:
1. 如果没登录就跳转到登录
2. 如果当前访问路径没有在权限系统里维护,则允许访问
3. 当前用户所拥有的权限如何不包含当前的访问地址,则跳转到/unauthorized, 否则就允许访问
package com.how2java.filter; import java.util.Set; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.PathMatchingFilter; import org.apache.shiro.web.util.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import com.how2java.service.PermissionService; public class URLPathMatchingFilter extends PathMatchingFilter { @Autowired PermissionService permissionService; @Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { String requestURI = getPathWithinApplication(request); System.out.println("requestURI:" + requestURI); Subject subject = SecurityUtils.getSubject(); // 如果没有登录,就跳转到登录页面 if (!subject.isAuthenticated()) { WebUtils.issueRedirect(request, response, "/login"); return false; } // 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行) boolean needInterceptor = permissionService.needInterceptor(requestURI); if (!needInterceptor) { return true; } else { boolean hasPermission = false; String userName = subject.getPrincipal().toString(); Set<String> permissionUrls = permissionService.listPermissionURLs(userName); for (String url : permissionUrls) { // 这就表示当前用户有这个权限 if (url.equals(requestURI)) { hasPermission = true; break; } } if (hasPermission) return true; else { UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限"); subject.getSession().setAttribute("ex", ex); WebUtils.issueRedirect(request, response, "/unauthorized"); return false; } } } }
以及applicationContext-shiro.xml做了改动
将urlPathMatchingFilter加入shiro使用这个过滤器
这样一样就配置全部的url路径了,而对应的角色没有和url对应起来.
为什么不把角色也对应起来,从代码开发角色来说是可以的,无非是role表添加一个url字段,但是从权限管理本身,当一个url即对应权限表的数据,又对应角色表的数据,
反而容易产生混淆.
这种呢,url地址,仅仅和权限表关联,从逻辑上明晰简单,更易维护.
requestURI:/
requestURI:/listProduct
requestURI:/deleteProduct
requestURI:/deleteOrder
requestURI:/deleteOrder
requestURI:/deleteOrder
requestURI:/unauthorized
requestURI:/deleteProduct
requestURI:/listProduct
查看日志也会较为轻松
以上是关于[Shiro] - 基于URL配置动态权限的主要内容,如果未能解决你的问题,请参考以下文章
一套基于SpringBoot+Vue+Shiro 前后端分离 开发的代码生成器