웹 개발에서 사용하는 주요 인증 방식으로는 토큰, 쿠키, 세션 등이 있습니다. 각각의 방식의 장단점은 다음과 같다.
토큰은 클라이언트가 로그인을 하면 서버에서 발급하며, 이후 클라이언트는 요청 헤더에 토큰을 포함하여 전송한다.
서버는 토큰을 검증하여 해당 클라이언트가 인증된 사용자인지 확인한다.
토큰은 JWT(JSON Web Token) 형식으로 만들어질 수 있으며, payload에 인증 정보를 포함시킬 수 있다.
토큰은 일반적으로 유효기간을 설정하여 만료되면 재발급해야 한다.
쿠키는 클라이언트에서 저장하며, 서버에서 Set-Cookie 헤더를 이용하여 클라이언트에 전송한다.
클라이언트는 이후 요청 헤더에 쿠키를 포함하여 전송한다.
쿠키는 브라우저에서 관리되므로 보안성이 높습니다. 하지만 쿠키도 탈취될 수 있으므로, HTTPS 프로토콜을 사용하여 통신하는 것이 좋다.
쿠키는 도메인, 경로, 만료 시간 등의 제한을 설정할 수 있다.
세션은 클라이언트가 로그인하면 서버에서 생성되며, 클라이언트는 세션 ID를 가지고 있습니다.
서버에서는 세션 ID를 이용하여 클라이언트를 식별하고, 인증 정보를 유지한다.
세션은 서버에서 유지되므로 보안성이 높습니다. 하지만 세션을 유지하기 위해 서버에서 데이터를 저장하고 관리해야 하므로, 서버 부하가 증가할 수 있다.
세션은 기본적으로 메모리에 저장되며, 분산 환경에서는 세션을 공유하기 위해 추가적인 처리가 필요한다.
인증 방식 선택은 상황에 따라 다르며, 보안성과 확장성, 사용자 경험 등을 고려하여 결정해야 한다. 예를 들어, SPA(Single Page Application)의 경우 토큰 기반 인증이 적합하며, 서버 사이드 렌더링을 사용하는 경우 쿠키나 세션이 적합할 수 있다.
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// 인증 처리
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
);
// 토큰 생성
String token = tokenProvider.createToken(authentication);
// 토큰을 Response Header에 추가
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + token);
// Response 반환
return ResponseEntity.ok().headers(headers).build();
}
@GetMapping("/api/data")
public ResponseEntity<?> getData(@RequestHeader("Authorization") String token) {
// 토큰 파싱
String authToken = token.substring(7);
// 토큰 검증
if (!tokenProvider.validateToken(authToken)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
// 데이터 반환
return ResponseEntity.ok(data);
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.and()
.logout()
.logoutSuccessUrl("/login")
.and()
.rememberMe()
.key("uniqueAndSecret")
.rememberMeParameter("remember-me")
.rememberMeCookieName("remember-me-cookie")
.tokenValiditySeconds(86400);
}
}
@GetMapping("/api/data")
public ResponseEntity<?> getData(Authentication authentication) {
if (authentication == null) {
// 인증되지 않은 경우
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
// 데이터 반환
return ResponseEntity.ok(data);
}
@Configuration
@EnableRedisHttpSession
public class HttpSessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
@GetMapping("/api/data")
public ResponseEntity<?> getData(HttpSession session) {
if (session == null || session.getAttribute("user") == null) {
// 인증되지 않은 경우
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
// 데이터 반환
return ResponseEntity.ok(data);
}