我有一个REST API,其中包含spline-引导2.5.5和spline-Security 5.5.2。
但是当一些特殊(但在我的域上下文中有效)字符作为路径变量传递时,请求失败。
三个失败的特殊字符是:斜杠(/)、分号(;)和百分比(%)。
让我们举一个非常简单的例子:
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1")
public class Controller {
@GetMapping("echo/{value}")
public ResponseEntity<String> echo(@PathVariable("value") String value) {
return ResponseEntity
.status(HttpStatus.OK)
.contentType(MediaType.TEXT_XML)
.body(value);
}
}
对于值“你好/那里”,我发送GET /api/v1/echo/hello/there,我收到:
<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Message</b> Invalid URI: noSlash</p><p><b>Description</b> The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).</p><hr class="line" /><h3>Apache Tomcat/9.0.53</h3></body></html>
对于值“hello;there”,我发送GET/api/v1/echo/hello;在那里,我收到:
{
"stackTrace": [
{
"classLoaderName": "app",
"methodName": "handleAccessDeniedException",
"fileName": "ExceptionTranslationFilter.java",
"lineNumber": 194,
"nativeMethod": false,
"className": "org.springframework.security.web.access.ExceptionTranslationFilter"
},
{
"classLoaderName": "app",
"methodName": "handleSpringSecurityException",
"fileName": "ExceptionTranslationFilter.java",
"lineNumber": 173,
"nativeMethod": false,
"className": "org.springframework.security.web.access.ExceptionTranslationFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "ExceptionTranslationFilter.java",
"lineNumber": 142,
"nativeMethod": false,
"className": "org.springframework.security.web.access.ExceptionTranslationFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "ExceptionTranslationFilter.java",
"lineNumber": 115,
"nativeMethod": false,
"className": "org.springframework.security.web.access.ExceptionTranslationFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "SessionManagementFilter.java",
"lineNumber": 126,
"nativeMethod": false,
"className": "org.springframework.security.web.session.SessionManagementFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "SessionManagementFilter.java",
"lineNumber": 81,
"nativeMethod": false,
"className": "org.springframework.security.web.session.SessionManagementFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "AnonymousAuthenticationFilter.java",
"lineNumber": 105,
"nativeMethod": false,
"className": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "SecurityContextHolderAwareRequestFilter.java",
"lineNumber": 149,
"nativeMethod": false,
"className": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "RequestCacheAwareFilter.java",
"lineNumber": 63,
"nativeMethod": false,
"className": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "OncePerRequestFilter.java",
"lineNumber": 103,
"nativeMethod": false,
"className": "org.springframework.web.filter.OncePerRequestFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "LogoutFilter.java",
"lineNumber": 103,
"nativeMethod": false,
"className": "org.springframework.security.web.authentication.logout.LogoutFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "LogoutFilter.java",
"lineNumber": 89,
"nativeMethod": false,
"className": "org.springframework.security.web.authentication.logout.LogoutFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "OncePerRequestFilter.java",
"lineNumber": 103,
"nativeMethod": false,
"className": "org.springframework.web.filter.OncePerRequestFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "SecurityContextPersistenceFilter.java",
"lineNumber": 110,
"nativeMethod": false,
"className": "org.springframework.security.web.context.SecurityContextPersistenceFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "SecurityContextPersistenceFilter.java",
"lineNumber": 80,
"nativeMethod": false,
"className": "org.springframework.security.web.context.SecurityContextPersistenceFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "OncePerRequestFilter.java",
"lineNumber": 103,
"nativeMethod": false,
"className": "org.springframework.web.filter.OncePerRequestFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "ChannelProcessingFilter.java",
"lineNumber": 133,
"nativeMethod": false,
"className": "org.springframework.security.web.access.channel.ChannelProcessingFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 336,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilterInternal",
"fileName": "FilterChainProxy.java",
"lineNumber": 211,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "FilterChainProxy.java",
"lineNumber": 183,
"nativeMethod": false,
"className": "org.springframework.security.web.FilterChainProxy"
},
{
"classLoaderName": "app",
"methodName": "invokeDelegate",
"fileName": "DelegatingFilterProxy.java",
"lineNumber": 358,
"nativeMethod": false,
"className": "org.springframework.web.filter.DelegatingFilterProxy"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "DelegatingFilterProxy.java",
"lineNumber": 271,
"nativeMethod": false,
"className": "org.springframework.web.filter.DelegatingFilterProxy"
},
{
"classLoaderName": "app",
"methodName": "internalDoFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 189,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 162,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilterInternal",
"fileName": "RequestContextFilter.java",
"lineNumber": 100,
"nativeMethod": false,
"className": "org.springframework.web.filter.RequestContextFilter"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "OncePerRequestFilter.java",
"lineNumber": 119,
"nativeMethod": false,
"className": "org.springframework.web.filter.OncePerRequestFilter"
},
{
"classLoaderName": "app",
"methodName": "internalDoFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 189,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 162,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "OncePerRequestFilter.java",
"lineNumber": 103,
"nativeMethod": false,
"className": "org.springframework.web.filter.OncePerRequestFilter"
},
{
"classLoaderName": "app",
"methodName": "internalDoFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 189,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 162,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "OncePerRequestFilter.java",
"lineNumber": 103,
"nativeMethod": false,
"className": "org.springframework.web.filter.OncePerRequestFilter"
},
{
"classLoaderName": "app",
"methodName": "internalDoFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 189,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "doFilter",
"fileName": "ApplicationFilterChain.java",
"lineNumber": 162,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationFilterChain"
},
{
"classLoaderName": "app",
"methodName": "invoke",
"fileName": "ApplicationDispatcher.java",
"lineNumber": 711,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationDispatcher"
},
{
"classLoaderName": "app",
"methodName": "processRequest",
"fileName": "ApplicationDispatcher.java",
"lineNumber": 461,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationDispatcher"
},
{
"classLoaderName": "app",
"methodName": "doForward",
"fileName": "ApplicationDispatcher.java",
"lineNumber": 385,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationDispatcher"
},
{
"classLoaderName": "app",
"methodName": "forward",
"fileName": "ApplicationDispatcher.java",
"lineNumber": 313,
"nativeMethod": false,
"className": "org.apache.catalina.core.ApplicationDispatcher"
},
{
"classLoaderName": "app",
"methodName": "custom",
"fileName": "StandardHostValve.java",
"lineNumber": 403,
"nativeMethod": false,
"className": "org.apache.catalina.core.StandardHostValve"
},
{
"classLoaderName": "app",
"methodName": "status",
"fileName": "StandardHostValve.java",
"lineNumber": 249,
"nativeMethod": false,
"className": "org.apache.catalina.core.StandardHostValve"
},
{
"classLoaderName": "app",
"methodName": "throwable",
"fileName": "StandardHostValve.java",
"lineNumber": 344,
"nativeMethod": false,
"className": "org.apache.catalina.core.StandardHostValve"
},
{
"classLoaderName": "app",
"methodName": "invoke",
"fileName": "StandardHostValve.java",
"lineNumber": 169,
"nativeMethod": false,
"className": "org.apache.catalina.core.StandardHostValve"
},
{
"classLoaderName": "app",
"methodName": "invoke",
"fileName": "ErrorReportValve.java",
"lineNumber": 92,
"nativeMethod": false,
"className": "org.apache.catalina.valves.ErrorReportValve"
},
{
"classLoaderName": "app",
"methodName": "invoke",
"fileName": "StandardEngineValve.java",
"lineNumber": 78,
"nativeMethod": false,
"className": "org.apache.catalina.core.StandardEngineValve"
},
{
"classLoaderName": "app",
"methodName": "service",
"fileName": "CoyoteAdapter.java",
"lineNumber": 357,
"nativeMethod": false,
"className": "org.apache.catalina.connector.CoyoteAdapter"
},
{
"classLoaderName": "app",
"methodName": "service",
"fileName": "Http11Processor.java",
"lineNumber": 382,
"nativeMethod": false,
"className": "org.apache.coyote.http11.Http11Processor"
},
{
"classLoaderName": "app",
"methodName": "process",
"fileName": "AbstractProcessorLight.java",
"lineNumber": 65,
"nativeMethod": false,
"className": "org.apache.coyote.AbstractProcessorLight"
},
{
"classLoaderName": "app",
"methodName": "process",
"fileName": "AbstractProtocol.java",
"lineNumber": 893,
"nativeMethod": false,
"className": "org.apache.coyote.AbstractProtocol$ConnectionHandler"
},
{
"classLoaderName": "app",
"methodName": "doRun",
"fileName": "NioEndpoint.java",
"lineNumber": 1726,
"nativeMethod": false,
"className": "org.apache.tomcat.util.net.NioEndpoint$SocketProcessor"
},
{
"classLoaderName": "app",
"methodName": "run",
"fileName": "SocketProcessorBase.java",
"lineNumber": 49,
"nativeMethod": false,
"className": "org.apache.tomcat.util.net.SocketProcessorBase"
},
{
"classLoaderName": "app",
"methodName": "runWorker",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 1191,
"nativeMethod": false,
"className": "org.apache.tomcat.util.threads.ThreadPoolExecutor"
},
{
"classLoaderName": "app",
"methodName": "run",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 659,
"nativeMethod": false,
"className": "org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker"
},
{
"classLoaderName": "app",
"methodName": "run",
"fileName": "TaskThread.java",
"lineNumber": 61,
"nativeMethod": false,
"className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable"
},
{
"moduleName": "java.base",
"moduleVersion": "17.0.2",
"methodName": "run",
"fileName": "Thread.java",
"lineNumber": 833,
"nativeMethod": false,
"className": "java.lang.Thread"
}
],
"type": "about:blank",
"title": "Unauthorized",
"status": "UNAUTHORIZED",
"detail": "Full authentication is required to access this resource",
"message": "Unauthorized: Full authentication is required to access this resource",
"localizedMessage": "Unauthorized: Full authentication is required to access this resource"
}
对于值“hello%there”,我在那里发送GET/api/v1/echo/hello%,并收到与分号相同的结果。
任何其他特殊字符似乎都被 Spring 正确解码,但不是这 3 个。
我错过了什么吗
有没有什么好的方法来实现这一点,而不必告诉spring“嘿,不要解码路径变量,我会自己做”,也不必搞乱安全配置(如中所述https://www.baeldung.com/spring-slash-character-in-url) ?
您是否尝试在特殊字符前使用< code>\或< code>`?
类似于hello\;那里
为了达到你的要求,你将不得不在tomcat
中进行一些深度配置和/或一些自定义重写规则。所有这些都很容易适得其反,因为在应用程序的uri匹配器上造成故障,甚至更糟的是造成安全故障。
最简单的方法是将控制器从期望包含特殊字符的字符串作为@PathVariable
转换为具有@RequestParam
的查询参数。
因此,而不是使用
@GetMapping("echo/{value}")
public ResponseEntity<String> echo(@PathVariable("value") String value) {
return ResponseEntity
.status(HttpStatus.OK)
.contentType(MediaType.TEXT_XML)
.body(value);
}
你可能会变成
@GetMapping("echo/")
public ResponseEntity<String> echo(@RequestParam("value") String value) {
return ResponseEntity
.status(HttpStatus.OK)
.contentType(MediaType.TEXT_XML)
.body(value);
}
对于值“hello/there ”,我发送GET /api/v1/echo/hello/there
然后尝试使用 GET /api/v1/echo?value=hello/there
对于Getmapping,您可以选择这种方式。
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/api/v1")
public class Controller {
@GetMapping("echo/**")
public ResponseEntity<String> echo(HttpServletRequest request) {
String fullUrl = request.getRequestURL().toString();
String url = fullUrl.split("/echo/")[1];
System.out.println(url);
return ResponseEntity
.status(HttpStatus.OK)
.contentType(MediaType.TEXT_XML)
.body(url);
}
}
SWIG文档对这两个指令解释如下: > :“SWIG提供了另一个带有指令的文件包含指令。的目的是从另一个SWIG接口文件或头文件收集某些信息,而不实际生成任何包装代码。此类信息通常包括类型声明(例如,typedef)以及可能用作接口中类声明基类的C类。" 我的问题是这两个指令之间有什么区别,使用它们的利弊是什么? 顺便说一下,我只是想了解一些背景信息。我有一个简单的C-python扩展,当我使用上
我已经阅读了这个类似问题的答案:YAML中的字符串是否需要引号? 然而,对于是否可以用包含正斜杠的字符串转义引号,没有答案。 例如,我们是否需要将引号添加到以下eslint规则“react/no deprecated”:off?
SpringMVC控制器中@PathVariable有问题。每当我传递包含plus('+')的字符串时,plus就会被空格替换。对参数进行编码没有帮助。 例如,如果我请求url myapp/resend-validation/my+mail@gmail.com,我在变量中得到“mymail@gmail.com”。在请求myapp/resend-validation/my%2bmail@gmail.
我有一个shell脚本(我们称之为 ),它以下列形式输出数据,但不将其保存到文件中: 我想在外壳脚本中使用这些值作为环境变量。我目前正在使用以下shell代码执行此操作: 这非常有用,除非< code>=右边的值包含空格。例如: 我在<code>product.sh</code>中尝试了两种不同的方法: < li >用引号将值括起来(< code > FOO = " split value " )
问题内容: 这让我发疯了,所以请您帮忙… 我有一个Java字符串,我想用单引号替换所有反斜杠双引号序列,即使我逃避了我认为必要的替换命令,该命令也不会对字符串产生任何影响。 感谢任何建议。 谢谢。 问题答案: 在Java中,字符串是不可变的。您对字符串执行的任何操作都会产生新的对象。操作后,您需要重新分配值。以下内容可能会对您有所帮助。
null Groovy:找不到四位十六进制字符代码