์ด์ ์์ ์์๋ ํ์๊ฐ์ , ๋ก๊ทธ์ธ, JWT ๋ฐ๊ธ๊น์ง ๊ตฌํํ๋ค.
๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๋ฉด accessToken์ ๋ฐ๊ธ๋ฐ์ ์ ์์๊ณ , ์ธ์ฆ์ ์ฒซ ๋จ๊ณ๋ ์์ฑ๋ ์ํ์๋ค.
ํ์ง๋ง ์์ง ํ ๊ฐ์ง ๋ฌธ์ ๊ฐ ๋จ์ ์์๋ค.
๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๋ค๋ ์ฌ์ค์ ์๋ฒ๋ ์๊ณ ์์์ง๋ง,
๊ทธ ํ ํฐ์ด ์ค์ API ์์ฒญ์์๋ ์ฌ์ฉ๋์ง ์๊ณ ์์๋ค.
์ฆ ํ์ฌ ์ํ๋ ์ด๋ฐ ๋๋์ด์๋ค.
๋ก๊ทธ์ธ ์ฑ๊ณต
โ JWT ๋ฐ๊ธ
โ ๋
์ถ์
์ฆ์ ๋ฐ๊ธํ๋๋ฐ,
์์ง ๋ฌธ ์ ๊ฒฝ๋น์์ด ์๋ ์ํ์๋ค.
๊ทธ๋์ ์ด๋ฒ ์์ ๋ชฉํ๋ ๋ช ํํ๋ค.
JWT๋ฅผ ์ค์ ์์ฒญ ์ธ์ฆ ํ๋ฆ์ ์ฐ๊ฒฐํ๋ค.
์ฒ์์๋ ์ด๋ฐ ์๊ฐ์ด ๋ค์๋ค.
๋ก๊ทธ์ธ ์ฑ๊ณตํ๋๋ฐ ์ ๋ ํ ํฐ์ ๋ณด๋ด์ผ ํ์ง?
๊ทธ ์ด์ ๋ JWT ๋ฐฉ์์ ์ธ์ ๋ฐฉ์๊ณผ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค.
์ธ์
๋ฐฉ์:
๋ก๊ทธ์ธ
โ ์๋ฒ๊ฐ ์ํ ๊ธฐ์ต
JWT ๋ฐฉ์:
๋ก๊ทธ์ธ
โ ํ ํฐ ๋ฐ๊ธ
API ์์ฒญ
โ ํ ํฐ ๊ฐ์ด ์ ๋ฌ
โ ์๋ฒ๊ฐ ๋งค๋ฒ ๊ฒ์ฆ
์ฆ ์๋ฒ๋ ๋ก๊ทธ์ธ ์ํ๋ฅผ ์ ์ฅํ์ง ์๋๋ค.
๋์ ํด๋ผ์ด์ธํธ๊ฐ ๋งค ์์ฒญ๋ง๋ค
Authorization: Bearer {accessToken}
ํํ๋ก ์ถ์ ์ฆ์ ๋ค๊ณ ์จ๋ค.
๊ทธ๋์ ์์ฒญ์ด Controller๊น์ง ๊ฐ๊ธฐ ์ ์
๋จผ์ ํ ํฐ์ ๊ฒ์ฌํ๋ ํํฐ๋ฅผ ์ถ๊ฐํ๋ค.
์์ฒญ ํ๋ฆ์ ๋ค์์ฒ๋ผ ๋ฐ๋์๋ค.
๋ธ๋ผ์ฐ์
โ
JWT Filter
โ
Controller
โ
Service
JWT Filter ๋ด๋ถ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
Authorization ํค๋ ํ์ธ
โ
Bearer ์ ๊ฑฐ
โ
JWT ์ถ์ถ
โ
ํ ํฐ ๊ฒ์ฆ
โ
์ฌ์ฉ์ ์กฐํ
โ
SecurityContext ์ ์ฅ
JWT ๊ฒ์ฆ ์ฑ ์์ ๊ธฐ์กด JwtTokenProvider๋ฅผ ํ์ฅํด์ ์ฒ๋ฆฌํ๋ค.
public boolean validateToken(String token) {
try {
parseClaims(token);
return true;
} catch (Exception e) {
return false;
}
}
ํ ํฐ์ ์ฝ๊ธฐ๋ง ํ๋ ๊ฒ์ด ์๋๋ผ,
์๋ช
๊ณผ ๋ง๋ฃ์๊ฐ๊น์ง ๊ฒ์ฆํ๋๋ก ํ๋ค.
JWT Filter๋ฅผ ๋ถ์ด๊ณ ๋์๋ ํ ๊ฐ์ง ๋ฌธ์ ๊ฐ ์์๋ค.
์ธ์ฆ์ ๋์๋๋ฐ
๋๊ฐ ๋ก๊ทธ์ธํ๋์ง
๋ฅผ ์ฝ๋ ์์์ ์ฝ๊ฒ ๊บผ๋ผ ์ ์์๋ค.
Spring Security๋ ๋ด๋ถ์ ์ผ๋ก ๋ค์ ๊ณต๊ฐ์ ํ์ฌ ์ฌ์ฉ์๋ฅผ ์ ์ฅํ๋ค.
SecurityContext
๊ตฌ์กฐ๋ ์ด๋ฐ ๋๋์ด๋ค.
SecurityContext
โโโโโโโโโโโโโโ
โ userId=3 โ
โ email=... โ
โโโโโโโโโโโโโโ
ํ์ง๋ง ๋งค๋ฒ ๋ค์์ฒ๋ผ ๊บผ๋ด๋ ๊ฑด ๋๋ฌด ๋ณต์กํ๋ค.
SecurityContextHolder
.getContext()
.getAuthentication()
๊ทธ๋์ ํ์ฌ ๋ก๊ทธ์ธ ์ฌ์ฉ์๋ฅผ ๊ฐ์ ธ์ค๋ ์ ์ฉ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์๋ค.
@Component
public class CurrentUserProvider {
public Long getCurrentUserId() {
Authentication authentication =
SecurityContextHolder
.getContext()
.getAuthentication();
CustomUserDetails userDetails =
(CustomUserDetails)
authentication.getPrincipal();
return userDetails
.getUser()
.getId();
}
}
์ด์ ๋ ์ด๋์๋ ๋ค์์ฒ๋ผ ์ฌ์ฉํ ์ ์๋ค.
Long userId = currentUserProvider.getCurrentUserId();
JWT๊ฐ ์ค์ ๋ก ๋์ํ๋์ง ํ์ธํ๊ธฐ ์ํด
๊ฐ๋จํ API๋ ์ถ๊ฐํ๋ค.
GET /api/users/me
์์ฒญ: Authorization: Bearer {accessToken}
์๋ต: 3
ํ ํฐ์ด ์์ผ๋ฉด: 403 Forbidden
ํ ํฐ์ด ์์ผ๋ฉด: ํ์ฌ ๋ก๊ทธ์ธ ์ฌ์ฉ์ ID ๋ฐํ
์ด ์์ ๋ถํฐ JWT๋ ๋จ์ ๋ฌธ์์ด์ด ์๋๋ผ
์ค์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ฐ๊ฒฐํ๋ ์ญํ ์ ํ๊ธฐ ์์ํ๋ค.

์ธ์ฆ ํ๋ฆ์ ๋ถ์ด๋ค ๋ณด๋
๊ธฐ๋ฅ ์์ฒด๋ณด๋ค ์คํจ ์ํฉ์ ์ฒ๋ฆฌํ๋ ๊ฒ๋ ์ค์ํ๋ค๋ ๊ฑธ ๋๊ผ๋ค.
์๋ฅผ ๋ค์ด ํ์๊ฐ์ ๋ก์ง์ ์ฒ์์ ์ด๋ ๊ฒ ๋์ด ์์๋ค.
if (userRepository.existsByEmail(request.getEmail())) {
throw new IllegalArgumentException("์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ์ด๋ฉ์ผ์
๋๋ค.");
}
์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ์๋ ์ ๋์ํ๋ค.
ํ์ง๋ง ๋์์ ๊ฐ์ ์ด๋ฉ์ผ ๊ฐ์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ์ด๋ฐ ์ํฉ์ด ๊ฐ๋ฅํ๋ค.
์์ฒญ A
existsByEmail()
โ false
์์ฒญ B
existsByEmail()
โ false
์์ฒญ A
insert ์ฑ๊ณต
์์ฒญ B
insert
โ DB unique ์ถฉ๋
๊ทธ๋์ ์ต์ข ์ ์ผ๋ก๋ DB ์ ์ฝ ์กฐ๊ฑด ์์ธ๊น์ง ์ฒ๋ฆฌํ๋๋ก ๋ณด์ํ๋ค.
๋ ๊ธฐ์กด GlobalExceptionHandler๋ ํ์ฅํ๋ค.
400 โ ์๋ชป๋ ์์ฒญ
401 โ ์ธ์ฆ ์คํจ
409 โ ์ค๋ณต ์์ฒญ
500 โ ์๋ฒ ๋ด๋ถ ์ค๋ฅ
๊ธฐ๋ฅ์ด ์ ์ ๋์ํ๋ ๊ฒ๋งํผ,
์คํจํ์ ๋ ์๋ฏธ ์๋ ์๋ต์ ์ฃผ๋ ๊ฒ๋ ์ค์ํ๋ค๊ณ ๋๊ผ๋ค.
์ด๋ฒ ์์ ์ดํ Flowbit ์ธ์ฆ ํ๋ฆ์ ๋ค์์ฒ๋ผ ์ ๋ฆฌ๋๋ค.
ํ์๊ฐ์
โ BCrypt ์ธ์ฝ๋ฉ
โ User ์ ์ฅ
๋ก๊ทธ์ธ
โ JWT ๋ฐ๊ธ
API ์์ฒญ
โ Authorization ํค๋ ์ ๋ฌ
JWT Filter
โ ํ ํฐ ๊ฒ์ฆ
SecurityContext
โ ์ฌ์ฉ์ ์ ์ฅ
CurrentUserProvider
โ ํ์ฌ ์ฌ์ฉ์ ์กฐํ
์ด์ ์์ ์์๋ JWT๋ฅผ ๋ฐ๊ธํ๋ ๋จ๊ณ๊น์ง๋ง ๊ตฌํํ๋ค.
์ด๋ฒ์๋ ๊ทธ ํ ํฐ์ ์ค์ API ์์ฒญ์ ์ฐ๊ฒฐํ๋ค.
์ด์ Flowbit์ ๋จ์ํ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ด ์๋ ์ํ๊ฐ ์๋๋ผ,
๋๊ฐ ์์ฒญํ๋์ง
๋ฅผ ์ ์ ์๋ ์ํ๊ฐ ๋์๋ค.
์ดํ์๋ ์ฌ๊ธฐ์ ์์ฐ์ค๋ฝ๊ฒ ๋ค์ ๊ตฌ์กฐ๋ก ์ด์ด์ง ์ ์๋ค.
Task.createdBy
TaskEvent.actorId
WorkspaceMember.user
RBAC ๊ถํ ์ ์ด
์ค๋ ์์ ์ ํ ๋ฌธ์ฅ์ผ๋ก ์ ๋ฆฌํ๋ฉด
๋ฐ๊ธ๋ JWT๋ฅผ ์ค์ ์ธ์ฆ ํ๋ฆ์ ์ฐ๊ฒฐํ๊ณ , ํ์ฌ ๋ก๊ทธ์ธ ์ฌ์ฉ์์ ์์ธ ์ฒ๋ฆฌ ๊ตฌ์กฐ๊น์ง ํ์ฅํ๋ค.