인가
인가(Authorization)란 인증된 사용자가 특정 리소스나 기능에 접근할 수 있는 권한이 있는지 확인하는 과정입니다.
인증 vs 인가
[인증 Authentication — "너 누구야?"]
사용자: "저 홍길동입니다" (아이디/비밀번호, 토큰 등)
서버: "확인됐습니다. 홍길동 맞네요" O
[인가 Authorization — "너 이거 해도 돼?"]
홍길동: "관리자 페이지 보여주세요"
서버: "홍길동은 일반 회원이라 안 됩니다" X 403
인증이 먼저 이루어지고, 그 다음에 인가가 수행됩니다.
요청 → 인증 (누구인가?) → 인가 (권한이 있는가?) → 리소스 접근
실패 시 401 실패 시 403
인가의 대표적 방식
1. 역할 기반 접근 제어 (RBAC)
사용자에게 역할(Role) 을 부여하고, 역할에 따라 접근을 제어하는 가장 보편적인 방식입니다.
[역할 구조]
ADMIN → 모든 기능 접근 가능
MANAGER → 게시글 관리 가능
USER → 본인 게시글만 조회/수정 가능
GUEST → 공개 게시글 조회만 가능
// Spring Security RBAC 설정
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/manage/**").hasAnyRole("ADMIN", "MANAGER")
.requestMatchers("/mypage/**").hasRole("USER")
.anyRequest().permitAll()
);
return http.build();
}
}
// 메서드 레벨 인가
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/members/{id}")
public ResponseEntity<Void> deleteMember(@PathVariable Long id) {
memberService.delete(id);
return ResponseEntity.noContent().build();
}
2. 리소스 기반 접근 제어
역할이 아니라 해당 리소스의 소유자인지 확인하는 방식입니다.
// "본인의 주문만 조회 가능"
@GetMapping("/orders/{orderId}")
public ResponseEntity<Order> getOrder(@PathVariable Long orderId,
@AuthenticationPrincipal UserDetails user) {
Order order = orderService.findById(orderId);
// 역할과 무관하게, 본인 주문인지 확인
if (!order.getMemberId().equals(user.getId())) {
throw new AccessDeniedException("본인의 주문만 조회 가능합니다");
}
return ResponseEntity.ok(order);
}
3. 권한 기반 접근 제어
역할보다 세분화된 개별 권한(Permission) 을 부여하는 방식입니다.
[RBAC — 역할 단위]
ADMIN 역할 → 모든 권한
[권한 기반 — 기능 단위]
사용자 A → POST_READ, POST_WRITE
사용자 B → POST_READ, POST_WRITE, POST_DELETE
사용자 C → POST_READ, USER_MANAGE
// 권한 기반 인가
@PreAuthorize("hasAuthority('POST_DELETE')")
@DeleteMapping("/posts/{id}")
public ResponseEntity<Void> deletePost(@PathVariable Long id) {
postService.delete(id);
return ResponseEntity.noContent().build();
}
RBAC vs 권한 기반 비교
| 항목 | RBAC (역할 기반) | 권한 기반 |
|---|---|---|
| 단위 | 역할 (ADMIN, USER 등) | 개별 권한 (POST_READ 등) |
| 유연성 | 역할 추가/변경 시 영향 큼 | 권한을 세밀하게 조합 가능 |
| 복잡도 | 단순 | 권한 수가 많아지면 관리 복잡 |
| 적합 규모 | 소~중규모 | 대규모, 복잡한 권한 체계 |
JWT에서의 인가
// JWT Payload에 역할/권한 정보 포함
{
"sub": "1",
"role": "ADMIN",
"permissions": ["POST_READ", "POST_WRITE", "USER_MANAGE"],
"exp": 1700000000
}
// JWT 기반 인가 필터
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws Exception {
String token = extractToken(request);
Claims claims = jwtProvider.parse(token);
String role = claims.get("role", String.class);
// 인증 + 권한 정보를 SecurityContext에 설정
List<GrantedAuthority> authorities =
List.of(new SimpleGrantedAuthority("ROLE_" + role));
Authentication auth = new UsernamePasswordAuthenticationToken(
claims.getSubject(), null, authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
chain.doFilter(request, response);
}
}
인가 처리 흐름 요약
1. 클라이언트 → 서버: 요청 + 인증 정보 (토큰/세션)
2. 서버: 인증 확인 → "홍길동(USER)" 확인
3. 서버: 인가 확인 → "이 API는 ADMIN만 접근 가능"
4. 서버: 홍길동은 USER → 권한 없음 → 403 Forbidden
면접 포인트
- 인증과 인가는 항상 인증 → 인가 순서로 수행되며, 인증 없이 인가는 불가능합니다.
- 실무에서는 RBAC가 가장 보편적이지만, 서비스가 복잡해지면 역할에 권한을 매핑하는 RBAC + 권한 기반의 혼합 구조를 사용합니다.
- Spring Security에서는
hasRole()은 역할 기반,hasAuthority()는 권한 기반 인가이며, 내부적으로hasRole("ADMIN")은hasAuthority("ROLE_ADMIN")과 동일하게 동작합니다.