HttpServletRequest InputStream 关闭 Spring Boot

问题描述 投票:0回答:1

我正在使用 SpringBoot (Kotlin) 开发一个应用程序,并且正在使用带有 Spring Boot Security 的身份验证过滤器。

过滤器如下所示:

class JWTAuthenticationFilter(private val tokenAuthenticationService:   TokenAuthenticationService) : GenericFilterBean() {

    @Throws(IOException::class, ServletException::class)
    override fun doFilter(request: ServletRequest, response: ServletResponse,  filterChain: FilterChain) {

    val authentication = tokenAuthenticationService.getAuthentication(request as HttpServletRequest)
    SecurityContextHolder.getContext().authentication = authentication
    filterChain.doFilter(request, response)
    }
 }

fun getAuthentication(request: HttpServletRequest): Authentication? {
    val token = request.getHeader(AUTHORIZATION_HEADER_KEY)

    if (token != null) {
        val credentials = ObjectMapper().readValue(request.inputStream, BaseRequest::class.java)
        val user = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
            .body.subject

        if (user != null && credentials.userName != null && user == credentials.userName) {
            return UsernamePasswordAuthenticationToken(user, null, emptyList<GrantedAuthority>())
        }
    }

    return null
}

对于没有变量的控制器方法,此方法有效并执行身份验证。对于具有请求正文的控制器方法,这不起作用,因为输入流被报告为已关闭。我从其他 SO 问题中读到,控制器方法在将请求正文添加到 HttpServletRequest 时读取输入流。它是否正确 ?在这种情况下,它在这里做什么?我已经阅读过有关 InputStream 缓存的内容,但在这种情况下,我不知道何时第一次读取输入流以缓存读取的值。

我在下面给出了控制器方法的代码:

@PostMapping
@RequestMapping("/status")
fun status(@Validated @RequestBody gameDetails: CreateGameRequest): ResponseEntity<GameVO> {
    val gameRoundPair = gameService.giveGameStatus(gameDetails.gameName)
    return ResponseEntity.ok(gameAssembler.toGameVO(gameRoundPair.first, gameRoundPair.second))
}

@PostMapping
@RequestMapping("/available_users")
fun get_available_users(): ResponseEntity<UsernameListVO> {
    val response = userService.getLoggedInUsers()
    return ResponseEntity.ok(userAssembler.toUsernameListVO(response))
}
spring-boot servlets spring-security
1个回答
0
投票

经过几个小时的搜索,我终于解决了这个问题。缓存是在添加到 SpringBoot Security 的过滤器链中的过滤器中进行的,该过滤器在其他过滤器之前运行。该过滤器如下所示:

class CachingRequestBodyFilter() : OncePerRequestFilter() {

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
         if (request.method.equals(HttpMethod.POST.name)) {
             var requestWrapper = CachingRequestWrapper(request)
             filterChain.doFilter(requestWrapper, response)
         } else {
             filterChain.doFilter(request, response)
         }
    }

      class CachingRequestWrapper(request: HttpServletRequest) : HttpServletRequestWrapper(request) {
         private var requestBody: String;

         init {
              val reader = BufferedReader(request.inputStream.reader())
              val content = StringBuilder()
              reader.use { reader ->
               var line = reader.readLine()
               while (line != null) {
                    content.append(line)
                    line = reader.readLine()
               }
            }
            requestBody = content.toString()
        }

        override fun getInputStream(): ServletInputStream {
             val byteArrayInputStream = requestBody.byteInputStream()
             val obj = object : ServletInputStream() {
                override fun read() : Int {
                    return byteArrayInputStream.read()
                }
                override fun isFinished(): Boolean {
                    return byteArrayInputStream.available() == 0
                }
                override fun isReady(): Boolean {
                    return true
                }
                override fun setReadListener(readListener: ReadListener): Unit {
                }
              }
             return obj
         }
    }
}

然后在身份验证过滤器中,我执行以下操作以从请求中读取凭据:

 if (request is CachingRequestBodyFilter.CachingRequestWrapper) {
            val objectMapper = ObjectMapper()
          objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            val credentials = objectMapper.readValue((request as CachingRequestBodyFilter.CachingRequestWrapper).inputStream, BaseRequest::class.java)
      ....................................
 }

这里我使用 Jackson 库从缓存的请求字符串中反序列化。

© www.soinside.com 2019 - 2024. All rights reserved.