我正在尝试在Spring MVC 3中编写和开发自己的CSRF过滤器(有一些额外的培训让我这样做,所以请不要建议我使用Spring Security。我知道,谢谢! )
我的过滤器适用于除enctype =“multipart / form-data”之外的所有表单,所以实际上我无法从普通的HttpServletRequest获取请求参数。
我已经尝试将它投射到MultipartHttpServletRequest,但我发现我也做不到。
请注意,我的目标不是获取文件,只是简单的表单输入名为“csrf”。我已经用我的表格上传了文件。
这是我的代码,直到现在:
CSRFilter
public class CSRFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
CSRF csrf = new CSRF(req);
if(csrf.isOk()){
chain.doFilter(req, res);
}else {
//todo : Show Error Page
String redirect = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/access-forbidden";
response.sendRedirect(redirect);
}
}
}
CSRF
public class CSRF {
HttpServletRequest request;
ServletRequest req;
String token;
boolean ok;
private static final Logger logger = Logger.getLogger(CSRF.class);
public CSRF(ServletRequest request) {
this.request = (HttpServletRequest) request;
this.req = request;
init();
}
public CSRF() {
}
public void setRequest(HttpServletRequest request) {
this.request = (HttpServletRequest) request;
this.req = request;
init();
}
private void init() {
if (request.getMethod().equals("GET")) {
generateToken();
addCSRFTokenToSession();
addCSRFTokenToModelAttribute();
ok = true;
} else if (request.getMethod().equals("POST")) {
if (checkPostedCsrfToken()) {
ok = true;
}
}
}
private void generateToken() {
String token;
java.util.Date date = new java.util.Date();
UUID uuid = UUID.randomUUID();
token = uuid.toString() + String.valueOf(new Timestamp(date.getTime()));
try {
this.token = sha1(token);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
this.token = token;
}
}
private void addCSRFTokenToSession() {
request.getSession().setAttribute("csrf", token);
}
private void addCSRFTokenToModelAttribute() {
request.setAttribute("csrf", token);
}
private boolean checkPostedCsrfToken() {
System.out.println("____ CSRF CHECK POST _____");
if (request.getParameterMap().containsKey("csrf")) {
String csrf = request.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
}else {
//Check for multipart requests
MultipartHttpServletRequest multiPartRequest = new DefaultMultipartHttpServletRequest((HttpServletRequest) req);
if (multiPartRequest.getParameterMap().containsKey("csrf")) {
String csrf = multiPartRequest.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
}
}
log();
return false;
}
private void log() {
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
if(username==null){
username = "unknown (not logged in)";
}
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
String userAgent = request.getHeader("User-Agent");
String address = request.getRequestURI();
System.out.println("a CSRF attack detected from IP: " + ipAddress + " in address \"" + address + "\" - Client User Agent : " + userAgent + " Username: " + username);
logger.error("a CSRF attack detected from IP: " + ipAddress + " in address \"" + address + "\" - Client User Agent : " + userAgent + " Username: " + username);
}
public boolean isOk() {
return ok;
}
static String sha1(String input) throws NoSuchAlgorithmException {
MessageDigest mDigest = MessageDigest.getInstance("SHA1");
byte[] result = mDigest.digest(input.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < result.length; i++) {
sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
}
我的调度员也有这条线:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="40000000"/>
</bean>
我也使用springMultipartResolver过滤器......
<filter>
<display-name>springMultipartFilter</display-name>
<filter-name>springMultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springMultipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</filter>
当我在multipart / form-data表单上尝试时,我得到java.lang.IllegalStateException: Multipart request not initialized
异常。
我在互联网上看了很多例子。其中大部分用于文件上传目的并且无法帮助我,我也尝试了不同的方法将HttpServletRequest转换为任何其他对象,这些对象可以解决多部分请求,但我无法成功。
我该怎么做?
感谢。
答案 0 :(得分:2)
您无法将MultipartHttpServletRequest
投射到CommonsMultipartResolver
,因为您首先必须解决您的请求。
我使用了MultipartHttpServletRequest
类,并使用commonsMultipartResolver.resolveMultipart(request)
方法获取了HttpServletRequest
,其中请求的类型为checkPostedCsrfToken()
。
所以,这是我的 CSRF 类,private boolean checkPostedCsrfToken() {
if (request.getParameterMap().containsKey("csrf")) {
String csrf = request.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
} else if (request.getContentType() != null && request.getContentType().toLowerCase().contains("multipart/form-data")) {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
MultipartHttpServletRequest multipartRequest = commonsMultipartResolver.resolveMultipart(request);
if (multipartRequest.getParameterMap().containsKey("csrf")) {
String csrf = multipartRequest.getParameter("csrf");
if (csrf.equals(request.getSession().getAttribute("csrf"))) {
return true;
}
}
}
log();
return false;
}
方法:
HttpServletRequestWrapper
但是,请注意,您将丢失所有请求参数和数据。所以你必须扩展public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null)
cacheInputStream();
return new CachedServletInputStream();
}
@Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private void cacheInputStream() throws IOException {
/* Cache the inputstream in order to read it multiple times. For
* convenience, I use apache.commons IOUtils
*/
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/* An inputstream which reads the cached request body */
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
/* create a new input stream from the cached request body */
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
@Override
public int read() throws IOException {
return input.read();
}
}
}
类来读取请求字节并使用它们来获取参数,如果对你来说参数不会丢失抛出过滤器链。
这是我在StackOverflow中找到的一个很好的帮助器类,(我再也找不到问题了,如果找到它我会编辑它。)
MultiReadHttpServletRequest
MultiReadHttpServletRequest
现在您需要做的就是在过滤器中使用HttpServletRequest
而不是普通public class CSRFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request);
CSRF csrf = new CSRF(multiReadHttpServletRequest);
if(csrf.isOk()){
chain.doFilter(multiReadHttpServletRequest, res);
}else {
//todo : Show Error Page
String redirect = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/access-forbidden";
response.sendRedirect(redirect);
}
}
}
:
function removeItem(id) {
$.ajax({
url:"removeItem_checkOut.php",
type:"GET",
data:"id="+id,
success:function(content) {
window.location.reload();
}
});
}
我希望这有助于某人:)
答案 1 :(得分:2)
我需要能够在不损坏Servlet或后续过滤器的情况下检查Request的主体,因此我创建了一个小型项目。
罐子是&lt; 10kb,如果你使用的是Tomcat,那么除此之外你不需要任何东西。此外,它已获得MIT许可,因此您可以在任何可能需要的项目中使用它。
您可以在https://github.com/isapir/servlet-filter-utils
找到该项目您所要做的就是用RereadableServletRequest
包裹传入的请求,例如
HttpServletRequest requestWrapper = new RereadableServletRequest(servletRequest);