Загружаем каталог…
Загружаем каталог…
06 · JWT 다루기 💡 한 줄 요약 : JwtUtil에 토큰 생성·전달·추출·검증·Claim 조회 기능을 모읍니다. 구현할 기능 기능 입력 결과 토큰 생성 사용자 식별값, 권한 서명된 JWT 쿠키 저장 토큰, 응답 객체 클라이언트에 토큰 전달 토큰 추출 요청의 쿠키 값 검증할 JWT 문자열 검증 JWT, 서버 키 정상·만료·위변조 판단 Claim 조회 검증된 JWT 사용자 식별값·권한 의존성과 키 설정 implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' jwt.secret.key=${JWT_SECRET_KEY} 여기서 환경변수에는 충분한 길이의 임의 키를 Base64로 인코딩한 값을 넣습니다. HS256에는 최소 256비트의 키가 필요합니다. Base64는 키를 숨기는 암호화가 아니라 문자열 표현 방식입니다. @Value("${jwt.secret.key}") private String secretKey; private Key key; @PostConstruct public void init() { byte[] bytes = Base64.getDecoder().decode(secretKey); key = Keys.hmacShaKeyFor(bytes); } @PostConstruct 는 의존성 주입 이후 초기화에 활용됩니다. 설정값을 읽은 뒤 실제 서명·검증에 사용할 Key를 준비합니다. 토큰 생성 private static final String BEARER_PREFIX = "Bearer "; private static final long TOKEN_TIME = 60 * 60 * 1000L; public String createToken(String username, UserRoleEnum role) { Date now = new Date(); String jwt = Jwts.builder() .setSubject(username) .claim("auth", role.name()) .setIssuedAt(now) .setExpiration(new Date(now.getTime() + TOKEN_TIME)) .signWith(key, SignatureAlgorithm.HS256) .compact(); return BEARER_PREFIX + jwt; } setSubject() 에는 사용자 식별값을 넣습니다. claim() 으로 권한 같은 값을 추가합니다. setExpiration() 은 토큰의 유효기간을 지정합니다. signWith() 는 서명에 사용할 키·알고리즘을 지정합니다. Bearer 는 JWT 자체의 일부가 아니라 토큰을 전달할 때 붙이는 접두어입니다. 쿠키로 전달하기 public void addJwtToCookie(String token, HttpServletResponse response) { String encoded = URLEncoder.encode(token, StandardCharsets.UTF_8) .replace("+", "%20"); Cookie cookie = new Cookie("Authorization", encoded); cookie.setPath("/"); response.addCookie(cookie); } 요청에서 같은 이름의 쿠키를 찾고 URL 디코딩한 뒤 토큰을 사용합니다. 📝 전달 방식 : 이름이 Authorization 인 쿠키와 Authorization HTTP 헤더는 서로 다릅니다. 이번 흐름은 쿠키에서 토큰을 읽습니다. 헤더 방식으로 바꾸려면 클라이언트 전송과 서버 추출 코드를 함께 바꿉니다. Bearer 접두어 제거 public String substringToken(String tokenValue) { if (tokenValue == null || !tokenValue.startsWith(BEARER_PREFIX)) { throw new IllegalArgumentException("토큰 형식이 올바르지 않습니다."); } String token = tokenValue.substring(BEARER_PREFIX.length()); if (token.isBlank()) { throw new IllegalArgumentException("토큰이 비어 있습니다."); } return token; } 서명·만료 검증과 Claim 조회 public Claims parseClaims(String token) { return Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) .getBody(); } 위 메서드는 단순히 문자열을 디코딩하는 작업이 아닙니다. 서명·만료 등의 검증에 실패하면 예외가 발생합니다. 상황 의미 만료된 토큰 허용된 사용 시간이 지남 서명 불일치 토큰 변경 또는 잘못된 검증 키 잘못된 형식 정상적인 JWT 구조가 아님 빈 토큰 검증할 문자열이 없음 Claims claims = jwtUtil.parseClaims(token); String username = claims.getSubject(); String role = claims.get("auth", String.class); 검증 함수와 정보 추출 함수를 따로 만들 수도 있지만, 같은 요청에서 검증을 마친 Claim을 재사용하면 중복 파싱을 줄일 수 있습니다. 복습 체크 토큰에 넣는 사용자 식별값·권한·발급일·만료일을 설명할 수 있습니다. 쿠키 URL 디코딩과 JWT 서명 검증을 구분할 수 있습니다. Bearer 를 제거한 뒤 검증해야 하는 이유를 설명할 수 있습니다. 위변조·만료된 토큰에서 사용자 정보를 그대로 신뢰하지 않습니다. 07 · 회원가입 구현 💡 한 줄 요약 : 회원 중복·역할을 확인하고 비밀번호를 해시한 뒤 회원 정보를 DB에 저장합니다. User에 필요한 정보 필드 역할 확인할 조건 id DB 식별값 기본 키 username 로그인에 사용할 회원 ID 중복 불가 password 비밀번호 해시 평문 저장하지 않음 email 이메일 중복·형식 확인 role 사용자 역할 USER 또는 ADMIN @Entity @Table(name = "users") @Getter @NoArgsConstructor 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; @Enumerated(EnumType.STRING) @Column(nullable = false) private UserRoleEnum role; public User(String username, String password, String email, UserRoleEnum role) { this.username = username; this.password = password; this.email = email; this.role = role; } } EnumType.STRING 은 역할의 문자열 이름을 저장합니다. 순서 번호로 저장하는 방식과 구분합니다. Repository 조회 메서드 public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); Optional<User> findByEmail(String email); } 회원가입 처리 순서 요청 DTO에서 아이디·비밀번호·이메일 등을 받습니다. 같은 아이디 또는 이메일이 이미 존재하는지 확인합니다. 기본 역할을 USER 로 지정합니다. 관리자 가입을 요청했다면 별도 승인 조건을 확인합니다. PasswordEncoder.encode() 로 비밀번호를 해시합니다. User Entity를 만들고 저장합니다. public void signup(SignupRequestDto requestDto) { if (userRepository.findByUsername(requestDto.getUsername()).isPresent()) { throw new IllegalArgumentException("중복된 사용자입니다."); } if (userRepository.findByEmail(requestDto.getEmail()).isPresent()) { throw new IllegalArgumentException("중복된 이메일입니다."); } // 이 축약 예제는 일반 사용자 가입만 보여줍니다. String encoded = passwordEncoder.encode(requestDto.getPassword()); User user = new User( requestDto.getUsername(), encoded, requestDto.getEmail(), UserRoleEnum.USER ); userRepository.save(user); } 비밀번호는 왜 matches()로 비교할까? BCrypt는 같은 비밀번호라도 Salt 때문에 해시 문자열이 달라질 수 있습니다. 입력 비밀번호를 다시 encode() 해서 저장된 문자열과 equals() 로 비교하지 않습니다. boolean matched = passwordEncoder.matches(inputPassword, storedHash); 실습의 관리자 가입 방식 관리자 가입 여부와 별도 관리자 토큰을 받아, 토큰이 맞으면 ADMIN 역할을 저장하는 흐름입니다. 사용자가 보낸 admin=true 만으로 관리자 권한을 부여하지 않습니다. 실제 권한 부여에는 승인된 관리자의 변경 기능 등 별도의 절차가 필요합니다. 📝 중복 확인 : 서비스에서 먼저 조회하면 사용자에게 오류를 안내하기 쉽습니다. 다만 동시에 가입 요청이 들어오는 경우도 있으므로 DB의 Unique 제약조건도 함께 적용합니다. 복습 체크 DTO와 User Entity의 역할을 구분할 수 있습니다. 회원가입 때 비밀번호 해시를 저장할 수 있습니다. 중복 조회와 DB Unique 제약조건이 각각 필요한 이유를 설명할 수 있습니다. 클라이언트가 보낸 값만으로 관리자 권한을 부여하지 않습니다. 08 · 로그인 구현 · JWT 💡 한 줄 요약 : 회원과 비밀번호를 확인한 뒤 해당 사용자의 JWT를 발급합니다. 로그인 요청 기능 메서드 경로 처리 로그인 화면 GET /api/user/login-page 화면 반환 로그인 POST /api/user/login 자격 증명 확인·토큰 발급 @Getter @Setter public class LoginRequestDto { private String username; private String password; } Service에서 직접 처리하는 단계 public void login(LoginRequestDto requestDto, HttpServletResponse response) { User user = userRepository.findByUsername(requestDto.getUsername()) .orElseThrow(() -> new IllegalArgumentException("로그인 정보가 일치하지 않습니다.")); if (!passwordEncoder.matches(requestDto.getPassword(), user.getPassword())) { throw new IllegalArgumentException("로그인 정보가 일치하지 않습니다."); } String token = jwtUtil.createToken(user.getUsername(), user.getRole()); jwtUtil.addJwtToCookie(token, response); } 확인해야 할 결과 상황 기대 동작 존재하는 회원·올바른 비밀번호 JWT 발급 존재하지 않는 회원 로그인 실패 잘못된 비밀번호 로그인 실패 정상 로그인 후 API 요청 발급받은 토큰으로 사용자 확인 이 단계에서는 Service가 직접 비밀번호를 비교합니다. 이후 Spring Security를 적용하면 인증 관리자·Provider가 인증을 처리하도록 연결합니다. 로그아웃과 토큰 유효기간 클라이언트의 쿠키를 삭제하면 해당 브라우저가 토큰을 보내지 않게 됩니다. 하지만 이미 발급된 토큰의 서명·만료 상태가 자동으로 바뀌지는 않습니다. 복사된 토큰까지 즉시 차단하려면 별도의 폐기 정책이 필요합니다. 복습 체크 로그인 성공 시에만 JWT가 발급되는 위치를 설명할 수 있습니다. 로그인 때 저장된 비밀번호 해시를 복호화하지 않습니다. 쿠키 삭제와 서버 측 토큰 무효화의 차이를 설명할 수 있습니다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
Spring 숙련 Chapter 1 - 회원 기능 구현. 06 · JWT 다루기 💡 한 줄 요약 : JwtUtil에 토큰 생성·전달·추출·검증·Claim 조회 기능을 모읍니다. 구현할 기능 기능 입력 결과 토큰 생성 사용자 식별값, 권한 서명된 JWT 쿠키 저장 토큰, 응답 객체 클라이언트에 토큰 전달 토큰 추출 요청의 쿠키 값 검증할 JWT 문자열 검증 JWT, 서버 키 정상·만료·위변조 판단 Claim 조회 검증된 JWT 사용자 식별값·권한 의존성과 키 설정 implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly…
Открыть источник