스프링 부트는 현대적인 웹 애플리케이션 개발을 위한 강력한 프레임워크입니다. 본 강좌에서는 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의 기본 개념과 이를 활용한 인증 시스템 구현의 기초를 이해하시기 바랍니다. 더 나아가, 실제 프로젝트에서 요구되는 다양한 기능을 구현하고 최적화하는 경험을 쌓아보시기 바랍니다.
이 강좌가 여러분의 스프링 부트 백엔드 개발에 도움이 되길 바랍니다!