본문 바로가기

Spring Boot/유저 관리 기능

인가와 권한 관리 : 실제 인가 구현 사례

- 인가가 실제 애플리케이션에서 어떻게 구현되는가

- 게시물 관리, API 엔트포인트 접근 제어, 관리자/일반 사용자 구분과 같은 사례 학습

- 인가 정책이 어떻게 서비스 요구사항과 연결되는가

- 시나리오를 살펴보며 추상 개념을 실제 구현으로 연결하기

- 실무에서 자주 마주치는 권한 설계 고민을 이해하고 해결법 익히기


게시물 접근 권한 관리

게시판 시스템에서 게시물(Post)은 작성자와 독자가 있고, 각 행동(읽기, 수정, 삭제)에 따라 권한이 달라진다

 

1. 기본 정책 정의

행위(action) 권한 대상 설명
게시물 읽기(READ) 모든 로그인 사용자 공개 게시물을 누구나 조회 가능
게시물 작성(CREATE) 로그인 사용자 인증된 작성자만 작성 가능
게시물 수정(UPDATE) 작성자 본인 본인의 소유 글만 수정 가능
게시물 삭제(DELETE) 작성자 본인 / 관리자 작성자는 본인 글만, 관리자는 전체 삭제

 

 

2. 예제

커스텀 UserPrincipal.java

import com.b1uffer.sessiontest.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

public class UserPrincipal implements UserDetails {

    private final User user;

    public UserPrincipal(User user) {
        this.user = user;
    }
	
    // 대충 오버라이드한 로직들
    
    // isAdmin 추가
    public boolean isAdmin() {
        return user.getRoles().contains("ADMIN");
    }
}

 

 

PostService.java

import com.b1uffer.sessiontest.entity.Post;
import com.b1uffer.sessiontest.repository.PostRepository;
import com.b1uffer.sessiontest.security.UserPrincipal;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@NoArgsConstructor
public class PostService {
    private PostRepository postRepository;

    public Post create(Long id, Post post) {
        Post findPost = postRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("post not found"));
        post.setContent(post.getContent());
        postRepository.save(findPost);
        return findPost;
    }

    public void update(Post post, String newContent) {
        // 인증 로직
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();

        // 작성자 본인이거나 관리자일 때 업데이트 가능
        if(!post.getOwner().equals(principal.getUsername()) || !principal.isAdmin()) {
            log.info("게시글 수정 실패");
            throw new IllegalArgumentException("수정 권한이 없습니다.");
        }
        post.setContent(newContent);
        log.info("게시글 수정 완료");
    }

    public void delete(Post post) {
        // 인증 로직
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();

        // 작성자 본인이거나 관리자일 때 삭제 가능
        if(!post.getOwner().equals(principal.getUsername()) || !principal.isAdmin()) {
            log.info("게시글 삭제 실패");
            throw new IllegalArgumentException("삭제 권한이 없습니다.");
        }
        postRepository.delete(post);
        log.info("게시글 삭제 완료");
    }
}

 

*작성자만 수정 가능, 관리자는 예외적으로 모든 글 수정 가능 정책이 필터링 형태로 구현되어있음

 

 

3. 워크플로우 다이어그램

항상 인증을 하고 인가처리를 진행한다

 

* 게시물 권한은 작성자/관리자를 중심으로 설계하는 경우가 많다

* 서비스 확장시, 공유 게시물(팀 단위 수정)같은 새로운 요구사항이 추가될 수 있으므로 정책을 유연하게 설계하는 것이 중요함


API 엔드포인트 권한 관리

REST API를 제공하는 서비스에서는 엔드포인트마다 접근 권한이 다르다

 

1. 엔드포인트 정책 예

엔트포인트 권한 설명
GET /api/users/{id} 본인 또는 관리자 개인정보 보호
POST /api/users 관리자 신규 사용자 등록
GET /api/posts 로그인 사용자 글 목록 조회
DELETE /api/posts/{id} 작성자 또는 관리자 글 삭제

 

 

2. 예(API Layer)

UserController.java

import com.b1uffer.sessiontest.entity.User;
import com.b1uffer.sessiontest.security.UserPrincipal;
import com.b1uffer.sessiontest.service.UserService;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/users")
public class UserController {
    private UserService userService;

    @GetMapping("{username}")
    public User getUser(@PathVariable String username, @AuthenticationPrincipal UserPrincipal userPrincipal) {
        if(!userPrincipal.getUsername().equals(username) || !userPrincipal.isAdmin()) {
            throw new SecurityException("Invalid username or password");
        }
        User getUser = userService.get(username);
        return getUser;
    }

    /**
     * 관리자 전용 api
     */
    @PostMapping
    public User createUser(@RequestBody User user, @AuthenticationPrincipal UserPrincipal userPrincipal) {
        if(!userPrincipal.isAdmin()) {
            throw new SecurityException("관리자만 등록 가능합니다.");
        }
        User createUser = userService.create(user.getUsername(), user.getPassword());
        return createUser;
    }
}

 

* 엔드포인트 권한은 API 명세 단계에서 미리 정의하는게 좋다

* 문서화된 권한 정책은 개발자, QA, 보안 담당자 모두에게 유용하니까 문서화는 꼭 해야한다. 보기 편함


관리자와 일반 사용자 권한 구분

관리자(Admin)와 일반 사용자(User)를 구분하는 것은 모든 서비스에서 필요하다

 

1. 역할(Role) 기반 구분

역할 권한 범위
관리자(Admin) 시스템 설정, 모든 데이터 접근, 사용자 관리
일반 사용자(User) 본인 데이터 접근, 서비스 기본 기능 사용

 

 

2. 예제

public class User {
    private String id;
    private String role; // "ADMIN", "USER"
    
    public boolean isAdmin() {
    	return "ADMIN".equals(role);
    }
}

 

 

3. 워크플로우

User는 관리자 페이지로 접근할 수 없다

 

* 관리자 권한은 최소한의 사용자에게만 부여함

* 관리자 계정이 탈취되면 피해가 크므로 이중 인증(MFA) 같은 보안 강화 방법을 적용해야함

https://b1uffer.tistory.com/363


정리

구분 주요 내용 핵심
게시물 권한 관리 작성자, 관리자 중심의 권한 부여 소유자 정책, 관리자는 예외
API 권한 관리 엔드포인트별 접근 제어 명세 단계에서 문서화가 필요함
관리자 / 사용자 구분 역할 기반 접근 제어(ADMIN, USER) 최소 권한 부여, MFA 권장