💡 Spring 핵심 개념 인터뷰 Q&A

JWT 로그인 동작 방식 요약
JWT 로그인 예시
JwtLoginApplication
/**
* The application entry point.
*
* @author Josh Cummings
*/
@SpringBootApplication
public class JwtLoginApplication {
public static void main(String[] args) {
SpringApplication.run(JwtLoginApplication.class, args);
}
}
Restconfig - 보안설정 및 JWT구성
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class RestConfig {
@Value("${jwt.public.key}")
RSAPublicKey key;
@Value("${jwt.private.key}")
RSAPrivateKey priv;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 모든 요청 인증 필요
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
// /token 경로는 CSRF 비활성화
.csrf(csrf -> csrf.ignoringRequestMatchers("/token"))
// 기본 HTTP 인증 사용
.httpBasic(Customizer.withDefaults())
// JWT 기반 인증 설정
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
// 세션 사용 안함 (무상태)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 인증 실패 및 인가 실패 처리 핸들러 등록
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler()));
return http.build();
}
@Bean
UserDetailsService users() {
//메모리 사용자 등록
return new InMemoryUserDetailsManager(
User.withUsername("user")
.password("{noop}password")
.authorities("app")
.build()
);
// @formatter:on
}
@Bean
JwtDecoder jwtDecoder() {
//디코더 설정
return NimbusJwtDecoder.withPublicKey(this.key).build();
}
@Bean
JwtEncoder jwtEncoder() {
// 공개키 - 개인키 기반 JWT 인코더 설정
JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwks);
}
}
HelloController - 인증된 사용자 이름 반환
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello(Authentication authentication) {
// 인증 객체로부터 사용자 이름 반환
return "Hello, " + authentication.getName() + "!";
}
}
TokenController - 토큰발급
import java.time.Instant;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TokenController {
@Autowired
JwtEncoder encoder;
@PostMapping("/token")
public String token(Authentication authentication) {
Instant now = Instant.now();
long expiry = 36000L;
// 권한 정보 문자영로 추출
String scope = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(" "));
//클레임생성
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("self")
.issuedAt(now)
.expiresAt(now.plusSeconds(expiry))
.subject(authentication.getName())
.claim("scope", scope)
.build();
// JWT생성 및 반환
return this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
}
| 상황 | 추천 방식 |
|---|---|
| 내부 관리자용 웹 서비스 (브라우저 기반) | Session |
| 외부 공개 REST API, 모바일 앱 | JWT |
| 대규모 트래픽, 분산 서버 구조 | JWT |
| 빠른 MVP 개발 | Session |