LUMI_dev

사용자 관리하기 - #2. 로그인 구현 JWT 본문

스파르타 코딩 클럽 | 자바 심화 과정/Spring Master (숙련 주차)

사용자 관리하기 - #2. 로그인 구현 JWT

luminous_dev 2025. 2. 5. 11:06

로그인을 하고 완료되면 JWT 토큰을 만들어서 클라이언트에 반환하도록 하겠음 

반환 방법 중 쿠키를 직접 생성해서 JWT 토큰을 담고, Response 객체에 담는 식으로 구현 예정 

 

.orElseThrow()

 

비어있으면 예외 던짐

 

UserController.java

@PostMapping ("/user/signup")
public String signup(SignupRequestDto requestDto) { //객체로 받고 있음
   userService.signup(requestDto);

    return "redirect:/api/user/login-page";
}

//로그인
@PostMapping("/user/login")
public String login(LoginRequestDto requestDto, HttpServletResponse res) {
    try {
        userService.login(requestDto,res); 
        //res = 끝나면 JwtTokenCookie에 넣고, cookie 담으로 Response 객체 넣어주기
        
    } catch (Exception e) {
        return "redirect:/api/user/login-page?error";
    }

    return "redirect:/";
}

 

 

UserService.java

package com.sparta.springauth.service;

import com.sparta.springauth.dto.LoginRequestDto;
import com.sparta.springauth.dto.SignupRequestDto;
import com.sparta.springauth.entity.User;
import com.sparta.springauth.entity.UserRoleEnum;
import com.sparta.springauth.jwt.JwtUtil;
import com.sparta.springauth.repository.UserRepository;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class UserService {

    private final UserRepository userRepository; //interface
    private final PasswordEncoder passwordEncoder;

    //로그인에서 Jwt 쓰기 위해 호출
    private final JwtUtil jwtUtil;

    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.jwtUtil = jwtUtil;
    }
	.
    .
    .

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

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

        ); //중복과 달리 사용자 확인해서 없으면 error 처리를 해줘야 함 - optional의 기능 중 하나인 orElseThrow
        //() -> 람다식 : 인자가 없는 메서드 정의 /Supplier 인터페이스의 get()메서드 구현- 인자를 받지 않고 값을 반환하는 메서드



        //비밀번호 확인
        if(!passwordEncoder.matches(password, user.getPassword())) {
                                    //평문(입력 받은 데이터), 암호화된 비밀번호
            //서로 일치하지 않으면
            throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");

        }

        //JWT 생성 및 쿠키에 저장 후 Response 객체에 추가
        //(JWT 사용하려면 Jwtutil을 호출해야함)
        String token = jwtUtil.createToken(user.getUsername(), user.getRole());

        //JWT를 쿠키에 넣고 Response객체에 넣기
        jwtUtil.addJwtToCookie(token,res); //res에 담는 과정까지
        
    }
}

 


전체 코드

UserController.java

package com.sparta.springauth.controller;

import com.sparta.springauth.dto.LoginRequestDto;
import com.sparta.springauth.dto.SignupRequestDto;
import com.sparta.springauth.service.UserService;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/api")
public class UserController {

    //주입
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    //로그인 페이지
    @GetMapping("/user/login-page")
    public String loginPage() {
        return "login";
    }

    //회원 가입 페이지
    @GetMapping("/user/signup")
    public String signupPage() {
        return "signup";
    }

    @PostMapping ("/user/signup")
    public String signup(SignupRequestDto requestDto) { //객체로 받고 있음
       userService.signup(requestDto);

        return "redirect:/api/user/login-page";
    }

    //로그인
    @PostMapping("/user/login")
    public String login(LoginRequestDto requestDto, HttpServletResponse res) {
        try {
            userService.login(requestDto,res);
        } catch (Exception e) {
            return "redirect:/api/user/login-page?error";
        }

        return "redirect:/";
    }
}

 

 

 

 

 

UserService.java

package com.sparta.springauth.service;

import com.sparta.springauth.dto.LoginRequestDto;
import com.sparta.springauth.dto.SignupRequestDto;
import com.sparta.springauth.entity.User;
import com.sparta.springauth.entity.UserRoleEnum;
import com.sparta.springauth.jwt.JwtUtil;
import com.sparta.springauth.repository.UserRepository;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class UserService {

    private final UserRepository userRepository; //interface
    private final PasswordEncoder passwordEncoder;

    //로그인에서 Jwt 쓰기 위해 호출
    private final JwtUtil jwtUtil;

    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.jwtUtil = jwtUtil;
    }

    // ADMIN_TOKEN
    private final String ADMIN_TOKEN = "AAABnvxRVklrnYxKZ0aHgTBcXukeZygoC";

    //회원 가입
    public void signup(SignupRequestDto requestDto) {
        String username = requestDto.getUsername();

        //암호화된 패스워드
        String password = passwordEncoder.encode(requestDto.getPassword());

        // 회원 중복 확인
        Optional<User> checkUsername = userRepository.findByUsername(username);
        //Optional은 값이 존재하거나 존재하지 않을 수 있음을 명시적으로 나타내는 컨테이너
        //Optional<User>는 User 객체를 감싸고 있으며, User가 존재할 수도 있고, 존재하지 않을 수도 있음


        if (checkUsername.isPresent()) {
            throw new IllegalArgumentException("중복된 사용자가 존재합니다.");
        }

        // email 중복확인
        String email = requestDto.getEmail();
        Optional<User> checkEmail = userRepository.findByEmail(email);
        if (checkEmail.isPresent()) { //.isPresent() : 값이 존재하는지 아닌지
            throw new IllegalArgumentException("중복된 Email 입니다.");
        }

        // 사용자 ROLE 확인
        UserRoleEnum role = UserRoleEnum.USER;//사용자 권한을 일단 설정해둠
        if (requestDto.isAdmin()) { //.isAdmin하면 Dto의 boolean admin값 가져옴
            if (!ADMIN_TOKEN.equals(requestDto.getAdminToken())) {
                throw new IllegalArgumentException("관리자 암호가 틀려 등록이 불가능합니다.");
            }
            role = UserRoleEnum.ADMIN; //어드민 권한으로 덮어씀
        }

        // 사용자 등록
        //테이블의 한 행에 해당하는 데이터를 만들어야 함
        User user = new User(username, password, email, role);
        userRepository.save(user); //repository에 의해 저장
    }

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

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

        ); //중복과 달리 사용자 확인해서 없으면 error 처리를 해줘야 함 - optional의 기능 중 하나인 orElseThrow
        //() -> 람다식 : 인자가 없는 메서드 정의 /Supplier 인터페이스의 get()메서드 구현- 인자를 받지 않고 값을 반환하는 메서드



        //비밀번호 확인
        if(!passwordEncoder.matches(password, user.getPassword())) {
                                    //평문(입력 받은 데이터), 암호화된 비밀번호
            //서로 일치하지 않으면
            throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");

        }

        //JWT 생성 및 쿠키에 저장 후 Response 객체에 추가
        //(JWT 사용하려면 Jwtutil을 호출해야함)
        String token = jwtUtil.createToken(user.getUsername(), user.getRole());

        //JWT를 쿠키에 넣고 Response객체에 넣기
        jwtUtil.addJwtToCookie(token,res); //res에 담는 과정까지
        
    }
}