JWT : 회원가입, 로그인, 필터

coldrice99·2024년 10월 25일
0
post-thumbnail

회원가입, 로그인, 그리고 필터 구현하기

이번 글에서는 사용자 관리 기능을 구현하면서 배운 내용을 정리해 보았습니다. 회원가입, 로그인, 그리고 필터를 활용한 인증과 인가를 어떻게 구현했는지 살펴보겠습니다.

1. 회원가입 구현

User 엔터티 클래스 구현

회원 정보를 저장하기 위해 User 엔터티 클래스를 작성했습니다. 이 클래스는 회원의 ID, 사용자 이름, 비밀번호, 이메일, 역할을 저장합니다:

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private UserRoleEnum role;
}

회원가입 API 구현

회원가입 요청을 처리하기 위해 UserService에 회원가입 로직을 구현했습니다. 중복 사용자나 이메일을 확인한 후, 비밀번호는 PasswordEncoder를 사용하여 암호화합니다:

public void signup(SignupRequestDto requestDto) {
    String username = requestDto.getUsername();
    String password = passwordEncoder.encode(requestDto.getPassword());

    // 회원 중복 확인
    Optional<User> checkUsername = userRepository.findByUsername(username);
    if (checkUsername.isPresent()) {
        throw new IllegalArgumentException("중복된 사용자가 존재합니다.");
    }

    // 사용자 등록
    User user = new User(username, password, requestDto.getEmail(), UserRoleEnum.USER);
    userRepository.save(user);
}

2. 로그인 구현: JWT

로그인 기능은 JWT(Json Web Token)를 이용해 구현했습니다. 사용자가 로그인하면 JWT를 생성해 쿠키에 저장하고, 이후 요청마다 해당 토큰을 이용해 인증을 수행합니다.

로그인 API 구현

사용자가 입력한 아이디와 비밀번호가 데이터베이스에 저장된 정보와 일치하는지 확인한 후, JWT를 생성하여 클라이언트에게 전달합니다:

public void login(LoginRequestDto requestDto, HttpServletResponse res) {
    String username = requestDto.getUsername();
    String password = requestDto.getPassword();

    User user = userRepository.findByUsername(username).orElseThrow(
            () -> new IllegalArgumentException("등록된 사용자가 없습니다.")
    );

    if (!passwordEncoder.matches(password, user.getPassword())) {
        throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
    }

    String token = jwtUtil.createToken(user.getUsername(), user.getRole());
    jwtUtil.addJwtToCookie(token, res);
}

3. 필터 구현: 인증 및 인가

필터 개념

필터는 웹 애플리케이션에서 클라이언트로부터 오는 요청과 응답을 가로채어 처리할 수 있는 기능입니다. 주로 로깅, 보안, 인증, 인가와 같은 작업에 사용됩니다.

LoggingFilter 구현

요청 URL을 로깅하기 위해 LoggingFilter를 작성했습니다. 이 필터는 모든 요청의 URL을 로깅하며, 후처리로 비즈니스 로직 완료 메시지를 기록합니다:

@Slf4j(topic = "LoggingFilter")
@Component
@Order(1)
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();
        log.info(url);

        chain.doFilter(request, response);
        log.info("비즈니스 로직 완료");
    }
}

AuthFilter 구현

AuthFilter는 JWT를 검증하여 인증된 사용자만이 요청을 처리할 수 있도록 합니다. 회원가입, 로그인, 정적 리소스를 제외한 모든 요청에 대해 인증을 수행합니다:

@Slf4j(topic = "AuthFilter")
@Component
@Order(2)
public class AuthFilter implements Filter {
    private final UserRepository userRepository;
    private final JwtUtil jwtUtil;

    public AuthFilter(UserRepository userRepository, JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.jwtUtil = jwtUtil;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();

        if (url.startsWith("/api/user") || url.startsWith("/css") || url.startsWith("/js")) {
            chain.doFilter(request, response);
        } else {
            String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);

            if (StringUtils.hasText(tokenValue)) {
                String token = jwtUtil.substringToken(tokenValue);

                if (!jwtUtil.validateToken(token)) {
                    throw new IllegalArgumentException("Token Error");
                }

                Claims info = jwtUtil.getUserInfoFromToken(token);
                User user = userRepository.findByUsername(info.getSubject()).orElseThrow(() ->
                        new NullPointerException("Not Found User")
                );

                request.setAttribute("user", user);
                chain.doFilter(request, response);
            } else {
                throw new IllegalArgumentException("Not Found Token");
            }
        }
    }
}

결론

이번 포스트에서는 제가 학습한 내용을 바탕으로 회원가입, 로그인, 그리고 필터를 활용한 사용자 관리 기능을 구현하는 방법을 소개했습니다. 이 과정에서 Spring Security의 PasswordEncoder를 사용해 비밀번호를 암호화하고, JWT를 활용해 인증을 구현했습니다. 필터를 통해 인증과 인가 로직을 비즈니스 로직과 분리함으로써 코드의 유지보수성을 높였습니다.

필터를 이용한 인증 및 인가 처리, JWT를 활용한 로그인 구현이 특히 유용했으며, 이러한 기술을 사용해 더욱 안전하고 효율적인 사용자 관리를 할 수 있었습니다.


https://github.com/coldrice99/spring-auth

profile
서두르지 않으나 쉬지 않고

0개의 댓글