반응형
Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
Tags
- string
- 자바
- 인텔리제이
- junit
- 자바스크립트
- js
- Visual Studio Code
- Eclipse
- math
- 배열
- 정규식
- Array
- javascript
- vscode
- IntelliJ
- HashMap
- junit5
- CSS
- ArrayList
- input
- 자바문법
- json
- html
- 이클립스
- list
- 문자열
- Java
- 테스트자동화
- 단위테스트
- java테스트
Archives
- Today
- Total
어제 오늘 내일
[Spring Security] 9편 - JWT 로그인 API 구현 & Postman 테스트 본문
지난 시간까지 보안 설정(Config)을 바꾸고, 필터(Filter)를 장착했습니다.
하지만 기존의 Form Login을 삭제했기 때문에, 현재는 로그인도 회원가입도 할 수 없는 상태입니다.
이번 마지막 시간에는 사용자가 JSON으로 아이디/비번을 보내면 토큰을 발급해 주는 API 컨트롤러를 만들고,
Postman으로 전체 흐름을 테스트하며 대장정을 마무리하겠습니다.
Step 1. 로그인 요청용 DTO 만들기
로그인할 때 받을 데이터(ID, PW)를 담을 객체입니다.
- 위치:
src/main/java/com/example/board/dto/MemberLoginDto.java
package com.example.board.dto;
import lombok.Data;
@Data
public class MemberLoginDto {
private String username;
private String password;
}
Step 2. 로그인 로직 구현 (MemberService)
여기가 "실제 인증"이 일어나는 핵심 로직입니다.
사용자가 입력한 정보가 맞는지 확인하고, 맞다면 토큰 발급기(JwtTokenProvider)를 통해 출입증을 발급합니다.
- 위치:
src/main/java/com/example/board/service/MemberService.java
package com.example.board.service;
import com.example.board.domain.entity.Member;
import com.example.board.domain.repository.MemberRepository;
import com.example.board.dto.MemberJoinDto;
import com.example.board.dto.MemberLoginDto;
import com.example.board.jwt.JwtToken;
import com.example.board.jwt.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {
private final MemberRepository memberRepository;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final JwtTokenProvider jwtTokenProvider;
private final PasswordEncoder passwordEncoder;
@Transactional
public void join(MemberJoinDto memberJoinDto) {
if (memberRepository.findByUsername(memberJoinDto.getUsername()).isPresent()) {
throw new IllegalStateException("이미 존재하는 아이디입니다.");
}
Member member = memberRepository.save(memberJoinDto.toEntity());
member.encodePassword(passwordEncoder);
}
// ★ 실제 로그인 처리 로직
@Transactional
public JwtToken login(String username, String password) {
// 1. Login ID/PW 를 기반으로 Authentication 객체 생성
// 이때 authentication 은 인증 여부를 확인하는 authenticated 값이 false
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
// 2. 실제 검증 (사용자 비밀번호 체크)이 이루어지는 부분
// authenticate 매서드가 실행될 때 CustomUserDetailsService 에서 만든 loadUserByUsername 메서드가 실행
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
// 3. 인증 정보를 기반으로 JWT 토큰 생성
JwtToken jwtToken = jwtTokenProvider.createToken(authentication);
return jwtToken;
}
}
Step 3. 로그인 API 컨트롤러 (MemberController)
기존 컨트롤러를 수정합니다. 화면(HTML)을 반환하는 게 아니라, JSON 데이터를 주고받아야 하므로 @RequestBody를 사용합니다.
[수정 포인트]
- 회원가입(
join): JSON 데이터를 받아서 처리하도록 변경. - 로그인(
login): ID/PW를 JSON으로 받아 토큰을 반환. - 경로(
RequestMapping):/members로 묶어서 관리.
- 위치:
src/main/java/com/example/board/controller/MemberController.java
package com.example.board.controller;
import com.example.board.dto.MemberJoinDto;
import com.example.board.dto.MemberLoginDto;
import com.example.board.jwt.JwtToken;
import com.example.board.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/members")
public class MemberController {
private final MemberService memberService;
// 1. 로그인 API
@PostMapping("/login")
public JwtToken login(@RequestBody MemberLoginDto memberLoginDto) {
String username = memberLoginDto.getUsername();
String password = memberLoginDto.getPassword();
JwtToken jwtToken = memberService.login(username, password);
log.info("request username = {}, password = {}", username, password);
log.info("jwtToken accessToken = {}, refreshToken = {}", jwtToken.getAccessToken(), jwtToken.getRefreshToken());
return jwtToken;
}
// 2. 회원가입 API
@PostMapping("/join")
public String join(@RequestBody MemberJoinDto memberJoinDto) {
try {
memberService.join(memberJoinDto);
return "회원가입 성공";
} catch (Exception e) {
return e.getMessage();
}
}
// 3. 테스트 API (로그인 한 사람만 접근 가능)
@PostMapping("/test")
public String test() {
return "success";
}
}
Step 4. SecurityConfig 경로 수정 (중요!)
컨트롤러의 주소가 /members/...로 시작하게 바뀌었으므로, 시큐리티 설정에서 허용할 주소(requestMatchers)도 맞춰줘야 합니다. 이걸 안 하면 403 에러가 뜹니다.
- 위치:
src/main/java/com/example/board/config/SecurityConfig.java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.httpBasic((basic) -> basic.disable())
.csrf((csrf) -> csrf.disable())
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests((auth) -> auth
// ▼▼▼ 여기 경로를 잘 봐주세요! (/members/...) ▼▼▼
.requestMatchers("/members/login", "/members/join").permitAll()
.requestMatchers("/members/test").hasRole("USER")
.anyRequest().authenticated()
)
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
Step 5. Postman으로 테스트하기 (대망의 순간)
이제 브라우저가 아닌 Postman을 켭니다. (없으면 설치해주세요!)
1. 회원가입 (JSON 전송)
- Method: POST
- URL:
http://localhost:8080/members/join - Body:
Raw->JSON선택 - Data:
{
"username": "silver",
"password": "1234",
"role": "USER"
}
(DTO 필드명과 일치해야 합니다. role 등은 DTO 구성에 따라 생략 가능)
- Send ->
"회원가입 성공"문자열 도착.
2. 로그인 시도 (토큰 발급)
- Method: POST
- URL:
http://localhost:8080/members/login - Body:
Raw->JSON선택
{ "username": "silver", "password": "1234" } - Send 클릭!
[결과 확인]
하단 응답창에 아래와 같이 나오면 성공입니다!
{
"grantType": "Bearer",
"accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzaWx2ZXIiLCJhdXRoIjoiUk9MRV9VU0VSIiw...",
"refreshToken": null
}
저 긴 문자열이 바로 여러분의 출입증(JWT)입니다.
3. 토큰 사용하여 요청 보내기
이제 저 토큰을 가지고, 로그인한 사람만 갈 수 있는 /members/test 페이지를 뚫어봅시다.
- Method: POST
- URL:
http://localhost:8080/members/test - 그냥 Send ->
403 Forbidden(출입금지) 뜸. (정상) - Header 탭 이동
- Key:
Authorization - Value:
Bearer eyJhbGciOiJIUz...(아까 받은 토큰 복붙, 앞에 Bearer와 띄어쓰기 한 칸 필수!)
- 다시 Send ->
"success"문자열 도착! 🎉
🎉 시리즈 완결
축하드립니다! 여러분은 이제:
- Spring Security의 기본 설정을 이해하고,
- DB와 연동하여 회원을 관리하며,
- 비밀번호를 암호화하여 저장하고,
- 최신 트렌드인 JWT 토큰 기반 인증 시스템까지 직접 구축했습니다.
반응형
'IT > SpringBoot' 카테고리의 다른 글
| [Spring Security] 10편 - 한눈에 보는 JWT 동작 원리 (Sequence Diagram & 총정리) (0) | 2026.02.21 |
|---|---|
| [Spring Security] 8편 - SecurityConfig 대수술 (FormLogin 삭제 & JWT 필터 적용) (0) | 2026.02.20 |
| [Spring Security] 7편 - JWT 로그인을 위한 준비 & TokenProvider 만들기 (0) | 2026.02.19 |
| [Spring Security] 6편 - 세션(Session) vs 토큰(JWT), 도대체 뭐가 다를까? (0) | 2026.02.19 |
| [Spring Security] 5편 - 회원가입 & 비밀번호 암호화 (BCrypt) (0) | 2026.02.18 |
Comments
