자기개발/검색한 자료 정리

Spring Boot에서 JWT를 사용하여 인증 및 인가 구현하기

실버블렛 2023. 4. 19. 21:26
반응형



요약: 이 글에서는 Spring Boot 기반의 웹 애플리케이션에서 JSON Web Token(JWT)을 사용하여 인증 및 인가를 구현하는 방법을 소개합니다. 개발자들이 일하다가 모르는 것이 있다면 이 글을 참고하여 JWT를 이용한 인증 및 인가 구현을 더 효과적으로 설계하고 구현할 수 있습니다.

JWT란?

JSON Web Token(JWT)은 웹 애플리케이션에서 사용되는 인증 및 인가 메커니즘 중 하나로, 간단하고 안전한 방법으로 서버와 클라이언트 사이에 정보를 전달할 수 있습니다. JWT는 헤더(header), 페이로드(payload), 시그니처(signature)의 세 부분으로 구성되어 있으며, 각 부분은 Base64Url 인코딩을 통해 문자열로 변환된 후 마침표(.)로 연결되어 생성됩니다.

필요한 의존성 추가

먼저, Spring Boot 프로젝트에 JWT를 사용하기 위해 필요한 의존성을 추가해야 합니다. pom.xml 파일에 다음과 같이 jjwt 라이브러리를 추가합니다.

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

JWT 유틸리티 클래스 생성

JWT를 생성하고 검증하는 유틸리티 클래스를 생성합니다. 이 클래스에서는 JWT를 생성할 때 사용할 비밀키를 정의하고, JWT의 생성 및 검증 메서드를 구현합니다.

예시:

import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtUtil {

    private final String secretKey = "mySecretKey";

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) // 1시간 동안 유효
                .signWith(SignatureAlgorithm.HS512, secretKey)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }
}

인증 및 인가 필터 생성

JWT를 사용하여 인증 및 인가를 처리하는 필터를 생성합니다. 이 필터에서는 사용자의 요청을 검사하여 JWT 토큰이 유효한지 확인하고, 유효한 경우 사용자의 인증 정보를 Spring Security 컨텍스트에 설정합니다.

예시:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;

    public JwtAuthenticationFilter(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authorizationHeader = request.getHeader("Authorization");

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            String token = authorizationHeader.substring(7);
            String username = jwtUtil.getUsernameFromToken(token);

            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                if (jwtUtil.validateToken(token)) {
                    Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }

        filterChain.doFilter(request, response);
    }
}

Spring Security 설정 업데이트

Spring Security 설정을 업데이트하여 JWT 인증 필터를 추가하고, 인증 요청이 필요한 엔드포인트를 정의합니다.

예시:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;
    private final JwtUtil jwtUtil;

    public SecurityConfig(UserDetailsService userDetailsService, JwtUtil jwtUtil) {
        this.userDetailsService = userDetailsService;
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/authenticate").permitAll()
                .anyRequest().authenticated()
            .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}


이제 Spring Boot 웹 애플리케이션에서 JWT를 사용하여 인증 및 인가를 처리할 수 있습니다. 클라이언트는 사용자 인증을 위해 /api/authenticate 엔드포인트에 요청을 보내고, 성공적으로 인증된 경우 JWT 토큰을 받습니다. 이 토큰은 이후의 요청에서 "Authorization" 헤더에 "Bearer {토큰}" 형식으로 포함되어야 합니다. 토큰이 유효한 경우, 사용자는 인증이 필요한 엔드포인트에 접근할 수 있습니다.

사용자 인증 엔드포인트 추가

마지막으로, 사용자 인증을 위한 엔드포인트를 추가해야 합니다. 사용자가 제공한 이메일과 비밀번호를 검증하고, 성공적으로 인증된 경우 JWT 토큰을 생성하여 반환합니다.

예시:

import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class AuthenticationController {

    private final AuthenticationManager authenticationManager;
    private final JwtUtil jwtUtil;

    public AuthenticationController(AuthenticationManager authenticationManager, JwtUtil jwtUtil) {
        this.authenticationManager = authenticationManager;
        this.jwtUtil = jwtUtil;
    }

    @PostMapping("/authenticate")
    public ResponseEntity<?> authenticate(@RequestBody AuthenticationRequest authenticationRequest) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authenticationRequest.getEmail(), authenticationRequest.getPassword())
        );

        String token = jwtUtil.generateToken(authentication.getName());
        return ResponseEntity.ok(new AuthenticationResponse(token));
    }
}


이제 클라이언트는 사용자 인증을 위해 /api/authenticate 엔드포인트에 요청을 보낼 수 있으며, 서버는 이를 처리하여 적절한 JWT 토큰을 반환합니다. 이 토큰은 클라이언트가 이후의 요청에서 사용하여 인증 및 인가를 처리할 수 있게 됩니다.

이 글에서는 Spring Boot 기반의 웹 애플리케이션에서 JWT를 사용하여 인증 및 인가를 구현하는 방법을 설명했습니다. 이를 통해 개발자들은 안전하고 효과적인 인증 및 인가 메커니즘을 쉽게 구현할 수 있습니다. 만약 개발 과정에서 문제가 발생하거나 추가 정보가 필요한 경우, 이 글을 참조하여 필요한 내용을 검색하고 찾아볼 수 있습니다.


#SpringBoot #JWT #인증 #인가 #JSONWebToken #WebApplication #API #Security #Java #프로그래밍

반응형