Java解决垂直鉴权问题(对垂直权限进行校验)
文章目录
- Java解决垂直鉴权问题(对垂直权限进行校验)
- 前言
- 一、垂直鉴权是什么?
- 二、实现过程
- 1.新建接口权限菜单映射表
- 2.项目初始化时加载接口菜单映射关系
- 3.自定义过滤器拦截器
前言
避免垂直鉴权实现原理:每次用户访问时,都要判定该用户是否有访问此 URL 的权限。
一、垂直鉴权是什么?
垂直权限漏洞是指Web应用没有做权限控制,或仅仅在菜单上做了权限控制,导致恶意用户只要猜到了其他页面的URL,就可以访问或控制其他角色拥有的数据或页面,达到权限提升的目的。
垂直权限的漏洞举例:Web应用程序在服务端没有做权限控制,只是在前端菜单显示上将部分页面隐藏了。此时,恶意用户可以猜测其他管理页面的 URL,就可以访问或控制其他角色拥有的数据或页面,达到越权操作的目的,可能会使得普通用户拥有了管理员的权限。
二、实现过程
1.新建接口权限菜单映射表
/*** <p>* 接口权限映射表* </p>*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("ed_interface_mapping")
public class InterfaceMapping implements Serializable {private static final long serialVersionUID = 1L;/*** 主键id*/@TableId(value = "id", type = IdType.AUTO)private Integer id;/*** 接口路径*/@TableField("interface_path")private String interfacePath;/*** 对应权限菜单*/@TableField("permission")private String permission;
}
2.项目初始化时加载接口菜单映射关系
@Slf4j
@Component
@RequiredArgsConstructor
public class VerticalPermissionHandle {//应用上下文,Web应用程序的基本URL路径@Value("${server.servlet.context-path}")private String contextPath;private final InterfaceMappingService interfaceMappingService;private Map<String, List<String>> map;/*** 项目初始化时加载接口菜单映射关系*/@PostConstructpublic void init() {log.info("============================加载权限开始==================================");//查询出所有的接口菜单映射关系List<InterfaceMapping> interfaceMappings = interfaceMappingService.list();if (ObjectUtil.isEmpty(interfaceMappings)) {throw new BizException(ResultEnum.ERROR.getCode(), "启动失败,未加载垂直权限关系");}this.map = new HashMap<>();/*** key为接口url* value为菜单(防止出现相同的接口url在多个权限菜单中出现(这种情况可能性很小),这里使用list存储菜单)*/interfaceMappings.stream().forEach(data -> {String key = contextPath + data.getInterfacePath();List<String> value;if (map.containsKey(key)) {value = map.get(key);} else {value = new ArrayList<>();}value.add(data.getPermission());map.put(key, value);});log.info("============================加载权限结束==================================");}public List<String> verticalPermission(String path) {List<String> result = map.get(path);if (ObjectUtil.isEmpty(result)) {return new ArrayList<>();}return result;}
}
3.自定义过滤器拦截器
/*** 自定义单点登录过滤器* 注:CheckFilter类为自己写的过滤器,与此业务无关* doCheck也是自定义的过滤器方法,可忽略* 具体业务逻辑如下已实现*/
@Slf4j
public class SsoFilter extends CheckFilter {@Overrideprotected boolean doCheck(HttpServletRequest request, HttpServletResponse response) throws IOException {String uri = request.getRequestURI();String token = request.getHeader("token");if (!verticalPermissionCheck(uri, token)) {log.info(" [当前访问接口uri] : {} ", uri);//用来接口返回,提示前端(可加可不加)response.setHeader("Access-Control-Allow-Origin", "*");response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");response.setContentType("application/json;charset=utf-8");ObjectMapper objectMapper = new ObjectMapper();ServletOutputStream writer = response.getOutputStream();writer.write(objectMapper.writeValueAsBytes("当前用户暂无该权限"));writer.flush();writer.close();return false;}else {return true;}}/*** 判断url所对应的菜单是否存在于登录用户的权限菜单中,* 存在则为true,不存在则为false* @param uri 用户访问的uri* @param token 登录的token* @return*/private boolean verticalPermissionCheck(String uri, String token) {List<String> permissions = verticalPermissionHandle.verticalPermission(uri);if (ObjectUtil.isEmpty(permissions)){return true;}/*** 在redis中获取用户信息* 其中用户信息包含这个角色所对应的permission菜单* 遍历根据url这个key获取的value菜单,如果存在这个角色所对应的菜单中,则放行*/User userByToken = UserUtils.getUserByToken(token);for (String permission : permissions) {if (userByToken.getAllPermissionString().contains(permission)) {return true;}}return false;}