
{
"code": "INVALID_TEAM_NAME",
"message": "유효하지 않은 그룹(팀)명입니다."
}
{
"cause": null,
"stackTrace": [
{
"classLoaderName": "app",
"moduleName": null,
"moduleVersion": null,
"methodName": "invalidGroupNameExceptionHandler",
"fileName": "GlobalExceptionHandler.java",
"lineNumber": 18,
"className": "com.coderder.colorMeeting.exception.GlobalExceptionHandler",
"nativeMethod": false
},
{
"classLoaderName": null,
"moduleName": "java.base",
"moduleVersion": "11.0.16",
"methodName": "invoke0",
"fileName": "NativeMethodAccessorImpl.java",
"lineNumber": -2,
"className": "jdk.internal.reflect.NativeMethodAccessorImpl",
"nativeMethod": true
},
···, // 축약
{
"classLoaderName": null,
"moduleName": "java.base",
"moduleVersion": "11.0.16",
"methodName": "run",
"fileName": "Thread.java",
"lineNumber": 829,
"className": "java.lang.Thread",
"nativeMethod": false
}
],
"code": "INVALID_TEAM_NAME",
"message": "유효하지 않은 그룹(팀) 이름입니다.",
"suppressed": [],
"localizedMessage": "유효하지 않은 그룹(팀) 이름입니다."
}
e.printStackTrace(); 주석처리 → 실패JwtAuthenticationFilter
// DB에 저장된 값과 일치하는가?
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
// System.out.println("============== attempt Authentication starts ===============");
// 1) request에서 username, password 받아오기
// ObjectMapper는 json을 파싱한다
// User.class를 넣으면 username, password 외에 기타 정보도 다 가져온다. loginDto를 추가로 만들자
ObjectMapper om = new ObjectMapper();
LoginRequestDto loginRequestDto = om.readValue(request.getInputStream(), LoginRequestDto.class);
// 2) 토큰을 만들고 로그인을 시도한다
// am.authenticate()는 토큰의 파라메터(getUsername, getPassword)를 차례대로 검증한다
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginRequestDto.getUsername(), loginRequestDto.getPassword());
Authentication authentication =
authenticationManager.authenticate(authenticationToken);
// 로깅
PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal();
// System.out.println("================== Authentication : "+principalDetails.getMember().getUsername() + " ==================");
// System.out.println("============== attempt Authentication ends ===============");
// 3) authenticate를 성공하면 authentication을 만든다.
// attemptAuthentication의 리턴값은 session 영역에 저장된다.
// 권한 관리를 위해 session에 저장한다. 권한 관리를 안 하면 굳이 세션에 저장하지 않아도 된다.
return authentication;
} catch (StreamReadException e) {
e.printStackTrace();
} catch (DatabindException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
};
jwtUtil의 validateToken 메서드)public String validateToken( String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return TokenProperties.VALID;
} catch (ExpiredJwtException e) {
return TokenProperties.EXPIRED;
} catch ( JwtException | IllegalArgumentException | NullPointerException e) {
return TokenProperties.INVALID;
}
}Spring Security에서는 에러가 나지 않고, 오로지 Service에서 발생하는 오류인데 왜 Spring Security의 영향을 받아 그 방식대로 오류를 타는가? 왜 인증/인가 관련 오류로 한 번 더 래핑되는가??
👉🏻 RuntimeException을 상속한 커스텀 Exception을 스프링 시큐리티에서 인증/인가 관련 exception으로 다뤄주고 있을 확률 있음
왜 Spring Security를 타고 나면 stackTrace가 추가되는가???
👉🏻 이 부분은 발견하지 못함. 심지어 application.properties에서
server.error.include-stacktrace=never를 굳이 설정해주어도 소용 없음. 어디선가 직접 친절히 추가하는 것으로 보이는데, 아직 해당 부분을 발견하지 못했다.
stackTrace를 따라 한 번 스프링 시큐리티를 뜯어보면 어디서 문제가 발생하는지, 어떻게 이 에러가 타고 온 건지 확인할 수 있을지도!!?!1ErrorResponse가 JwtUtil을 거치며 String으로 바뀌며 무언가 추가로 붙는 줄 알았는데, 디버깅해보니 이미 ErrorResponse가 생성될 때부터 property로 stackTrace를 비롯한 이것 저것 생기더군요. 엥 이게 뭐지? 해서 ErrorResponse가보니.. ErrorResponse 클래스가 RuntimeException을 상속받게 하는, 어이없는 실수 때문이었습니다.
@Getter
@Builder
@AllArgsConstructor
public class ErrorResponse extends RuntimeException {
private String code;
private String message;
public ErrorResponse(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
}
@Getter
@Builder
@AllArgsConstructor
public class ErrorResponse {
private String code;
private String message;
public ErrorResponse(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
}
디버깅을 했을 때, 제대로 꼼꼼히 보지 않아서 (Ex. ErrorResponse의 property들까지 확인해보지 않아서) 오류의 발생 구역을 아예 잘못 파악했고, 그래서 헛삽질 하는 기간이 아주 길었다.
사실 아직 디버깅으로 오류를 찾는 방법에 익숙하지 못한 것 같다. 익숙해져야 하겠다.