스프링 부트는 현대적인 웹 애플리케이션 개발을 위한 강력한 프레임워크입니다. 본 강좌에서는 JWT(Json Web Token)를 사용하여 사용자 인증을 구현하고, 이를 통해 로그인 및 로그아웃 기능을 구현하는 방법에 대해 단계별로 설명하겠습니다. 이 과정에서는 토큰 서비스를 추가하여 보안 및 사용자 관리 기능을 향상시키는 방법도 다룰 것입니다.
1. JWT란 무엇인가?
JWT는 JSON을 기반으로 한 개방형 표준으로, 정보를 안전하게 전달하기 위한 방식을 제공합니다. JWT는 세 부분으로 구성되어 있습니다: Header, Payload, Signature.
1.1 Header
Header는 JWT의 타입과 해싱 알고리즘을 지정합니다. 예를 들어:
{ "alg": "HS256", "typ": "JWT" }
1.2 Payload
Payload 부분은 사용자의 정보와 사용자 ID, 만료 시간 등을 포함하는 본문입니다. 이 부분은 알아보기 쉬운 JSON 형식으로 구성되어 있습니다.
1.3 Signature
Signature는 인코딩된 Header와 Payload를 조합하여 비밀키로 서명한 값입니다. 이 값은 토큰의 무결성을 보장하고, 서버가 토큰을 검증하는 데 사용됩니다.
2. 프로젝트 설정
이 강좌에서는 스프링 부트와 Maven을 사용하여 프로젝트를 생성합니다. IntelliJ IDEA 또는 Eclipse와 같은 IDE를 이용하여 프로젝트를 설정할 수 있습니다.
2.1 Maven 프로젝트 생성
이클립스 또는 IntelliJ IDEA에서 Maven 프로젝트를 생성한 후, pom.xml 파일에 다음 의존성을 추가합니다.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjsonwebtoken</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies>
2.2 Spring Security 설정
Spring Security를 사용하여 기본적인 보안 설정을 해줍니다. SecurityConfig 클래스를 생성하고, 사용자 인증 및 권한 설정을 추가합니다.
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated(); } }
3. JWT 생성 및 검증
이제 JWT를 생성하고 검증하는 방법을 살펴보겠습니다. JWTUtil 클래스를 생성하여 필요한 메서드를 구현합니다.
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class JWTUtil { private String secretKey = "secret"; public String generateToken(String username) { Map<String, Object> claims = new HashMap<>(); return createToken(claims, username); } private String createToken(Map<String, Object> claims, String subject) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10시간 .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); } public Boolean validateToken(String token, String username) { final String extractedUsername = extractUsername(token); return (extractedUsername.equals(username) && !isTokenExpired(token)); } private String extractUsername(String token) { return extractAllClaims(token).getSubject(); } private Claims extractAllClaims(String token) { return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody(); } private Boolean isTokenExpired(String token) { return extractAllClaims(token).getExpiration().before(new Date()); } }
4. 사용자 인증 및 로그인 API 구현
이제 사용자 인증 및 로그인 API를 구현할 차례입니다. AuthController 클래스를 생성하여 필요한 메서드를 추가합니다.
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JWTUtil jwtUtil; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody AuthRequest authRequest) { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()) ); final String token = jwtUtil.generateToken(authRequest.getUsername()); return ResponseEntity.ok(token); } } class AuthRequest { private String username; private String password; // getters and setters }
5. 로그아웃 API 구현
로그아웃 API는 클라이언트에서 JWT 토큰을 삭제하여 구현합니다. 별도의 로그아웃 API가 필요하지 않지만 이 부분에 대한 예제를 추가할 수 있습니다.
6. 토큰 서비스 추가하기
토큰 서비스를 추가하여 사용자 정보를 관리하고, 필요한 경우 사용자 세션을 관리하는 기능을 추가합니다. TokenService 클래스를 생성하여 이 기능을 구현합니다.
import org.springframework.stereotype.Service; @Service public class TokenService { @Autowired private JWTUtil jwtUtil; public String refreshToken(String token) { if (jwtUtil.isTokenExpired(token)) { String username = jwtUtil.extractUsername(token); return jwtUtil.generateToken(username); } return token; } }
7. 기타 고려사항 및 마무리
본 강좌에서는 JWT를 사용하여 로그인 및 로그아웃 기능을 구현하고, 사용자 인증을 위한 토큰 서비스를 추가하는 방법을 살펴보았습니다. 실제 애플리케이션에서는 JWT의 유효성 검사를 강화하고 사용자 권한 관리, 토큰 저장소 구현 등 추가적인 보안 조치가 필요합니다.
8. 결론
스프링 부트를 활용한 JWT 기반의 인증 시스템 구현은 현대 웹 애플리케이션의 보안을 강화하는 데 필수적인 요소가 되었습니다. 본 강좌를 통해 JWT의 기본 개념과 이를 활용한 인증 시스템 구현의 기초를 이해하시기 바랍니다. 더 나아가, 실제 프로젝트에서 요구되는 다양한 기능을 구현하고 최적화하는 경험을 쌓아보시기 바랍니다.
이 강좌가 여러분의 스프링 부트 백엔드 개발에 도움이 되길 바랍니다!