Shiro导致request.getInputStream()无法获取数据

本文探讨了Content-Type为application/x-www-form-urlencoded时,在REST请求中遇到的问题。特别是当使用Shiro过滤器时,该过滤器可能会提前解析请求体,导致后续处理无法读取数据。文中提供了解决方案,并讨论了不同Content-Type的影响。

之前参照:http://blog.youkuaiyun.com/kimmking/article/details/17505637

遇到的情况情况很相似

通过核心HttpURLConnection连接当前新开发的医疗系统,由于当前conn方法已经连接过其他旧系统,没有问题;

当连接此系统时发现问题,新系统中用request.getInputStream()接受数据流都是NULL

之前考虑到是由于Shiro或者什么之类的拦截导致;

幸好找到这篇文章,可以解决




今天发现一个很奇怪的问题,我们系统里用REST方式做前后端的访问,具体就是所有的请求都是POST,URL对应处理的action,HTTP body里的json是请求参数;后端程序里从Request.getReader拿到json参数,然后调用相应的action来处理。有一个登陆接口,在页面ajax和Android里请求是正常的,但是在iphone里请求出错。调试了下,发现request.getReader拿不到数据。

         通过tcpmon代理查看HTTP请求报文,对比下正确和错误的REST请求,感觉最大的问题可能是出现在Content-type上,进而发现application/x-www-form-urlencoded时,会出现这个问题,但是text/html,text/xml等格式都没有问题。这就能确定是Content-Type引起的了。

         为什么Content-Type会引起这个问题呢?

         我们知道request.getInputStream\getReader,都只能调用一次,调用过了,buffer里的数据就没有了。而request.getParameterXX方法也可能会解析buffer,如果这个调用过了,再调用getReader也会没有数据了。难道Content-Type不同的时候导致调用了这个?

         调试下,追了追shiro和tomcat的代码,发现果然如此。

请求进来先进过shiro的filter,filter里试图拿到当前subject,先从cookie试着拿sessionId,不行就从URL中试着拿sessionId,还没有的话就从request.getParameter里获取sessionId,这里调用到了tomcat的request.getParameter实现:如果Content-Type是multipart/form-data或者application/x-www-form-urlencoded,则直接解析http body……问题就出现了。

         简单的说就是,如果请求是application/x-www-form-urlencoded格式的话,shiro的filter在我们的servlet或action处理之前,就可能直接把request的body给读取并清空了。

通过这个实现我们也可以看到:除了这两个Content-Type之外的格式都不会有问题,登陆后sessionId放到cookie里了也不会有问题。

按理讲,我们用json应该手动设置Content-Type:application/json;因为application/x-www-form-urlencoded就是为了在body里使用类似QueryString的key-value格式。所以写代码的时候要注意了,如果某些HTTP库的实现里,默认POST是form格式的,如果你要自己处理HTTP body就需要手工的设置自己想要的Content-Type了。

         知道怎么回事了,就很好处理了。在xcode里,将Content-Type改成text/html或application/json即可。

         进一步的来看,shiro处理考虑到sessionId在cookie和url里,还考虑到一种情况:session藏在页面form的hidden里,通过form的post方式,把key-value作为http body来post到服务器端,shiro可能通过解析httpbody拿到sessionId,这个对session来说用处不大,因为我们一般都放到cookie或url的jsessionId里,但是对于某些需要自己处理http body的场景,shiro的filter显然破坏掉了后续对body的request.getReader读取。


### Shiro 在无 JSP 环境下传递 Shell 的实现方式 在 Shiro 框架中,通常通过认证和授权机制来管理用户会话。如果需要在无 JSP 的环境下实现 Shell 的传递,可以通过以下方法完成: 1. **基于 RESTful API 的实现**: 在无 JSP 的环境中,可以使用 RESTful API 来处理用户的请求和响应。通过 JSON 或 XML 格式的数据交互,将 Shell 命令的执行结果返回给客户端。 2. **利用 Controller 处理请求**: 参考提供的代码示例[^2],可以在 `Controller` 中定义一个接口用于接收 Shell 命令并返回执行结果。例如,可以通过 `Runtime.getRuntime().exec()` 方法执行系统命令,并将结果封装为 JSON 格式返回。 3. **确保安全性**: 在实现过程中,必须对输入进行严格校验,防止恶意命令注入攻击。可以通过白名单机制限制可执行的命令。 以下是具体的代码实现示例: ```java @RestController @RequestMapping("shell") public class ShellController { @PostMapping("execute") public ResponseEntity<String> executeShell(@RequestBody Map<String, String> request) { String command = request.get("command"); // 输入校验(白名单机制) if (!isValidCommand(command)) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid command"); } try { Process process = Runtime.getRuntime().exec(command); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); StringBuilder output = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { output.append(line).append("\n"); } return ResponseEntity.ok(output.toString()); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error executing command: " + e.getMessage()); } } private boolean isValidCommand(String command) { // 定义允许的命令列表 List<String> allowedCommands = Arrays.asList("ls", "pwd", "whoami"); return allowedCommands.contains(command.split(" ")[0]); } } ``` ### 注意事项 - **权限控制**: 使用 Shiro 对访问该接口的用户进行权限控制,确保只有授权用户才能调用该接口。可以通过 `Subject` 对象检查用户是否具有特定权限。 - **日志记录**: 记录所有 Shell 命令的执行情况,便于后续审计和问题排查。 - **环境隔离**: 如果可能,建议在沙盒环境中执行 Shell 命令,避免对生产环境造成影响。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值