Springboot Shiro 诡异的POST请求第一次请求接收参数都为null 第二次请求正常

看现象:

第一次请求:

 

 

后端接收请求数据为null

Shirohttpservlet 里面参数size 为0

 

第二次请求:

 

 

var data = {id: 6,
    name: "cs",
    pid:"",
    description: "cs",
    available: 1};
$.ajax({type:"POST",url:"/types/edit",data:data ,success:function(result){
        $("#div1").html(result);
    }});

ajax 请求传值接收为null

 

ajax请求没有问题 前端数据已经过来了

ResourceUrlEncodingFilter

第一次提交时 postData为null

 

 

Fillder监听已传值

点击请求

 

接受到数据

 

2019-06-20 20:45:03 [com.zyd.blog.controller.RestArticleController:87] INFO  - ======isMarkdown=false&id=9&title=jmeter%E4%B8%ADhtml%E6%8A%A5%E5%91%8A%E7%94%9F%E6%88%90%E6%96%B9%E5%BC%8F&content=%3Ch1%3Etest%3C%2Fh1%3E&description=jmeter%E4%B8%ADhtml%E6%8A%A5%E5%91%8A%E7%94%9F%E6%88%90%E6%96%B9%E5%BC%8F+%26nbsp%3B+1%E3%80%81%E4%BD%BF%E7%94%A8%E5%91%BD%E5%90%8D%EF%BC%9A++%26gt%3Bjmeter+-g+loginWithPassword100c180s.jtl+-o+.%2Freport+%E8%A7%A3%E9%87%8A%EF%BC%9Ajmeter+&keywords=jmeter&typeId=2&tags=5&status=1&original=on&comment=on&top=on&recommended=on&file=&coverImage=http%3A%2F%2Fimg.dmanis.cn%2Foneblog%2F20190619130639308.png&commended-file=&commendedImage=
2019-06-20 20:45:03 [com.zyd.blog.controller.RestArticleController:90] INFO  - ======{}
2019-06-20 20:45:03 [com.zyd.blog.controller.RestArticleController:97] INFO  - ======Article(bizArticle=BizArticle(tags=null, bizType=null, title=null, userId=null, coverImage=null, commendImage=null, qrcodePath=null, isMarkdown=null, content=null, contentMd=null, top=null, typeId=null, status=null, recommended=null, original=null, description=null, keywords=null, comment=null, lookCount=null, commentCount=null, loveCount=null))
2019-06-20 20:45:05 [com.zyd.blog.controller.RestArticleController:87] INFO  - ======
2019-06-20 20:45:05 [com.zyd.blog.controller.RestArticleController:90] INFO  - ======{"isMarkdown":["false"],"id":["9"],"title":["jmeter中html报告生成方式"],"content":["<h1>test</h1>"],"description":["jmeter中html报告生成方式 &nbsp; 1、使用命名:  &gt;jmeter -g loginWithPassword100c180s.jtl -o ./report 解释:jmeter "],"keywords":["jmeter"],"typeId":["2"],"tags":["5"],"status":["1"],"original":["on"],"comment":["on"],"top":["on"],"recommended":["on"],"file":[""],"coverImage":["http://img.dmanis.cn/oneblog/20190619130639308.png"],"commended-file":[""],"commendedImage":[""]}
2019-06-20 20:45:05 [com.zyd.blog.controller.RestArticleController:97] INFO  - ======Article(bizArticle=BizArticle(tags=null, bizType=null, title=jmeter中html报告生成方式, userId=null, coverImage=http://img.dmanis.cn/oneblog/20190619130639308.png, commendImage=null, qrcodePath=null, isMarkdown=false, content=<h1>test</h1>, contentMd=null, top=true, typeId=2, status=1, recommended=true, original=true, description=jmeter中html报告生成方式 &nbsp; 1、使用命名:  &gt;jmeter -g loginWithPassword100c180s.jtl -o ./report 解释:jmeter , keywords=jmeter, comment=true, lookCount=null, commentCount=null, loveCount=null))
2019-06-20 20:45:07 [com.zyd.blog.business.aspect.BussinessLogAspect:65] INFO  - 进入文章列表页 | 0:0:0:0:0:0:0:1 - GET http://localhost:8085/articles - Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
 try {
            ServletInputStream inputStream = request.getInputStream();
            log.info("======" + StreamUtils.copyToString(inputStream, Charset.defaultCharset()));

            Map map  = request.getParameterMap();
            log.info("======" + JSON.toJSONString(map));
        }catch (Exception e){

        }
        if (article == null){
            log.info("====== null" );
        }else{
            log.info("======" + article.toString());
        }

 

前言

在SpringMVC web应用中,对于一个rest接口,获取请求参数我们一般使用@requestParam@requestBody等注解 。对于表单类型的请求参数,有一下几种获取方式

  1. @requestParam注解方式
  2. request.getParameter(String name)
  3. request.getInputStream()

前两种方式其实是一种方式,@requestParam底层就是利用request.getParameter的原理。这两种方式有一个弊端就是只能一个个获取,而且必须知道对方传过来的参数的key值,如果想要一次性获取,可以使用request.getInputStream方法获取一个inputStream对象,然后读取流里面的数据。

//获取到的数据格式key=value以‘&’分隔的形式
age=20&name=faderw

问题

但在实际过程中,我们会发现通过request.getInputStream()方式获取的数据为空。

根据Servlet规范,如果同时满足下列条件,则请求体(Entity)中的表单数据,将被填充到request的parameter集合中(request.getParameter系列方法可以读取相关数据)

  1. 这是一个HTTP/HTTPS请求
  2. 请求方法是POST(querystring无论是否POST都将被设置到parameter中)
  3. 请求的类型(Content-Type头)是application/x-www-form-urlencoded
  4. Servlet调用了getParameter系列方法

这里的表单数据已经被填充到parameterMap中,不能再通过getInputStream获取。



作者:Top_Bear
链接:https://www.jianshu.com/p/2d0d72ce2aee
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

这个有点坑,首先request的流的获取每个请求只能获取一次,之后再通过getInputStream获取流的时候就获取不到数据了,还有getInputStream和getReader和getParameter都可以获取输入流数据,但是存在冲突,也就是三者只要有一个对request获取了输入流信息,那么其他的方法之后就获取不到数据了。这就是springboot埋的一个小坑,那么怎么通过request获得Post请求的body值呢?
--------------------- 
作者:课么多巨蜥 
来源:CSDN 
原文:https://blog.csdn.net/qq_36005199/article/details/84562057 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

今天在项目中获取request的请求数据为空,消耗了一天的时间
百度了两篇文章解决了这个问题:原因 解决方案

  1. 阐述下问题:
    项目是记录请求数据及响应数据,但在获取请求数据时使用request.getInputStream()为空,而使用
	Enumeration enu=request.getParameterNames();  
	while(enu.hasMoreElements()){  
		String paraName=(String)enu.nextElement();  
		System.out.println(paraName+": "+request.getParameter(paraName));  
	}  

但是获取的值是不完整的,它将原数据前面部分作为参数key,后面部分作为参数value。

  1. 分析原因
    经过一顿操作,发现客户端上送的Content-Type的值为 这里可以看下

application/x-www-form-urlencoded;charset=UTF-8

而不是

text/xml
text/plain
application/json

所以请求数据的值是以key/value形式存储的。
若在使用request.getInputStream()前已经使用过getParameter或者@requestParam注解方式,则request.getInputStream()获取为空。

3 解决方案
1 将post请求的Content-Type 设置为text/xml
2 通过遍历获取所有参数,再获取参数值
3 避免在request.getInputStream()之前使用getParameter或者@requestParam注解方式
对httprequest进行修饰
定义InputStreamHttpServletRequestWrapper类继承于HttpServletRequestWrapper对请求数据进行处理


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.web.bind.annotation.RequestMethod;

public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper{

    private final byte[] streamBody;
    private static final int BUFFER_SIZE = 4096;
    
	//对请求数据判断,getInputStream()为空的数据对key和value值进行拼接
    public InputStreamHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        byte[] bytes = inputStream2Byte(request.getInputStream());
        if (bytes.length == 0 && RequestMethod.POST.name().equals(request.getMethod())) {
            //从ParameterMap获取参数,并保存以便多次获取
            bytes = request.getParameterMap().entrySet().stream()
                    .map(entry -> {
                        String result;
                        String[] value = entry.getValue();
                        if (value != null && value.length > 1) {
                            result = Arrays.stream(value).map(s -> entry.getKey() + "=" + s)
                                    .collect(Collectors.joining("&"));
                        } else {
                            result = entry.getKey() + "=" + value[0];
                        }

                        return result;
                    }).collect(Collectors.joining("&")).getBytes();
        }
        	//System.err.println(new String(bytes));
        streamBody = bytes;
    }

    private byte[] inputStream2Byte(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[BUFFER_SIZE];
        int length;
        while ((length = inputStream.read(bytes, 0, BUFFER_SIZE)) != -1) {
            outputStream.write(bytes, 0, length);
        }

        return outputStream.toByteArray();
    }


    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(streamBody);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}


写doFilterInternal类对InputStreamHttpServletRequestWrapper 进行调用即过滤器对请求的处理,该类继承于OncePerRequestFilter,一次请求只调用一次

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;


public class InputStreamWrapperFilter extends OncePerRequestFilter{

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
     HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        ServletRequest servletRequest = new InputStreamHttpServletRequestWrapper(httpServletRequest);
        filterChain.doFilter(servletRequest, httpServletResponse);
    }
}

最后确定过滤器调用的顺序
在application类中

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import com.springboot.SpringBootUtill.InputStreamWrapperFilter;

@SpringBootApplication
@Slf4j
public class Application
{
  public static void main(String[] args)
  {
    log.info("=================开始成功=================");
    SpringApplication.run(Application.class, args);
    log.info("=================启动成功=================");
  }
	@Bean
  @Order(1)
  public FilterRegistrationBean inputStreamWrapperFilterRegistration() {
      FilterRegistrationBean registrationBean = new FilterRegistrationBean();
      registrationBean.setFilter(new InputStreamWrapperFilter());
      registrationBean.setName("inputStreamWrapperFilter");
      registrationBean.addUrlPatterns("/*");
      return registrationBean;
  }
}
  •  

到此为止该问题已经解决了,可以获取到参数了

//获取请求body
	String getRequestMsg(HttpServletRequest request) throws IOException {
		BufferedReader br = request.getReader();
	    String str, wholeStr = "";   
	    while((str = br.readLine()) != null){
	        wholeStr += str;
	}
		return wholeStr;  
	}

现在还有个疑问,我并没有在request.getInputStream()前使用过getParameter或者@requestParam注解方式。是不是springboot在嵌入tomcat后,请求自动调用了getParameter方法呢。

https://www.jianshu.com/p/2d0d72ce2aee

https://www.cnblogs.com/dayou123123/p/3443939.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
在Spring Boot使用Shiro进行拦截Ajax请求,可以使用Shiro提供的Filter来实现。 首先,需要创建一个自定义的ShiroFilter,并在Spring Boot的配置文件进行配置: ```java @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, Filter> filters = new HashMap<>(); filters.put("ajax", new AjaxFilter()); shiroFilterFactoryBean.setFilters(filters); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/**", "ajax,user"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } } ``` 在上面的代码,我们创建了一个自定义的ShiroFilter,并设置了一个名为“ajax”的Filter。同时,我们也添加了一个FilterChain,将所有请求都拦截,并使用“ajax”和“user”两个Filter进行处理。 接下来,我们需要创建一个名为“AjaxFilter”的Filter,用于处理Ajax请求。在该Filter,我们可以判断请求是否为Ajax请求,如果是,则返回一个JSON对象,表示操作被拦截。 ```java public class AjaxFilter extends AccessControlFilter { @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { HttpServletRequest httpRequest = WebUtils.toHttp(request); String requestedWith = httpRequest.getHeader("x-requested-with"); return requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest"); } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpResponse = WebUtils.toHttp(response); httpResponse.setCharacterEncoding("UTF-8"); httpResponse.setContentType("application/json;charset=UTF-8"); PrintWriter out = httpResponse.getWriter(); out.println("{\"success\":false,\"message\":\"您没有权限进行该操作!\"}"); out.flush(); out.close(); return false; } } ``` 在上面的代码,我们重写了“isAccessAllowed”和“onAccessDenied”两个方法。在“isAccessAllowed”方法,我们判断请求是否为Ajax请求。如果是,则返回true,表示允许访问;否则,返回false,表示禁止访问。 在“onAccessDenied”方法,我们返回一个JSON对象,表示操作被拦截。在该方法,我们首先设置HTTP响应的字符编码和内容类型。然后,我们获取PrintWriter对象,并使用该对象输出JSON字符串。最后,我们关闭PrintWriter对象,并返回false,表示禁止访问。 最后,我们需要将“AjaxFilter”添加到Spring Boot的配置文件: ```properties shiro.filter.ajax=com.example.shiro.AjaxFilter ``` 这样,我们就可以在Spring Boot使用Shiro拦截Ajax请求了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未来AI编程

共鸣===鼓励 打赏您随意

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值