개발/Spring Boot

Spring Boot 시리즈 23편 – 인증과 권한 전략 아키텍처: OAuth2, JWT, Role 기반 접근 제어까지

B컷개발자 2025. 5. 2. 09:43

Spring Boot에서 OAuth2, JWT 기반 인증 및 권한 관리를 안전하게 구현하는 전략을 소개합니다. 실무 중심의 Role 기반 접근 제어 설계와 구현 사례 포함.


Spring Boot 시리즈 23편 – 인증과 권한 전략 아키텍처: OAuth2, JWT, Role 기반 접근 제어까지

서비스가 성장하면 단순한 로그인 처리만으로는 부족합니다.
외부 연동(OAuth), 모바일/웹 통합 인증, 사용자 권한별 기능 제한 등
강력하면서도 유연한 인증·권한 아키텍처가 필요해집니다.

이번 글에서는 Spring Boot 기반으로

  • OAuth2 로그인
  • JWT 토큰 인증
  • Role/Permission 기반 인가 처리
    전반을 실전 아키텍처 수준으로 정리합니다.

📌 1. 인증 vs 권한 – 구분부터 명확히

개념 설명

인증(Authentication) "누구인지" 확인 – 로그인, 토큰
권한(Authorization) "무엇을 할 수 있는지" 확인 – 역할, 권한

✅ 2. 인증 흐름 설계 – OAuth2 + JWT 구조

[1] 사용자가 Google 등으로 로그인 요청
     ↓
[2] OAuth2 서버로 인증 요청 → 성공
     ↓
[3] 우리 서버에서 JWT 토큰 발급 (Access + Refresh)
     ↓
[4] 클라이언트는 AccessToken 포함하여 API 호출
     ↓
[5] 서버는 토큰 인증 후 사용자 정보 주입
  • OAuth2는 사용자 인증 대행
  • JWT는 우리 서비스의 인증 상태 유지

🛠️ 3. Spring Boot에서 OAuth2 로그인 설정

1️⃣ 의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

2️⃣ application.yml 설정 (Google 예시)

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: {클라이언트ID}
            client-secret: {클라이언트비밀}
            redirect-uri: "{baseUrl}/login/oauth2/code/google"
            scope: profile, email

3️⃣ OAuth2 로그인 성공 시 JWT 발급 로직

@Service
public class OAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest request) {
        OAuth2User oauth2User = super.loadUser(request);
        String email = oauth2User.getAttribute("email");

        // 사용자 정보 확인/저장
        User user = userRepository.findOrCreate(email);

        // JWT 토큰 생성 후 리다이렉트 or API 응답
        String token = jwtProvider.createToken(user.getId(), user.getRole());
        return new CustomOAuth2User(token, user);
    }
}

🔐 4. JWT 인증 구조 구현

1️⃣ JwtProvider 클래스

public class JwtProvider {

    private final String secret = "secret-key";

    public String createToken(String userId, String role) {
        return Jwts.builder()
            .setSubject(userId)
            .claim("role", role)
            .setExpiration(new Date(System.currentTimeMillis() + 3600_000))
            .signWith(SignatureAlgorithm.HS512, secret.getBytes())
            .compact();
    }

    public boolean validate(String token) {
        try {
            Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }

    public String getUserId(String token) {
        return Jwts.parser()
            .setSigningKey(secret.getBytes())
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
    }
}

2️⃣ JWT 필터 구현

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtProvider jwtProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
        String token = extractToken(request);

        if (token != null && jwtProvider.validate(token)) {
            String userId = jwtProvider.getUserId(token);
            Authentication auth = new UsernamePasswordAuthenticationToken(userId, null, List.of());
            SecurityContextHolder.getContext().setAuthentication(auth);
        }

        chain.doFilter(request, response);
    }
}

✅ 5. 권한 관리 – Role 기반 인가 처리

1️⃣ Security 설정

http
    .authorizeHttpRequests()
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
    .anyRequest().authenticated()

2️⃣ 메서드 수준 제어 (@PreAuthorize)

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long userId) {
    // 관리자만 가능
}
  • Spring Expression Language(SpEL)를 활용한 세밀한 제어 가능

🧠 실무 적용 전략 요약

항목 전략

인증 방식 OAuth2 → JWT로 변환하여 자체 인증 토큰 유지
토큰 저장 Access + Refresh Token 분리 관리
역할 설계 USER, ADMIN 등 enum 기반 권한 체계 확립
보안 처리 Token 만료 시간, 서명 검증, Redis 블랙리스트 가능
클라이언트 대응 로그인 성공 → JWT 저장 (쿠키 or LocalStorage)

✅ 마무리 요약

항목 요약

인증 OAuth2 기반 외부 로그인, JWT로 자체 인증 유지
인가 Role/Permission 기반 API 보호
필터 구성 JWT 필터를 Security 체인에 삽입
실무 포인트 Refresh 토큰 재발급, 토큰 만료 관리, 권한 계층 분리 필요
확장 방향 API Gateway 인증 연계, OpenID Connect 확장

📌 다음 편 예고

Spring Boot 시리즈 24편: Swagger와 SpringDoc을 활용한 API 문서 자동화 전략