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;
/**
* 项目初始化时加载接口菜单映射关系
*/
@PostConstruct
public 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 {
@Override
protected 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;
}