GitHub - GDG-on-Campus-SKHU/24-25-Server-Assignment-05
Contribute to GDG-on-Campus-SKHU/24-25-Server-Assignment-05 development by creating an account on GitHub.
github.com
🎈로그인이란?
로그인은 중요한 데이터와 리소스를 보호하기 위해 컴퓨터 시스템과 애플리케이션에서 사용되는 일반적인 보안 수단입니다.
로그인은 사용자가 특정 시스템(애플리케이션, 웹사이트 등)에 접근할 수 있도록 인증하는 절차라고 할 수 있습니다. 이 과정에서 사용자는 일반적으로 사용자 이름(또는 이메일 주소)과 비밀번호 같은 자격 증명(credentials)을 입력합니다. 시스템은 이 정보를 기반으로 사용자가 등록된 사용자임을 확인하고, 적절한 접근 권한을 부여합니다.
🎈로그인이 왜 필요한데?
막연하게 생각하면 회원가입/로그인을 그냥
회원가입시 DB에 정보를 저장. -> 로그인시 받은 데이터와 DB 정보를 비교해서 일치하면 로그인 성공.
이라고 생각할 수 있는데요.
하지만 실제로 로그인은 단순한 "비교" 이상의 의미를 갖고 있습니다.
✔️인증(Authentication)과 인가(Authorization)
위에서 언급한 내용은 인증(Authentication)에 관한 내용입니다.
쉽게 설명하자면 로그인 과정에서 사용자가 입력한 아이디와 비밀번호를 데이터베이스의 정보와 비교하여 로그인 성공 여부를 확인하는 로직으로, "이 사용자가 누구인지"를 확인하는 과정입니다.
즉 로그인한 사용자가 실제로 등록된 사용자인지를 검증하는 단계죠.
하지만 로그인은 필연적으로 인가(Authorization)도 수행해야 하는데요.
인가는 인증과는 다른 단계로, 사용자가 무엇을 할 수 있는지 결정하는 권한 부여 과정입니다.
로그인한 사용자가 시스템에 인증되었다고 해서 모든 리소스에 접근할 수 있는 것은 아닙니다.
예를 들어, 일반 사용자에게는 특정 게시물을 "조회"할 권한만 주지만, 관리자에게는 "수정"과 "삭제" 권한도 부여할 수 있습니다.
이처럼 인증이 완료된 사용자가 시스템 내에서 어떤 작업을 수행할 수 있는지, 어떤 데이터에 접근할 수 있는지를 관리하는 것이 인가입니다.
🔐Spring Security
인증(Authentication)과인가(Authorization)를 Spring Security를 이용하면 매우 쉽게 구현할 수 있습니다.
1️⃣ 인증(Authentication)차원에서 Spring Security의 역할
Spring Security는 사용자의 로그인 자격 증명(예: 아이디와 비밀번호)을 안전하게 검증하고, 자격 증명이 성공할 때만 시스템에 접근할 수 있도록 합니다.
2️⃣ 인가(Authorization)차원에서 Spring Security의 역할
Spring Security는 특정 URL이나 메서드에 접근할 수 있는 권한을 정의하여, 인증된 사용자만 적절한 리소스에 접근하도록 제어할 수 있습니다.
3️⃣ Spring Security의 보안 기능
1. 비밀번호 암호화: BCryptPasswordEncoder와 같은 암호화 알고리즘을 사용해 비밀번호를 안전하게 암호화합니다. 이렇게 하면 데이터베이스가 해킹되더라도 원본 비밀번호가 노출되지 않습니다.
2. 세션 관리: Spring Security는 세션 고정 보호(Session Fixation Protection)와 같은 기능을 통해 사용자가 로그인한 이후에도 동일한 세션 ID를 유지함으로써, 세션 탈취와 같은 공격을 방어합니다. 또한, 세션 만료 정책을 설정해 세션이 일정 시간 후에 자동으로 종료되도록 할 수 있습니다.
3. CSRF 보호: Spring Security는 CSRF(Cross-Site Request Forgery) 공격으로부터 애플리케이션을 보호합니다. CSRF 공격은 사용자가 의도하지 않은 요청을 서버에 보내도록 유도하는 공격으로, Spring Security는 기본적으로 CSRF 토큰을 사용해 이를 방지합니다.
4. CORS 설정: CORS(Cross-Origin Resource Sharing) 설정을 통해 특정 도메인에서만 서버 리소스에 접근하도록 제한할 수 있습니다. 이를 통해 악의적인 사이트에서의 요청을 차단할 수 있습니다.
5. HTTP 응답 헤더 보안: Spring Security는 X-Content-Type-Options, X-Frame-Options와 같은 보안 헤더를 설정하여, 클릭재킹(Clickjacking), MIME 스니핑(MIME Sniffing)과 같은 공격으로부터 보호할 수 있습니다.
4️⃣ Spring Security를 사용한 전체적인 보안 흐름
1. Spring Security가 사용자 자격 증명(예: 아이디와 비밀번호)을 받아서 검증합니다. 비밀번호는 PasswordEncoder로 암호화되어 비교됩니다.
2. 사용자가 인증되면 세션이 생성되고, 이후 요청에서 이 세션을 통해 인증된 사용자임을 식별합니다.
3. 사용자가 특정 URL이나 리소스에 접근할 때, Spring Security는 해당 사용자가 로그인에 성공했는지 확인하여, 인증된 사용자에게만 접근을 허용합니다. 또한, 필요할 경우 사용자의 역할(예:ROLE_USER,ROLE_ADMIN)을 확인하여 해당 리소스에 접근할 권한이 있는지 결정합니다. 적절한 역할이 부여되지 않은 경우, 접근이 제한됩니다.(인가)
4. CSRF 보호, CORS 설정, 보안 헤더 설정 등 추가적인 보안 정책이 적용됩니다.
5. 사용자가 로그인한 이후부터는 클라이언트가 세션 ID를 포함한 쿠키를 서버에 전송하여 사용자가 이미 인증된 상태임을 서버에 알립니다.
❓그래서 세션(Session)을 이용한다는 게 뭐죠?
클라이언트와 서버는 반드시! 추정 불가능한 임의의 식별자 값으로 연결해야 합니다. 세션이 이런 역할을 수행하기에 적합한데요.
세션(Session) 기반 인증은 사용자 로그인 시 서버에서 세션을 생성하고, 클라이언트에게는 세션 ID를 쿠키에 담아 전송하는 방식입니다. 이후 클라이언트의 모든 요청에는 세션 ID가 포함되어 전송되며, 서버는 이 세션 ID를 바탕으로 사용자의 인증 상태와 관련된 데이터를 관리하게 됩니다.
이렇게 서버에 중요한 정보를 보관하고 연결을 유지하는 방법을 세션(Session)이라 합니다.
+ 세션이라는 것이 뭔가 특별한 것이 아니라 단지 쿠키를 사용하는데, 서버에서 데이터를 유지하는 방법일 뿐입니다.
❓쿠키(Cookie) : 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 파일으로, 쿠키는 사용자가 따로 요청하지 않아도 브라우저가 Request 시에 Request Header를 넣어서 자동으로 서버에 전송하게 됩니다.
🎵SpringBoot로 세션(Session)을 생성해보자
@PostMapping("/login")
public ResponseEntity<MemberLoginResDto> login(@RequestBody LoginReqDto loginReqDto, HttpServletRequest request) {
MemberLoginResDto response = memberService.login(loginReqDto);
// 세션을 가져오거나 생성합니다.
HttpSession session = request.getSession(true);
// Authentication 객체 생성
Authentication authentication = new UsernamePasswordAuthenticationToken(
loginReqDto.loginId(), null, null);
// SecurityContext에 인증 정보를 설정
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
// 세션에 SecurityContext 저장
SecurityContextHolder.setContext(context);
session.setAttribute("SPRING_SECURITY_CONTEXT", context);
return ResponseEntity.ok(response);
}
저희 모두 기본적으로 CRUD를 통해서 데이터를 저장하는 로직은 구현할 수 있죠?
위 코드는 그냥 간단한 CRUD 코드에 세션 생성 코드만 약간 첨가한 거라고 생각하시면 됩니다. 어렵게 생각하지 않아도 돼요.
학생증을 예시로 위의 로직을 설명해 볼게요.
MemberLoginResDto response = memberService.login(loginReqDto);
마치 학생증을 만들기 위해 내가 진짜 이 학교에 입학했는지 확인하는 단계입니다. 이 단계에서 에러가 발생하지 않으면 본인 인증은 성공한 거!
이 코드는 MemberLoginResDto를 반환합니다. 이 DTO 안에는 맴버를 반환하고 있구요.
memberService의 login 메서드는 단순히 DB에서 아이디와 비밀번호가 잘 저장되어 있는지 확인하는 로직입니다.
HttpSession session = request.getSession(true);
학생이 학교에서 매번 본인 확인을 받지 않고 쉽게 다닐 수 있도록 학생증 보관함(세션)을 준비해 줍니다. 뭘 할 때마다 학생증 보여주라고 하면 귀찮잖아요..
이 보관함에 학생이 누구인지 정보를 넣어두면, 이후 학교에서 쉽게 신분 확인을 할 수 있어요. (아직 보관함 안에 무엇을 넣진 않았어요!)
request.getSession(true)는 세션을 가져오거나, 세션이 없다면 새로 생성하는 로직이에요.
Authentication authentication = new UsernamePasswordAuthenticationToken(
loginReqDto.loginId(), null, null);
이 부분은 학생증에 들어갈 학생 신분 정보(이름이나 학번)를 만드는 과정입니다. 예를 들어, 이 학생이 누구인지 표시하는 "학번" 같은 것을 정리해서 준비하는거죠. 앞에서 내 정보에 대한 인증이 완료되었으니, 그 정보를 입력하면 됩니다.
UsernamePasswordAuthenticationToken은 Spring Security에서 사용자를 인증하기 위한 토큰 객체입니다. 여기서는 loginId(사용자 ID)만 설정하고 권한 정보는 비워둔 상태(null)이고, 이 Authentication 객체는 SecurityContext에서 사용자의 인증 상태를 나타내는 객체로 사용됩니다.
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
이제 학생증에 본인의 이름과 정보를 써서, 나중에 이걸 꺼내 보여주면 이 학생이 누구인지 쉽게 알 수 있도록 준비합니다.
SecurityContextHolder는 Spring Security가 인증 상태를 관리하는 핵심 클래스입니다.
먼저 빈 SecurityContext를 생성하고, 그 안에 앞서 생성한 Authentication 객체를 설정하여 현재 사용자가 인증된 상태임을 나타냅니다.
SecurityContextHolder.setContext(context);
session.setAttribute("SPRING_SECURITY_CONTEXT", context);
마지막으로, 우리가 방금 만든 학생증(SecurityContext)을 학교 중앙 시스템(SecurityContextHolder)에 등록하는 역할을 합니다. 그 후 이제 준비된 학생증을 보관함에 넣어둡니다.
그러면 이제는 학교의 다른 시설에 가더라도 학생증 보관함에서 꺼내어 신분을 증명할 수 있어요.
SecurityContextHolder.setContext(context);를 호출하여 해당 SecurityContext를 설정하고, 그 후 session.setAttribute("SPRING_SECURITY_CONTEXT", context);를 통해 이 인증 정보를 세션에 저장하는 로직입니다.
☑️그럼 세션(Session)은 만능일까?
세션 방식이 안정적이지만, 모든 상황에 완벽한 해결책은 아니에요. 만약 세션이 정말 만능이었다면, 다양한 로그인 방식이 생겨나지 않았겠죠?
세션 기반 인증의 장점
- 안전한 전송
- 세션 기반 인증은 클라이언트와 서버 간에 오가는 데이터가 세션 ID뿐이라는 특징이 있어요. 사용자 정보나 민감한 데이터는 서버 측에서만 관리하고, 클라이언트에서는 세션 ID만 전송하므로 클라이언트에서의 데이터 유출 위험이 줄어듭니다.
- 세션 ID는 사용자 정보와는 별도로 고유 식별자로 사용되기 때문에 비교적 안전하게 인증을 유지할 수 있어요.
- 인증 상태 관리 용이
- 세션 기반 인증은 사용자가 로그아웃할 때나 세션이 만료될 때 서버에서 인증 정보를 바로 무효화할 수 있습니다.
- 이를 통해, 계정당 하나의 기기에서만 로그인 허용하거나, 탈취된 세션 ID를 즉시 차단하는 등 서버에서 인증 상태를 능동적으로 관리할 수 있죠.
세션 기반 인증의 단점
- 서버의 메모리 부담
- 서버는 모든 사용자 세션을 메모리에 저장하기 때문에, 사용자가 늘어날수록 서버의 메모리 사용량도 증가합니다. 따라서 사용자 수가 급격히 증가할 때는 서버에 상당한 부담이 될 수 있어요.
- 확장성의 한계
- 세션 정보를 서버에 저장하기 때문에 여러 대의 서버를 구축하는 확장성 측면에서 어려움이 있을 수 있습니다. 예를 들어 로드 밸런서를 통해 여러 서버를 사용하는 경우, 각 서버가 같은 세션 정보를 공유할 수 있도록 추가적인 관리 방식이 필요합니다.
🔚결론
로그인 방식은 서비스의 특성, 보안 수준, 확장성을 고려하여 신중하게 선택해야 해요. 각 방법마다 장단점이 분명히 존재하기 때문에, 세션 기반 인증이 필요한 서비스도 있고, JWT나 OAuth가 적합한 경우도 있습니다.
간단한 웹사이트나 내부 애플리케이션에서는 세션 기반 인증이 충분히 강력한 선택이 될 수 있지만, 확장성과 API 호환성이 중요한 대규모 서비스라면 JWT 같은 토큰 기반 인증이 더 적합할 수 있습니다. 소셜 로그인을 통해 사용자 경험을 높이고 싶다면 OAuth를 도입하는 것도 좋은 선택이겠죠.
결국 중요한 것은 서비스의 요구사항에 맞는 인증 방식을 선택하는 것입니다. 여러 로그인 방식의 특징을 잘 이해하고, 서비스 상황에 맞게 적절한 방법을 채택하여 안정적이면서도 사용자 친화적인 인증 환경을 구축해 보세요.
'springboot' 카테고리의 다른 글
[SpringBoot] QueryDSL 그게 뭔데 다들 쓰는거지? (0) | 2024.12.28 |
---|---|
[SpringBoot] 웹소켓으로 채팅하고 채팅방 관리하기 (0) | 2024.11.23 |
[SpringBoot] Spring Boot로 REST API 만들기 (0) | 2024.09.28 |
[SpringBoot] IoC/DI 가 뭘까? (0) | 2024.08.28 |
[SpringBoot] orElse와 orElseGet 차이점 (0) | 2024.08.10 |