[SpringWebFlux + jwt] Jwts.parserBuilder blocking

Wjong·2024년 3월 18일
0
2024-03-18T22:06:28.399+09:00 ERROR 38144 --- [service-back] [     parallel-3] a.w.r.e.AbstractErrorWebExceptionHandler : [826b2ae3-2]  500 Server Error for HTTP POST "/user/refresh"

reactor.blockhound.BlockingOperationError: Blocking call! java.io.RandomAccessFile#readBytes
	at java.base/java.io.RandomAccessFile.readBytes(RandomAccessFile.java) ~[na:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP POST "/api/user/refresh" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at java.base/java.io.RandomAccessFile.readBytes(RandomAccessFile.java) ~[na:na]
		at java.base/java.io.RandomAccessFile.read(RandomAccessFile.java:405) ~[na:na]
		at java.base/java.io.RandomAccessFile.readFully(RandomAccessFile.java:469) ~[na:na]
		at java.base/java.util.zip.ZipFile$Source.readFullyAt(ZipFile.java:1512) ~[na:na]
		at java.base/java.util.zip.ZipFile$ZipFileInputStream.initDataOffset(ZipFile.java:924) ~[na:na]
		at java.base/java.util.zip.ZipFile$ZipFileInputStream.read(ZipFile.java:940) ~[na:na]
		at java.base/java.util.zip.ZipFile$ZipFileInflaterInputStream.fill(ZipFile.java:457) ~[na:na]
		at java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158) ~[na:na]
		at java.base/java.io.InputStream.readNBytes(InputStream.java:506) ~[na:na]
		at java.base/java.util.jar.JarFile.getBytes(JarFile.java:814) ~[na:na]
		at java.base/java.util.jar.JarFile.checkForSpecialAttributes(JarFile.java:1004) ~[na:na]
		at java.base/java.util.jar.JarFile.isMultiRelease(JarFile.java:388) ~[na:na]
		at java.base/java.util.jar.JarFile.getEntry(JarFile.java:510) ~[na:na]
		at java.base/sun.net.www.protocol.jar.URLJarFile.getEntry(URLJarFile.java:131) ~[na:na]
		at java.base/sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:135) ~[na:na]
		at java.base/sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:175) ~[na:na]
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.parse(ServiceLoader.java:1172) ~[na:na]
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.nextProviderClass(ServiceLoader.java:1213) ~[na:na]
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1228) ~[na:na]
		at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1273) ~[na:na]
		at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1309) ~[na:na]
		at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1393) ~[na:na]
		at io.jsonwebtoken.impl.lang.Services.loadFirst(Services.java:110) ~[jjwt-impl-0.11.5.jar:0.11.5]
		at io.jsonwebtoken.impl.lang.Services.loadFirst(Services.java:100) ~[jjwt-impl-0.11.5.jar:0.11.5]
		at io.jsonwebtoken.impl.DefaultJwtParserBuilder.build(DefaultJwtParserBuilder.java:191) ~[jjwt-impl-0.11.5.jar:0.11.5]
		at com.ssdc.serviceback.global.auth.JwtService.validateRefreshToken(JwtService.java:83) ~[main/:na]
		at com.ssdc.serviceback.domain.user.service.UserService.refreshAccessToken(UserService.java:33) ~[main/:na]
		at com.ssdc.serviceback.domain.user.controller.UserController.refreshToken(UserController.java:51) ~[main/:na]

SpringWebFlux + Spring Security6.1 에서 jwt 사용중 blockhound에의해 blocking이 발생했다.
원인 코드는 아래와 같다

    public boolean validateAccessToken(String token) {
        try {
            Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
            return claims.getExpiration().after(new Date());
        } catch (Exception e) {
            return false;
        }
    }

Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
이 부분에서 blocking이 발생했는데, 도통 해결방안을 찾지못하다가 드디어 찾았다.


https://stackoverflow.com/questions/76199457/jwts-parserbuilder-is-blocking



@Service
public class JwtService {


    final private JwtParser parser;

    public JwtService(){
        this.parser = Jwts.parserBuilder().setSigningKey(this.key).build();
    }
...

    public boolean validateAccessToken(String token) {
        try {
            Claims claims = parser.parseClaimsJws(token).getBody();
            return claims.getExpiration().after(new Date());
        } catch (Exception e) {
            return false;
        }
    }

}

JwtParser 인스턴스를 미리 생성 후, 재사용하는 방식을 이용한다.

profile
뉴비

0개의 댓글