前言:
后端接口的调用,必须受到用户权限的限制,对某个用户是否有权限访问该接口,其角色,部门,等都是影响因素,还有一个用户的状态,也是变化的等,需要做到每次调用接口,都要实时刷新一下,该用户是否有权限访问?
步骤:
从需求出发,既然是需要做到每次调用接口,都要实时刷新一下,该用户是否有权限访问?肯定要用注解!
建自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RefreshFilterChain {
}
凡是标记该注解,均会引发 Shiro 过滤器链的变化, 将刷新过滤器链.
建AOP刷新切面类
@Aspect
@Component
public class RefreshFilterChainAspect {
@Resource
private ShiroService shiroService;
@Pointcut("@annotation(aaaa.bbbb.common.annotation.RefreshFilterChain)")
public void updateFilterChain() {}
@AfterReturning("updateFilterChain()")
public void doAfter() {
shiroService.updateFilterChain();
}
}
建AOP的刷新切面类,便于以后注解到各个API上,实现更新过滤器链!
在shiro服务类ShiroService,加入实现
public void updateFilterChain() {
synchronized (shiroFilterFactoryBean) {
AbstractShiroFilter shiroFilter;
try {
shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
} catch (Exception e) {
throw new ShiroException("get ShiroFilter from shiroFilterFactoryBean error!");
}
PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();
// 清空老的权限控制
manager.getFilterChains().clear();
shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
shiroFilterFactoryBean.setFilterChainDefinitionMap(getUrlPermsMap());
userNameRealm.clearAllAuthCache();
// 清除每个 Filter 中的 appliedPaths 信息
for (Map.Entry<String, Filter> filterEntry : manager.getFilters().entrySet()) {
if (filterEntry.getValue() instanceof PathMatchingFilter) {
PathMatchingFilter filter = (PathMatchingFilter) filterEntry.getValue();
Map<String, Object> appliedPaths = (Map<String, Object>) ReflectUtil.getFieldValue(filter, "appliedPaths");
synchronized (appliedPaths) {
appliedPaths.clear();
}
}
}
// 重新构建生成
Map<String, String> chains = shiroFilterFactoryBean
.getFilterChainDefinitionMap();
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue().trim().replace(" ", "");
manager.createChain(url, chainDefinition);
}
log.info("更新 Shiro 过滤器链");
}
}
在shiroService实现updateFilterChain()方法,实现清空老的权限控制,清除每个 Filter 中的 appliedPaths 信息,重新构建生成权限控制,从而实现更新 Shiro 过滤器链
在Controller中的crud的API方法上加上@RefreshFilterChain
@OperationLog("新增菜单")
@RefreshFilterChain
@PostMapping
@ResponseBody
public ResultBean add(Menu menu) {
menuService.insert(menu);
return ResultBean.success();
}
这样就可以安全的开放接口,给别人,防止自己的接口,被恶搞了!
总结:
以上是一种思路,从需求出发,进而展开,对代码的编写,涉及到很多知识点,如:
spring的aop的运用
- Pointcut:Pointcut定义时,还可以使用&&、||、! 这三个运算。进行逻辑运算。可以把各种条件组合起来使用
- AfterReturning:使用@AfterReturning注解时,指定了一个returning属性,该属性值为rvt,这表明允许在Advice方法(log()方法)中定义名为rvt的形参,程序可通过rvt形参来访问目标方法的返回值
shiro的三个核心组件:Subject, SecurityManager 和 Realms.
- Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
- SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
- Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
在ShiroFilterFactoryBean中,createFilterChainManager方法比较重要,包含以下必要操作:
- DefaultFilterChainManager对象的创建
- defaultFilters的获取和相关url的填充
- 自定义filters的获取和相关url的填充
- 获取FilterChainDefinitionMap (这个就是配置文件中的filterChainDefinitions的映射关系)
- 对url和权限的映射关系作处理
DefaultFilterChainManager,是路径和filter的管理类filterChainManager的实现类,用于管理我们在shiroFilterFactoryBean中配置的过滤器和模板,以及配置的参数
- 获得spring中的所有filters,都被放入到这个类(filterChainManager)中
- filter根据pathPattern(路径模板)进行分类,方便再查找某个路径的对应的filterChain时,采取的提前分类处理。