1. build.gradle에 추가
// 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
//전체 코드
2. interceptor 상속받기
public class aminCheckInterceptor implements HandlerInterceptor {
HandlerInterceptor를 지켜야 한다.implements이다.3. 로그인한 사용자(인증)확인하기 위해 이름(이메일, 고유 식별자) 꺼내기
// 1. 현재 로그인한 사용자 이름 꺼내기
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String currentusername = auth.getName();
getName이 SecurityContextHolder메서드에 들어가면 있음 getName은 이메일 또는 고유의 식별자임4. 요청 URI에서 username 추출
// 2. [요청 URI 추출] 사용자가 요청한 전체 주소 경로를 가져옵니다. (예: /api/user/1L/email)
String path = request.getRequestURI();
// 3. https://ko.wikipedia.org/wiki/%EB%94%94%EC%BD%94%EB%94%A9 한글 이름이나 특수문자가 주소창에 포함되어 깨지는 현상을 방지하기 위해 UTF-8 문자셋으로 디코딩합니다.
String decodedPath = URLDecoder.decode(path, StandardCharsets.UTF_8);
// 4. [경로 분석] 디코딩된 주소를 '/' 문자를 기준으로 조각냅니다.
// 예: "/api/user/1L/email" -> ["", "api", "user", "1L", "email"]
String[] parts = decodedPath.split("/");
// 5. [대상 유저 추출] 끝에서 두 번째 조각을 가져옵니다. (위 예시의 경우 뒤에서 두 번째인 "1L"이 추출됨)
String username = parts[parts.length - 2];
5. 관리자 권한 비교
자바에서 추상화(Abstraction)는 크게 두 가지 방식으로 구현됩니다.
내가 만드는 클래스의 목적에 따라 extends와 implements를 적절하게 선택해야 합니다.
[ 추상화 (Abstraction) ]
│
┌────────────────┴────────────────┐
▼ ▼
추상 클래스 (Abstract Class) 인터페이스 (Interface)
- 상속하여 확장할 때 사용 (is-a) - 공통 규격/계약을 강제할 때 (has-a)
- 키워드: extends - 키워드: implements
인터페이스(Interface)는 100% 추상화 상태입니다.
implements (구현):
HandlerInterceptor (인터페이스/추상화):
(구현 클래스):
삼성이나 애플이 실제로 공장에서 만들어낸 "진짜 C타입 충전기 제품"입니다.
implements:
그래야 스마트폰(스프링)에 꽂았을 때 에러 없이 충전이 되겠죠?
왜 implements 하나요? * 스프링이 제공하는 인터셉터 규격(HandlerInterceptor)에 내 클래스를 맞춰서 스프링이 인식할 수 있게 만들기 위함입니다.
implements가 추상화인가요? * 맞습니다! 알맹이가 없는 추상적인 규격(인터페이스)을 가져와서, 실제 동작하는 코드로 완성(구현)하는 도구가 바로 implements입니다.
스프링 시큐리티가 상속받는 자바의 표준 보안 규격(java.security.Principal) 인터페이스를 보면, 사용자의 고유한 식별 정보를 가져오는 메서드가 하필 getName()으로 정의되어 있습니다.
public interface Principal {
public boolean equals(Object another);
/**
* Returns a string representation of this principal.
*
* @return a string representation of this principal.
*/
public String toString();
/**
* Returns a hashcode for this principal.
*
* @return a hashcode for this principal.
*/
public int hashCode();
/**
* Returns the name of this principal.
*
* @return the name of this principal.
*/
// 자바 표준 규격: 사용자를 유일하게 식별할 수 있는 이름을 반환해라!
public String getName();
자바 설계자가 "사용자를 식별하는 유일한 값"의 이름을 getId()가 아니라 getName()으로 지어둔 것이다.
여기서 말하는 Name은 우리가 흔히 쓰는 본명(예: "홍길동")이 아니라, 시스템이 나를 중복 없이 유일하게 알아볼 수 있는 "식별자(Username / Email / Login ID)"를 의미한다.
실제 스프링 시큐리티에서의 흐름
로그인 시도: 지현님이 email과 password로 로그인을 합니다.
UserDetails 생성: 스프링 시큐리티는 인증된 사용자 정보를 UserDetails 객체에 담습니다. 이때 홍길동의 이메일(또는 로그인 ID)이 username 자리에 저장된다.
SecurityContextHolder에 보관: 이 인증 정보가 Authentication이라는 객체로 감싸져서 컨텍스트에 저장된다.
auth.getName() 호출 시:
스프링 시큐리티는 Authentication 내부의 Principal 객체(실제로는 우리가 커스텀한 UserDetails 구현체)를 들여다 본다.
그리고 그 안에 들어있는 username(즉, 로그인 ID나 이메일)을 꺼내서 반환해 준다.
💡 한 줄 요약
실제 데이터: "test@email.com" 또는 "1L" (중복 없는 고유 ID)
꺼내는 메서드: auth.getName() (규격상 이름이 getName일 뿐!)
만약 로그인 ID가 아니라 데이터베이스에 저장된 진짜 사용자 이름(예: "홍길동")을 꺼내고 싶다면, auth.getName()을 쓰면 안 되고 아래처럼 Principal을 실제 객체로 다운캐스팅(형변환)해서 가져와야한다.
// 로그인할 때 커스텀한 UserDetailsImpl을 썼다고 가정했을 때
UserDetailsImpl userDetails = (UserDetailsImpl) auth.getPrincipal();
String loginId = userDetails.getUsername(); // 고유 ID (auth.getName()과 동일)
String realName = userDetails.getNickname(); // 데이터베이스에 저장된 진짜 이름/닉네임