关于java项目越权问题
- 问题描述
- 实现思路
- 具体代码
项目上线前做了安全扫描,安全部门扫描出一个关于越权的问题。这个问题是在刚开始开发接口的时候没有考虑到的一个事情。(此项目是有关于用户所拥有的项目和活动权限的问题。)
问题描述
首先一个用户进到列表页面,点击编辑一个项目,然后通过抓包,得到调取后台接口的参数列表信息,然后知道参数列表里有id信息,这样用户如果知道其他项目id,或者这有查看权限,没有编辑项目的项目id,通过修改id,就可以越权,对没有权限的项目进行修改。
实现思路

- 首先用户登录时,通过Shiro框架,将用户信息放入Subject中,并将该用户对用的项目id和权限id放入redis中。
- 再分析并整理对应相关越权的接口,将这些接口整理,并且添加拦截器,将这些接口进行拦截。
- 拦截之后,从Shiro框架的Subject获取用户信息,根据用户信息查询该用户对应的项目id或活动id。
- 因为有些接口对项目操作,有的接口只对活动操作,有的接口对这两个都操作,所以我选择先判读项目id是否为空,然后再判读项目id存不存在在redis中,不存在直接返回false,若存在,再判断活动id的情况。

具体代码
1.拦截器配置类 CrossPowerConfig.java
import com.exp.core.interceptor.CrossPowerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;import java.util.List;@Configuration
public class CrossPowerConfig implements WebMvcConfigurer {@Autowiredprivate CrossPowerInterceptor crossPowerInterceptor;@Overridepublic void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {}@Overridepublic void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {}@Overridepublic void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {}@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {}@Overridepublic void addFormatters(FormatterRegistry formatterRegistry) {}@Overridepublic void addInterceptors(InterceptorRegistry registry) {// addPathPatterns("/**") 表示拦截所有的请求,registry.addInterceptor(crossPowerInterceptor).addPathPatterns(//要拦截的接口地址"/aaa/bbb","/aaa/ccc"...);}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {}@Overridepublic void addCorsMappings(CorsRegistry corsRegistry) {}@Overridepublic void addViewControllers(ViewControllerRegistry viewControllerRegistry) {}@Overridepublic void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {}@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {}@Overridepublic void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {}@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> list) {}@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> list) {}@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {}@Overridepublic void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {}@Overridepublic Validator getValidator() {return null;}@Overridepublic MessageCodesResolver getMessageCodesResolver() {return null;}
}
2.拦截器执行操作类 CrossPowerInterceptor.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class CrossPowerInterceptor implements HandlerInterceptor {private static Logger log = LoggerFactory.getLogger(CrossPowerInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {log.info("--------------------->进入越权相关拦截器---------------------");dosomething...//判断符合条件boolean flag=true;if(flag){return true;}else {return false;}}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}
}
当到这以为大功告成的时候,发现有一个接口,传数据的形式是JSON,当在拦截器里request.getinputstream()的时候,通过了拦截器,返回了true,进入接口的时候request.getinputstream()就获取为空,然后想到request.getinputstream()只能获取一次的问题,所以又考虑添加过滤器,在过滤器里将获取请求中的流,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
这时候整体的流程就变成了

添加的具体代码如下:
1.HttpServletRequestReplacedFilter.java
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class HttpServletRequestReplacedFilter implements Filter {@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {ServletRequest requestWrapper = null;if(request instanceof HttpServletRequest) {requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);}//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。// 在chain.doFiler方法中传递新的request对象if(requestWrapper == null) {chain.doFilter(request, response);} else {chain.doFilter(requestWrapper, response);}}@Overridepublic void init(FilterConfig arg0) throws ServletException {}
}
2.RequestReaderHttpServletRequestWrapper.java
import com.exp.util.HttpHelper;import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{private final byte[] body;public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {super(request);body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}
}
3.HttpHelper.java
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;public class HttpHelper {public static String getBodyString(HttpServletRequest request) throws IOException {StringBuilder sb = new StringBuilder();InputStream inputStream = null;BufferedReader reader = null;try {inputStream = request.getInputStream();reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));String line = "";while ((line = reader.readLine()) != null) {sb.append(line);}} catch (IOException e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (reader != null) {try {reader.close();} catch (IOException e) {e.printStackTrace();}}}return sb.toString();}
}
4.启动类 App.java
import com.exp.core.filter.HttpServletRequestReplacedFilter;
import com.exp.core.listener.MyApplicationContextListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;/*** 程序启动入口*/
@SpringBootApplication
@EnableTransactionManagement
public class App {public static void main(String[] args) {SpringApplication app = new SpringApplication(App.class);//注册监听器app.addListeners(new MyApplicationContextListener());app.run(args);}/*** 功能描述:* 设置过滤,getinputstream取request数据时,再放回request中。* @Param: []* @Return: org.springframework.boot.web.servlet.FilterRegistrationBean* @Author: * @Date: 2020/9/14 14:58*/@Beanpublic FilterRegistrationBean httpServletRequestReplacedRegistration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(new HttpServletRequestReplacedFilter());registration.addUrlPatterns(//要拦截的接口地址"/aaa/bbb","/aaa/ccc"...);registration.addInitParameter("paramName", "paramValue");registration.setName("httpServletRequestReplacedFilter");registration.setOrder(1);return registration;}}


















