[Spring] 401 / 403 에러 뜨는데 뭘 잘못한거죠?

Dev_ch·2023년 1월 27일
0
post-thumbnail

평소와 같이 하루를 보내던 도중 지인한테 연락이 왔다. 진행하던 개인 프로젝트 도중에 자꾸 401 에러가 떠서 한번 봐달라고 해서 집으로 돌아와 코드를 확인하고 해당 지인의 에러를 해결했다. 개발이 항상 그렇듯, 사소한거에서 틀리거나 놓쳐서 되게 어이없는 경우가 많은데 이번에도 그랬다. 아무튼 이번 기회를 삼아 이번 에러를 해결하기까지의 과정과 401 / 403에 대해 조금 자세히 정리해보려 한다.

1. 상태코드 401과 403에 대해서

💡 상태코드 401과 403이 헷갈려요!

상태코드 401403은 인증되지 않은 사용자라는 공통점이 있어 헷갈리지만 인증이 왜(why) 되지 않았냐에 집중하면 쉽게 나눠진다. 각각의 상태코드에 대한 설명을 먼저 살펴보자.


401(Unauthorized)

  • 해당 리소스에 유효한 인증 자격 증명이 없기 때문에 요청이 적용되지 않았음을 나타냄
  • 사용자가 로그인 없이 리소스를 요청한 경우

403 (Forbidden)

  • 서버에 요청이 전달되었지만, 권한 때문에 거절되었다는 것을 의미함
  • 관리자 페이지를 일반 회원이 요청한경우

🤔 401 과 403의 왜(Why?)

상태코드 401이 왜(why) 표시되나면 사용자가 어떠한 방식(ex.로그인)을 통해 자격 증명을 얻지 않고 자격 증명이 필요한 페이지에 접근하려 해서이다. 대신 계속해서 인증이 가능하다.

그에 반해 403이 왜(why) 표시되는 이유는 로그인을 하였더라도 자격 증명이 일반 유저로 제공되어있음에도 관리자 자격 증명이 필요한 페이지에 접근하려해서이다. 그렇기 때문에 페이지가 원하는 권한이 아닌 경우 재인증을 하더라도 지속적으로 접속을 거부하게 되어 인증이 불가능하다.

2. 그렇다면 지인의 401은 왜 발생했을까?

일단 위의 내용처럼 자격 증명을 얻지 않고 자격 증명이 필요한 요청을 해서이다.
지인의 코드는 로그인을 하는 API가 자꾸 자격 증명을 필요로 해서 문제가 발생했고 당연하게도 로그인 API는 분명 자격 증명이 필요없이 전체 허용이 되어야 하는 API 이기 때문에 수정이 필요했다.

에러를 해결하기 위해 필자가 먼저 확인했던 항목들은 아래와 같다.

  • SecurityCofing 에서 요청한 API가 자격 증명이 필요없게끔 전체 허용으로 해주었는지 -> YES
    • .antMatchers("/auth/signup", "/auth/signin").permitAll()
  • Controller에서 허용한 API 주소와 작성한 API 주소가 같은지 -> YES

API 주소나 설정 관련 코드들은 문제가 없어 Service 클래스 에서 로그를 찍어보았고 해당 부분에서 코드가 제대로 작동되지 않는 것을 확인하였다.

 Authentication authentication = authenticationManagerBuilder
 .getObject()
 .authenticate(authenticationToken);
 
SecurityContextHolder
        .getContext().setAuthentication(authentication);

로그인을 할때 Authentication을 생성하는 과정에서 문제가 발생되었기 때문에 세부 로직을 살펴봤다.

  • UserDetailsService를 상속받은 클래스를 먼저 확인 하였는데 별다른 점을 발견하지 못하였고 다른 부분을 확인하였다.

혹시 필터 부분에 문제가 있을 수도 있어

  • SecurityConfig에 핸들러와 엔트리 포인트가 잘 적용되었는지
  • JwtFilter 클래스에 문제가 있는지 확인

해보았지만 해당 부분에서도 문제점을 찾지 못하였고 미궁속으로 빠졌다 🤔


회원가입 API는 잘 작동되는 것을 확인하였기 때문에 역시 로그인 로직에서 문제가 발생된다는 걸 인지하고 있었고 로그인 로직과 관련된 코드와 클래스, 세부 항목들을 살펴보았고 점점 더 어려워졌다.

💡 그러면 대체 뭐가 문제였을까 ?

로그인 로직에서의 코드를 확인을 해보아도 문제점이 발견되지 않아 미궁속으로만 빠져가다가 아래 코드의 Entity를 확인해보게 되었다. 그리고 바로 문제점을 찾았다.

엔티티에서 문제가 있을거라 생각도 못했고 User클래스에 빨간줄이 그어져있었지만 서버가 정상적으로 돌아가 확인이 힘들었던 것 같다.

User.class

@Entity
@Getter
@Setter
@Builder
@Table(name = "user_table")
public class User extends BaseTimeEntity {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;

문제는 기본생성자와 전체 컬럼을 파라미터로 받는 생성자가 존재하지 않아서였다.

엔티티 내부에서 @AllArgsConstructor, @NoArgsConstructor 를 통해 생성자를 만들어주거나, 또는 코드를 통해 생성자를 만들어주지만 해당 엔티티에서는 생성자를 만들어주는 어노테이션이나 코드가 존재하지 않았다.

그럼에도 서버는 잘 돌아갔기 때문에 이 부분이 빠졌을거라곤 생각을 못했던 것 같다 😰


이렇듯 401과 403은 인증과 권한에 대한 부분이기 때문에 SecurityConfig와 같이 권한을 허용해주는 부분에서의 문제가 아니라면, 분명 단순한 실수인 문제가 많은 것 같다. 필자도 프로젝트를 개발할때 401을 봐왔었고 아주 단순한 코드 실수로 401 문제를 겪었었다.

개발을 하면서 항상 느끼는건 에러는 정말 단순한 실수나 문제로 코드가 제대로 돌아가지 않는 경우가 대부분인것 같다. 지인 또한 해당 코드를 보고 엔티티를 늘 그렇듯 생성하다가 어노테이션을 빼먹은 것 같다고 하였다. 😉

그럼 이번 포스팅은 여기서 끄읏-!

profile
내가 몰입하는 과정을 담은 곳

0개의 댓글