如何解决纵向越权问题,附代码

135 篇文章 0 订阅
16 篇文章 0 订阅
文章讲述了在测试过程中发现的权限问题,即低权限用户通过替换Token访问了高权限功能。文章介绍了未做好权限控制的风险,以及使用Redis缓存和JWT进行身份验证、防止越权的方法,包括网关代码示例和登录登出操作的实现。
摘要由CSDN通过智能技术生成

正文

测试过程详述

低高权限两个用户中菜单栏中销量上报功能栏下不同功能,用低权限Token替换掉高权限Token,低权限可访问高权限的功能列表,同样回显出高权限的回显数据

风险分析

由于后台应用没有做权限控制,或仅仅在菜单、按钮上做了权限控制,导致恶意用户只要猜测其他管理页面的URL或者敏感的参数信息,就可以访问或控制其他角色拥有的数据或页面,达到权限提升的目的。

解决思路

1、使用redis,key=sessionId,value=token
2、用户登录时,将信息存到缓存中
3、当用户请求接口时,在网关获取到sessionId,再根据传入的token去对比缓存的token。如果一致,就放行;如果不一致,就报错:不可越权,请求未授权
4、用户登出时,清出sessionId的缓存

代码

网关的代码

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpRequest request = exchange.getRequest();
		HttpHeaders headers = request.getHeaders();
		String path = exchange.getRequest().getURI().getPath();
		if (isSkip(path)) {
			return chain.filter(exchange);
		}
		if (Objects.nonNull(authProperties.getSkipAuth()) && authProperties.getSkipAuth()) {
			return chain.filter(exchange);
		}
		ServerHttpResponse resp = exchange.getResponse();
		String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
		String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
		if (StringUtils.isBlank(headerToken) && StringUtils.isBlank(paramToken)) {
			return unAuth(resp, "缺失令牌,鉴权失败");
		}
		String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken;
		String token = JwtUtil.getToken(auth);
		Claims claims = JwtUtil.parseJWT(token);
		if (claims == null) {
			return unAuth(resp, "请求未授权");
		}
		//是否开启纵向越权
		if (authProperties.getUltraVires()) {
			Mono<Void> resp1 = isUltraVires(headers, resp, token);
			if (resp1 != null) return resp1;
		}
		return chain.filter(exchange);
	}

private Mono<Void> isUltraVires(HttpHeaders headers, ServerHttpResponse resp, String token) {
		String cookie = headers.getFirst(TokenConstant.COOKIE);
		if(ObjectUtil.isEmpty(cookie)){
			return unAuth(resp, "不可越权,请求未授权");
		}
		String sessionId =cookie.replace("JSESSIONID=","").trim();
		RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
		String sessionKey = StringUtil.format(CacheNames.SESSION_KEY,sessionId);
		String redisToken = (String)redisUtil.get(sessionKey);
		if(ObjectUtil.isEmpty(redisToken)){
			return unAuth(resp, "不可越权,请求未授权");
		}
		if(!token.equals(redisToken)){
			return unAuth(resp, "不可越权,请求未授权");
		}
		return null;
	}

登陆器的代码

登录时:

/**
	 * 用于越权校验
	 * @param request
	 * @param authInfo
	 */
	private void setSessionRedis(HttpServletRequest request, AuthInfo authInfo) {
		String accessToken = authInfo.getAccessToken();
		String sessionId = request.getSession().getId();
		String sessionKey = StringUtil.format(CacheNames.SESSION_KEY,sessionId);
		redisUtil.set(sessionKey,accessToken);
	}

登出时:

@GetMapping("/login/out")
	@ApiOperation(value = "退出登录", notes = "退出登录")
	public R loginOut() {
		HttpServletRequest request = WebUtil.getRequest();
		String sessionId = request.getSession().getId();
		String sessionKey = StringUtil.format(CacheNames.SESSION_KEY,sessionId);
		redisUtil.del(sessionKey);
		return R.success();
	}

注意

根据YAML语法规范,参数名不能以"is"开头。这是因为以"is"开头的参数名可能与YAML中的布尔值表示冲突,导致解析错误。如果您需要使用以"is"开头的参数名,可以考虑使用引号将其括起来,或者选择其他不以"is"开头的命名方式。请参考YAML规范和相关文档以获取更详细的信息。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值