2021. 12. 21. 11:43ใBack-End ์์ ์ค/๊ธฐํ ์ฐธ๊ณ ์๋ฃ
๐ ํ ํฐ์ ์ฌ์ฉํ ์ด์ ?
๊ฐ์ฅ ํฐ ์ด์ ๋ HTTP์ ํน์ง ์ค Connectionless ์ Stateless ๋๋ฌธ์ ๋๋ค.
HTTP ํ๋กํ ์ฝ์ ํ๋ฒ์ ์์ฒญ - ์๋ต ์ฌ์ดํด์ด ์๋ฃ๋๋ฉด ์ฐ๊ฒฐ์ ์ข ๋ฃํ๊ธฐ ๋๋ฌธ์, ๋์ผํ ํด๋ผ์ด์ธํธ๊ฐ ์ฌ๋ฌ๋ฒ ์์ฒญ์ ํด์ค๋๋ผ๋ ์๋ฒ๋ ํด๋น ํด๋ผ์ด์ธํธ์ ๋ํ ๊ถํ์ ์ธ์งํ ์ ์์ต๋๋ค.
ํด๋ผ์ด์ธํธ์ ์ํ๋ฅผ ์ ์งํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ผ๋ก๋ ๋ํ์ ์ผ๋ก ์ธ์ ๊ณผ ์ฟ ํค ๋ฐฉ์์ด ์์ต๋๋ค.
Cookie ๋ฐฉ์ : ๊ฐ๋ณ Client ์ํ ์ ๋ณด๋ฅผ HTTP Request / Response Header์ ๋ด์ ์ ๋ฌํ๋ Infomation / Data๋ฅผ ์ด์ผ๊ธฐ ํฉ๋๋ค.
Cookie๋ ์น๋ช ์ ์ธ ๋จ์ ์ด ์๋๋ฐ, ๊ทธ๊ฒ์ ๋ฐ๋ก ๋ณด์ ์ทจ์ฝ์ ์ ๋๋ค.
Cookie ๋ฐฉ์์ Client ์ํ ์ ๋ณด๋ฅผ Client์ ์ ์ฅํ๊ณ , HTTP Request / Response Header์ ๋ด์ ์ ๋ฌํ๊ธฐ ๋๋ฌธ์ ํดํน ๋ฐ ์ค๋ํ ๊ณต๊ฒฉ์ ์ํ ๋ณ์กฐ์ ์ธ๋ถ ๋ ธ์ถ์ ์ทจ์ฝํ ๋จ์ ์ด ์์ต๋๋ค.
์ ์ฌ์ง์ฒ๋ผ ์ํธํ ๋์ง ์์ Cookie๋ ํต์ ๊ฐ ์ ๋ณด๊ฐ ๋ชจ๋ ๋ ธ์ถ๋ฉ๋๋ค.
Session ๋ฐฉ์ : ๊ฐ๋ณ Client ์ํ ์ ๋ณด๋ฅผ Server์ ์ ์ฅํ๋ ๊ธฐ์ ์ ๋๋ค.
Server๋ ๊ฐ๋ณ Client Session ์๋ณํ๊ธฐ ์ํด "Session ID"๋ฅผ ๋ถ์ฌํ๊ณ , ์ด๊ฒ์ "Session Cookie"๋ฅผ ์ด์ฉํ์ฌ Client์ Server ๊ฐ์ ์ฃผ๊ณ ๋ฐ๋ ํ์์ผ๋ก ๋์ํฉ๋๋ค.
Session๋ฐฉ์์ Client ์ํ ์ ๋ณด๋ฅผ Server์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ Cookie์ ๋นํด ๋ณด์์ ์์ ํ๋ค๋ ์ฅ์ ์ด ์์ผ๋, ์ด๊ฒ ์ญ์ ๋ณด์์ ์ทจ์ฝ์ ์ด ์๋๋ฐ, ๊ณต๊ฒฉ์๊ฐ ์ ์์ ์ธ ์ฌ์ฉ์์ Session ID ์ ๋ณด๋ฅผ ํ์ทจํ๋ค๋ฉด ์ ์ ์ฌ์ฉ์๋ก ์์ฅ ์ ๊ทผ์ด ๊ฐ๋ฅํ HTTP Session Hijecking ๊ณต๊ฒฉ์ด ๊ฐ๋ฅํ๋ค๋ ์ทจ์ฝ์ ์ด ์๊ณ , Session ์ ์ฅ์๋ฅผ Server์์ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ด์ฉ์๊ฐ ๋ง์์ง๋ฉด ๋ง์์ง์๋ก Server์ ๊ฑธ๋ฆฌ๋ ๋ถํ๊ฐ ์ฆ๊ฐํ๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
์์ ๋๊ฐ์ง ๋ฐฉ์์ด ๋ํ์ ์ธ ์ธ์ฆ ๋ฐฉ์์ด๋, ์ด๊ฑฐํ ๋จ์ ๋ค์ ๊ณ ๋ คํ์ฌ ์ ํฌ๋ JWT(Json Web Token)๋ผ๋ ๋ฐฉ์์ ์ฑํํ์ฌ ์ธ์ฆ์ ๊ตฌํ ํด ๋ณด์์ต๋๋ค.
ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ์์คํ ์ ์ ์ ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์๋ฒ๋ ์ธ์ ์ ๋ด์๋์ง ์์ต๋๋ค. ํด๋ผ์ด์ธํธ๋ API ์์ฒญํ ๋๋ง๋ค ํ ํฐ์ HTTP Authorization header์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ด์ ์๋ฒ์๊ฒ ๋ณด๋ ๋๋ค. ์๋ฒ๋ ์ด ํ ํฐ์ ํตํ์ฌ ํด๋ผ์ด์ธํธ๋ค์ ์๋ณํ ์ ์์ต๋๋ค.
JWT Token ๋ฐฉ์์ ํฌ๊ฒ 3๊ฐ์ง ์์๋ก ๊ตฌ์ฑ์ด ๋ฉ๋๋ค.
- Header : 3๊ฐ์ง ์์๋ฅผ ์ํธํํ ์๊ณ ๋ฆฌ์ฆ ๋ฑ์ Option Value
- Payload : User์ ๊ณ ์ ID ๋ฑ ์ธ์ฆ์ ํ์ํ ์ ๋ณด
- Verify Signatuer : Header, Paload์ Secret Key๊ฐ ๋ํด์ ธ ์ํธํ
์ ํฌ๋ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก HMAC SHA256์ ์ฌ์ฉํ์์ต๋๋ค.
์ฌ๊ธฐ์ HMAC์ ๋ฉ์์ง ์ธ์ฆ ์ฝ๋๋ฅผ ํด์ฌ ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ํธํ ํ๋ ๋ฐฉ์์ด๊ณ , SHA256์ ๊ธธ์ด๊ฐ 256bit ์ธ Message Disast๋ฅผ 64๋ฒ์ Round๋ฅผ๋๋ ค ์ผ๋ฐฉํฅ(๋ณตํธํ ๋ถ๊ฐ ๋ฐฉ์)์ผ๋ก ์ํธํ ํ๋ ๋ฐฉ์์ผ๋ก, ์์ ํ๋ค๊ณ ํ๋จ๋์ด ์ด ์ํธํ ๋ฐฉ์์ ์ ํํ๊ฒ ๋์์ต๋๋ค.
๋ํ, ์๋ฒ์ ๋ถ๋ด์ด ๊ฐ์ํ ๋ฟ๋ง ์๋๋ผ, ์ฌ๋ฌ ํ๋ซํผ ๋ฐ ๋๋ฉ์ธ์์ ์ ์ฉ๊ฐ๋ฅ ํ๋ค๋ ์ฅ์ ์ด ์์ต๋๋ค. ์ด๋ฌํ ์ด์ ๋ก, ๋ํ์ ์ธ ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ ์์คํ ์ธ JWT์ ์ฌ์ฉํ์์ต๋๋ค.
๐ ์์ธ์คํ ํฐ๊ณผ ๋ฆฌํ๋ ์ํ ํฐ ์ ๋ต
๋จผ์ Access Token์ Client๊ฐ Server์ ์ธ์ฆ์ ์ํด ์ฌ์ฉ๋๋ JSON Web Token์ ๋๋ค.
๋ณด์์์๋ 100% ์์ ์ ์๋ค๋ผ๋ ๋ง์ด ์์ต๋๋ค. JWT ์ญ์ 100% ๋ณด์์ ์์ ํ ์๋ ์์ต๋๋ค.
์ ์์ ์ธ ์ด์ฉ์๊ฐ Access Token์ ํ์ทจ ํ๋ค๋ฉด Session ๋ฐฉ์์์ Session ID๋ฅผ ํ์ทจํ ๊ฒ๊ณผ ๊ฐ์ด ํน์ ์ด์ฉ์์ ๊ถํ์ผ๋ก Server์ ์ ๊ทผํ ์ ์๋ค๋ ์ํ์ฑ์ด ์์ต๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ก ์ธํด ์ ํฌ๋ ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด Access Token ์ ํจ ์๊ฐ์ 5๋ถ์ผ๋ก ์ฃผ์์ต๋๋ค.
//์์ธ์ค ํ ํฐ ์ ํจ์๊ฐ 5๋ถ
public static Long ACCESS_TOKEN_VALID_TIME = 5 * 60 * 1000L;
public static String ACCESS_TOKEN_NAME = "ACCESS TOKEN";
public static String REFRESH_TOKEN_NAME = "REFRESH TOKEN";
public JwtUtil(String secret) {
key = Keys.hmacShaKeyFor(secret.getBytes());
}
์ด๋ ๊ฒ Access Token์ ์๊ฐ์ 5๋ถ์ผ๋ก๋ง ์ฃผ์๋ค๋ฉด ์ ๋ํ ์ด์ฉ์๋ค์ 5๋ถ์ด ์ง๋๋ฉด ๋ค์ Login์ ํด์ผ ํ๋ ๋ถํธํจ์ด ์๊ธฐ๊ฒ ๋ฉ๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด์ ์ ํฌ๋ Refrash Token ๊ธฐ๋ฒ์ ์ถ๊ฐ๋ก ์ฌ์ฉํ์์ต๋๋ค.
//๋ฆฌํ๋ ์ ํ ํฐ ์ ํจ์๊ฐ 24์๊ฐ(ms๋จ์)
public static Long REFRESH_TOKEN_VALID_TIME = 1440 * 60 * 1000L;
Refrash Token์ ๊ฒฝ์ฐ ์ ํจ ๊ธฐ๊ฐ์ 24์๊ฐ์ผ๋ก ์ฃผ์ด ์ ๋ํ ์ด์ฉ์๊ฐ ์ง์์ ์ธ Login์ ํตํด ๋ถํธํจ์ด ์๊ธฐ์ง ์๋๋ก ๊ตฌํ์ ํ์์ต๋๋ค. ๋ํ, Refrash Token ์ญ์ ์ ์์ ์ธ ์ด์ฉ์์๊ฒ ํ์ทจ ๋นํ ์ ๋ณด์ ์ฌ๊ณ ๋ก ์ด์ด์ง ์ ์๊ธฐ ๋๋ฌธ์ ์ด ์ ๋ณด๋ฅผ ์ธ๋ถ์ ๋ ธ์ถ ์ํค์ง ์๊ธฐ ์ํด Data Base์ ์ ์ฅํ๋๋ก ์ค๊ณ ํ์์ต๋๋ค.
@Transactional
public DefaultRes<UserSignInResponseDto> signIn(UserSignInRequestDto requestDto){
Optional<String> optionalEmail = userRepository.findByEmail(requestDto.getEmail());
return optionalEmail.map(email ->{
Optional<User> optionalUser = userRepository.findByUser(email, requestDto.getPassword());
return optionalUser.map(user -> {
String accessToken = JwtUtil.createAccessToken(user.getId(), user.getGrade());
String refreshToken = JwtUtil.createRefreshToken(user.getId(), user.getGrade());
user.setRefreshToken(refreshToken); // Dirty Checking์ ์ด์ฉํ Data Base ๊ฐ ์ ์ฅ
return DefaultRes.response(HttpStatus.OK.value(),"์ฑ๊ณต", new UserSignInResponseDto(accessToken,refreshToken, user.getId(), user.getGrade()));
}).orElseGet(()-> DefaultRes.response(HttpStatus.OK.value(), "๋น๋ฐ๋ฒํธ๋ถ์ผ์น"));
}).orElseGet(()->DefaultRes.response(HttpStatus.OK.value(), "์์ด๋๋ถ์ผ์น"));
}
๐ ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ์ ํต์
๐ ๊ถํ ์ฒ๋ฆฌ
ํด๋ผ์ด์ธํธ ์์ฒญ์ ๋ํ ๊ถํ ์ฒ๋ฆฌ๋ ์คํ๋ง ์ธํฐ์ ํฐ๋ฅผ ํ์ฉํ์ฌ ๊ตฌํํ์์ต๋๋ค.
Client๊ฐ Request๋ฅผ ๋ณด๋ด๊ฒ ๋๋ฉด Dispatcher Servlet์์ Request์ ํด๋นํ๋ Controller๋ก ๊ฐ๊ธฐ ์ ์ ํด๋น ๋ด์ฉ์ Interceptor๊ฐ ๊ฐ๋ก์ฑ๊ฒ ํฉ๋๋ค.
์ด๋ฅผ ํตํด ์ ํฌ๊ฐ ๊ตฌํํ User Grade (Admin, End User, Black User)๋ฅผ ํ์ธํ๊ณ , ํด๋น Grade์ ๋ง๋ ํ๋๋ง์ ํ ์ ์๊ฒ ๊ตฌํ์ ํ์ต๋๋ค.
}else if(tokenName.equals(JwtUtil.REFRESH_TOKEN_NAME)){
Long userPk = claims.get("user_pk", Long.class);
Boolean check = sessionService.refreshTokenCheck(userPk, jwtToken);
if(check){
String accessToken;
if(userGrade.equals(UserGrade.ADMIN)){
accessToken = JwtUtil.createAccessToken(userPk, UserGrade.ADMIN);
}else{
accessToken = JwtUtil.createAccessToken(userPk, UserGrade.USER);
}
์์ ๊ฐ์ด ๋จผ์ ์ ํฌ๋ Loginํ ์ด์ฉ์๊ฐ ์ ์์ ์ธ Token์ ๊ฐ์ง๊ณ ์๋์ง๋ฅผ ํ์ธํ ๋ค ๊ทธ ์ด์ฉ์ ํ์์ ๋ณด์ Grade ๊ฐ์ ํ๋จํ๊ฒ ๋ง๋ค์์ต๋๋ค.
์ฆ, ์ ํฌ๋ Interceptor๋ฅผ ํตํด ์ด์ฉ์์ Token Value๋ฅผ ํ๋จํ๊ณ , User Grade์ ํ๋จํ์ฌ ๊ฑฐ๊ธฐ์ ๋ง๋ ๊ถํ์ ๊ฐ์ง๊ณ , ์ ํฌ๊ฐ ๊ตฌํํ Service๋ฅผ ์ด์ฉํ ์ ์๋๋ก ๊ตฌํํ์์ต๋๋ค.
'Back-End ์์ ์ค > ๊ธฐํ ์ฐธ๊ณ ์๋ฃ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
GraphQL๊ณผ Apollo ๋ค๋ฒ์งธ ์ด์ผ๊ธฐ - Apollo๋? (0) | 2023.11.17 |
---|---|
GraphQL๊ณผ Apollo ์ธ๋ฒ์งธ ์ด์ผ๊ธฐ - GraphQL์ ์ ๋ณด ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ์ (0) | 2023.11.16 |
GraphQL๊ณผ Apollo ๋๋ฒ์งธ ์ด์ผ๊ธฐ - REST API๋? (0) | 2023.11.16 |
GraphQL๊ณผ Apollo ์ฒซ๋ฒ์งธ ์ด์ผ๊ธฐ - ๊ฐ๋ ์ตํ๊ธฐ (0) | 2023.11.16 |
Swagger๋? (0) | 2022.04.09 |