今天和小伙伴们聊一个简单的话题,是有一个小伙伴在星球上提的问题,问题不难,但是不熟悉的小伙伴可能会出错,所以简单说一下。
先来看看这个小伙伴是怎么提问的:
老实说,有时候我真的会被小伙伴们的提问感动到! 这年头能把问题这么详细列出来的人真的不多了。松哥微信每天也有不少小伙伴在提问,有的问题就一句话,看了也不知道想问啥,那我也没法回复,我就不贴图了,免得有小伙伴对号入座。
总之这个小伙伴这个提问特别好,我看完文字看完图之后,基本上就已经知道发生什么事了。
他出问题的代码在这里:
这个问题其实这块就是 vhr 中的动态权限问题,小伙伴们知道,vhr 的权限可以动态设置,也就是我们可以通过修改数据库中的表,来动态调整每一个接口需要什么样的权限,vhr 里相关代码主要涉及到三个类:
- https://github.com/lenve/vhr/blob/master/vhr/vhrserver/vhr-web/src/main/java/org/javaboy/vhr/config/CustomFilterInvocationSecurityMetadataSource.java
- https://github.com/lenve/vhr/blob/master/vhr/vhrserver/vhr-web/src/main/java/org/javaboy/vhr/config/CustomUrlDecisionManager.java
- https://github.com/lenve/vhr/blob/master/vhr/vhrserver/vhr-web/src/main/java/org/javaboy/vhr/config/SecurityConfig.java
这三个类的实现细节在 vhr 中都有详细的介绍,这里我就不细说了。这里就简单说说每个类的作用:
- CustomFilterInvocationSecurityMetadataSource:这个是自定义的获取元数据的类,作用是根据请求的 URL 地址,分析出这个请求所需要的角色。
- CustomUrlDecisionManager:这是一个决策器,按理说决策器需要配合投票器一起来使用,但是我这里简化了步骤,直接在决策器中做了判断,就没用投票器了,决策器在执行过程中如果抛出了异常,那么就是权限不足,如果决策器能够正常执行不抛出任何异常,那么就说明权限没问题。
- SecurityConfig:这就是 Spring Security 的配置类,在这个配置类中可以通过对象后置处理器 ObjectPostProcessor 来动态修改一个 Bean,上面两个对象就是通过 ObjectPostProcessor 添加到相应实例中的。
那么这个小伙伴出问题的类就是决策器,我们再来看看他这一张图片:
decide 方法就是决策方法,这个方法第一个参数 Authentication 参数重保存了当前用户的信息,包括当前用户的角色啥的;第三个参数是一个集合,里边是当前请求需要的角色信息。
现在就在 decide 方法中做比对,如果当前用户具备当前请求所需要的角色,那么就没问题,如果当前用户不具备当前请求所需要的角色,那么就抛出异常。
仔细看你发现他这个逻辑也没啥问题,而且看下面的日志也打印出来了,说明 if 分支也进去了,所以问题就出在了这个 return 上面。
小伙伴们看这里的 return 在 forEach 中,而不是我们常见的 for 循环中,那么这歌 forEach 和 for 有啥不同呢?
这个 forEach 是 lambda 表达式相关的内容,forEach 的参数是一个消费者,也就是消费一个数据,类似下面这样:
1 | List<String> list = new ArrayList<>(); |
那么你想一下,accept 方法中出现一个 return,那么仅仅只能终止当前方法的执行,并不影响循环整体的执行,即并不能提前终止循环。
现在就真相大白了,如果匹配到权限是满足的,提前终止的 return 语句没生效,方法最后一句异常还是会抛出来。
所以改成这样就行啦:
好啦,最后再盲猜一下为什么这个小伙伴会出现这个错误。在 vhr 视频中,我是前后端都手把手教大家写的,视频也会写前端代码,前端有一个 forEach,就是真正的 for 循环,而且前端的箭头函数跟后端的 Lambda 也挺像的,估计小伙伴可能看到前端的,以为后端也能这么用(纯瞎猜~。