Spring Security를 사용하여 요청 권한 설정하기

2025. 4. 18. 20:44·Backend

개요


이전에 유레카 미니프로젝트에서 진행한 Spring Boot 프로젝트를 배포하였는데 설계 미스로 인한 보안 문제가 발생했다.

미니프로젝트의 주제는 유레카 부트캠프 사람들간의 경매 서비스이며, 서버에서는 회원가입&로그인, 입찰 등록&조회, 판매글 등록&조회&수정&삭제, 회원 정보 수정, 공지사항 등록&삭제 기능을 한다.

 

보안이 우려되는 부분은 다음과 같다.

  • 사용자가 특정 권한을 가지고 있지 않아도 endpoint와 요청 데이터만 일치한다면 원하는 결과를 얻을 수 있는 문제.

이는 결국 로그인을 하지 않아도 로그인 한 사용자만 이용할 수 있는 서비스에 요청을 할 수 있다는 것을 의미하였고, 더 나아가 관리자 권한이 필요한 요청이라도 요청 데이터만 일치하다면 원하는 결과를 얻을 수 있다는 것을 의미한다.

  • 서버에서 이미지 업로드 기능을 수행하는데 사용자가 올린 파일의 확장자 검사를 하지 않는다.

이는 사용자가 악성 코드를 악의적으로 업로드할 가능성이 있다.

 

이번에는 위 두가지 보안 문제 중 첫번째인 권한 문제를 Spring Security의 세션 로그인을 도입하여 해결하였다.


Spring Security로 요청별 권한 설정하기


Spring Security에서는 SecurityConfig에 다음 메소드를 통하여 엔드포인트마다 권한을 설정할 수 있다.

.permitAll() 로그인을 하지 않아도 요청 허용
.hasRole() 로그인 한 뒤에 특정한 role이 있어야만 접근 가능
.hasAnyRole() 복수의 role 허용
.authenticated()  로그인이 되어 있다면 모두 접근 가능
.denyAll() 로그인이 되어 있어도 모두 접근 불가

 

인가 동작 순서는 위에서부터 아래로 수행된다.

 

예를 들어 위에서 .anyRequest().permitAll()을 수행한다면 이미 맨 처음에 모든 요청이 허용되었기 때문에 다른 요청은 인가를 적용할 수 없다.

 

때문에 우선순위를 잘 설계하여야 한다.


Rest api 환경에서 Spring Security 적용하기


현재 진행중인 프로젝트는 프론트엔드에서 Next.js를, 백엔드에서 Spring을 사용중이었으므로 Rest api로 동작하여야 했다.

 

때문에 SecurityConfig에 다음 설정을 넣어서 인증되지 않은 유저의 요청을 막았다.

http
                .exceptionHandling(exceptionHandleConfig -> exceptionHandleConfig.authenticationEntryPoint(
                new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)));

해당 설정을 통하여 Spring Security의 로그인 페이지 생성을 막았다.

 

보통 유저가 로그인 페이지를 마주하는 일은 해당 유저가 권한이 없음에도 로그인 권한이 필요한 기능에 접근하였을 때이다.

때문에 이러한 경우에 로그인 페이지가 아닌 401 에러를 반환하는 원리이다.


로그인 기능의 경우 기본적으로 formLogin 방식으로 작동하며 이 경우 응답으로 html을 보내주는 문제가 발생하였다.

 

일반적인 rest api 응답에 html을 응답하는 것은 적합하지 않다.

 

때문에 다음 코드를 통해 로그인 성공 시 { "message" : "로그인 성공" } 을 반환하도록 지정하였다.

        http
                .formLogin((auth) -> auth.usernameParameter("email").passwordParameter("password")
                        .loginProcessingUrl("/user/login")
                        .successHandler((request, response, authentication) -> {
                            response.setStatus(HttpServletResponse.SC_OK);
                            response.setContentType("application/json;charset=UTF-8");
                            response.getWriter().write("{\"message\": \"로그인 성공\"}");
                        })
                        .permitAll());

 

위 코드는 Spring Security의 일반적인 설정과 많이 다르므로 필요한 부분만 참고하길 권장한다.


세션 확인하기


사용자가 로그인이 필요한 작업을 수행할 경우 사용자가 현재 로그인되어 있는지 확인하여야 한다.

 

아래와 같이 사용자의 로그인 여부를 확인한다.

    public Boolean RegistBid(@RequestBody Bid bid, @AuthenticationPrincipal CustomUserDetails userDetails){
        log.debug("BidInfo : {}", bid);
        bid.setUser_id(userDetails.getUser().getUser_id());
        bidService.createBid(bid);
        return true;
    }

CustomUserDetails는 현재 로그인한 유저의 세션 정보가 있다.

 

요청으로 들어온 객체의 user_id를 로그인한 유저의 세션 정보로 바꾼다.

 

세션에 정보가 없을 경우 위에서 설정한 401 에러를 반환한다.

 

무작위로 user_id를 설정하여 요청을 보낸다 해도 자동으로 로그인한 유저의 user_id로 바꾼다.


마무리


배포에 대한 배경 지식이 많이 없었어서 요청을 권한별로 정리하는 기본적인 설계를 못한 것 같다.

 

JWT는 단일 서버이고 규모가 작은 프로젝트였기에 도입하지 않았다.

 

설계의 중요성에 대해 다시한번 알 수 있었고, Spring Security를 단편적으로나마 공부하게 되는 계기가 되었다.

 

다음엔 호스팅 서버에서 배포할 시 유의해야 할 사항에 대해 정리해보려고 한다.

'Backend' 카테고리의 다른 글

[NP]TCP/IP(Transmission Control Protocol/Internet Protocol) 소켓 통신  (0) 2023.07.13
'Backend' 카테고리의 다른 글
  • [NP]TCP/IP(Transmission Control Protocol/Internet Protocol) 소켓 통신
MaKa_
MaKa_
이게 왜안될까요
  • MaKa_
    벌레 잡는 사람
    MaKa_
  • 전체
    오늘
    어제
    • 공부 (45)
      • CS (18)
        • 자료구조(data structure) (7)
        • 알고리즘(Algorithm) (6)
      • React (6)
      • React Native (2)
      • Next.js (5)
      • Backend (2)
      • 이야기 (3)
        • 회고 (2)
        • 일상 (1)
      • 기타 (7)
      • 알고리즘 문제 (2)
        • 백준 (0)
        • 프로그래머스 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
MaKa_
Spring Security를 사용하여 요청 권한 설정하기
상단으로

티스토리툴바