2021. 12. 21. 13:56ใProgramming Project ์์ ์ค/๋ด์ฉ ์ ๋ฆฌ
์ฐ๋ฆฌ ๊ฐ์กฑ์ ์ปค๋ฎค๋ํฐ ์น ์๋น์ค๋ฅผ ๋ง๋ค๊ธฐ ์ํด ๋จผ์ Server ์์ ์ ํ๊ณ ์๋ ๊ฒ์ด์์.
์ฝ๋ฉ์ ํ๋ฉด์ ๋ด์ฉ์ ์ ๋ฆฌํด์ ๋์ค์ ์ฃผ๋ ํ๋๋ ํ์ธํ๊ณ , ์ฌ๊ธฐ ์ค์ ์ฌ๋ฌ๋ถ๋ค๊ณผ๋ ๊ณต์ ํ๊ณ ์ถ์ ๊ฒ์ด์์!
๊ทธ๋ผ ์์ํด ๋ณด๊ฒ ์ต๋๋ค!
ํด๋น ํ๋ก์ ํธ์ ๋ํ Source Code๋ '์ฃผ๋ํ๋ Git Hub'์์ ํ์ธํ์ค ์ ์์ต๋๋ค.
๐ ๋ชฉ์ฐจ(INDEX)
1. ์ด๊ธฐ๊ตฌ์ฑ
2. Spring Security ์ด๊ธฐ ๊ตฌ์ฑ
3. Interceptor ์ค์
. 4. ์ด๊ธฐ Domain ์ค์
๐ Package Tree
๐ CORS Filter
๋จผ์ ์ด ์ฝ๋๋ CORS Filter๋ฅผ ๊ตฌํํ ๋ค Bean์ผ๋ก ๋ฑ๋กํ๊ธฐ ์ํด ์์ฑํ ๊ฒ์ด์์. ์ด ๋ฐฉ๋ฒ์ Spring MVC๊ฐ ์๋ Srping WEB์๋ง ์์กดํ๊ฑฐ๋, Security ์ค์ Level์์ CORS๋ฅผ ํ์์ ์ผ๋ก ํ์ธํ ๋ ์ ์ฉํ๊ธฐ์ ์ฌ์ฉํ ๊ฒ์ด์์.
13๋ฒ์งธ ์ค์ ๋ํด ์ด์ผ๊ธฐ ํ์๋ฉด @Order๋ Spring Bean์ ์์๋ฅผ ์ ํ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด์์.
Default Option์ Ordered.LOWEST_PRECEDENCE์ด๊ณ , collection์ componenet๋ฅผ Spring 4.0์์๋ถํฐ ์ฃผ์
ํ ์ ์๊ฒ ๋๋ฉด์ ์ด ์ด๋
ธํ
์ด์
์ ํตํด ์์๋ฅผ ์ ํ๋๋ฐ, ์ฌ์ฉํ๊ฒ ๋ ๊ฒ์ด์์.
@Order์๋ ์์๊ฐ์ ํตํด ์์๋ฅผ ์ ํ ์ ์๊ณ , ์ฃผ๋ํ๋์ด ์ค Ordered.HIGHEST_PRECEDENCE์ ์ค์ ์์๊ฐ์ -2147483648์ผ๋ก ์ ์ผ ๋จผ์ ์คํํ๊ฒ ํ๊ฒ ๋ค๋ ์๋ฏธ์ธ ๊ฒ์ด์์.
@Order์ ๋ํด์๋ ์ด ๊ณณ์ ์์ธํ ์ค๋ช
์ ๋ฌ์ ๋๋๋ก ํ๊ฒ ์ต๋๋ค.
doFilterInternal()๋ฅผ override(์ฌ๊ตฌ์ฑ)ํ๊ณ , ๋งค๊ฐ๋ณ์ ์ค ํ๋์ธ response ์์ SetHeader์ ๋ํด ์ค์ ์ ํ๋ ๊ฒ์ด์์. ์ด๋ ๊ฒ ํ ์ด์ ๋ ๋ฐ๋ก Filter๋ฅผ ๋ฑ๋กํ์ฌ CORS์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๊ฒ ํ๊ธฐ ์ํจ์
๋๋ค.
์์ ์๋ฆฌ๋ ์ค์ ์์ฒญํ๊ธฐ ์ ์ ์๋น์์ฒญ(Preflight Request)๋ฅผ ํตํด ํ์ฉ ๊ฐ๋ฅํ Domain, Method ๋ฑ์ ์๋ตํ๊ณ , ์ด๋ฅผ ํตํด ํต์ ํ ์ ์๋๋ก ํ๋ ๊ฒ์ด์์.
19๋ฒ์งธ ์ค์ Request์ Cookie, authorization Header๋ค ๋๋ TLS Client ์ธ์ฆ์ ๋ฑ์ ๋ด์ ๋ณด๋ด๋ ๊ฒ์ ํ์ฉํ๋ค๋ ๊ฒ์ด์์. ์ด ๊ฒฝ์ฐ Access-Control-Allow-Origin, *๊ณผ ๊ฐ์ด ์ ๊ท์์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ด Origin๊ฐ์ ์ง์ ๋ช
์์ ์ผ๋ก ์ง์ ํ ๊ฒ์ด์์.
Request์ Orgin Header๋ฅผ ๋์ ์ผ๋ก ๋ฐ์ ์ค์ ํ๊ธฐ ๋๋ฌธ์ '*' ๊ณผ ๊ฐ์ ์ค์ ์ด๋๋๋ค.
์ด๋ ๊ฒ ๊ตฌํํ๋ฉด ์ฐจํ ajax๋ฅผ ์๋ ์ฒ๋ผ ์ฃผ๋ฉด ๋๋ ๊ฒ์ด์์.
$.ajax({
url: 'http://hongga-community.com/hello',
data: {
param_1 : '111'
param_2 : '222'
},
type: 'GET'
dataType : 'json',
xhrFields: {
withCredentials: true
},
success: function(data) {
console.log(data);
},
error : function(e) {
console.log(e);
}
});
์์ xhrFilds์ withCredentials true๋ XMLHttpRequest๋ฅผ ์์ฑํ ๋, Cookie, authorization Header๋ค TLS ์ธ์ฆ์๋ฅผ ํฌํจํ๊ฒ ๋๋ต๋๋ค.
๐ JwtUtil
๐ฝ Field Variable
๋จผ์ ์ฃผ๋ํ๋์ JAVA์์ ์ํธํ ๊ด๋ จ Key๋ฅผ ์ ๊ณตํ๋ java.security.Key๋ฅผ static ๋ณ์๋ก ๋ง๋ค์ด ์ค ๊ฒ์ด์์.
๊ทธ ๋ค์์๋ jwt์ ๋ํ Log๋ฅผ ๋จ๊ธฐ๊ธฐ ์ํด slf4j๋ฅผ ์ฌ์ฉํ ๋ถ๋ถ์ธ ๊ฒ์ด์์.
Refresh Token์ ์ ํจ๊ธฐ๊ฐ์ 2์ฃผ๋ฅผ ์ค์ ์ด์ฉ์๋ค์ ํธ์์ ๋ณด์์ ์ ๊ฒฝ ์ด๊ฒ์ด์์.
Access Token์ 1์๊ฐ์ ์ฃผ์๋ต๋๋ค.
๊ทธ ๋ค์ 30๋ฒ์งธ ์ค์ Method Body๋ ์ด ๋ด์ฉ์ ์ํด ์ค ๊ฒ์ด์์.
๊ทธ ์ ์ Hash Function์ ์ ๋ชจ๋ฅด์ ๋ค๋ฉด ์ด ๊ณณ์ ์ฐธ๊ณ ํ์๊ธฐ ๋ฐ๋ผ๋ ๊ฒ์ด์์.
HMAC-SHA ์๊ณ ๋ฆฌ์ฆ๊ณผ ๋ฌธ์์ด secret key ๋๋ ์ธ์ฝ๋ฉ๋ byte array๋ฅผ ์ฌ์ฉํด์ JWT์ ์๋ช ํ๋ ๊ฒฝ์ฐ signWith ๋ฉ์๋ ์ธ์๋ก ์ฌ์ฉํ SecretKey ์ธ์คํด์ค๋ก ๋ณํํด์ผ ํ๋ ๊ฒ์ด์์.
- ์ธ์ฝ๋ฉ๋ byte array:
SecretKey key = Keys.hmacShaKeyFor(encodedKeyBytes);
- Base64 ์ธ์ฝ๋ญ๋ ๋ฌธ์์ด
SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));
- Base64URL ์ธ์ฝ๋ฉ๋ ๋ฌธ์์ด
SecretKey key = keys.hmacShakeyFor(Decoders.BASE64URL.decode(scretString));
- ์ธ์ฝ๋ฉ ์๋ ๋ฌธ์์ด(์ password ๋ฌธ์์ด)
SecretKey key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
secretString.getBytes()์์๋ ํญ์ ์บ๋ฆญํฐ์ ์ ์ง์ ํด์ผ ํ๋ ๊ฒ์ด์์.
๊ทธ๋ฆฌ๊ณ ์๋ณํ ์ ์๋ ๋ฌธ์์ด pasword๋ ๊ฐ๋ฅํ ํผํด์ผ ํ๋ ๊ฒ์ด์์. ๊ฐ๋ฅํ๋ฉด ์์ ํ ๋๋คํค๋ฅผ ์์ฑํด์ ์ฌ์ฉํ๋๊ฒ์ด ์ข๋ค๊ณ ํฉ๋๋ค.
๐ฝ Method(createAccessToken / createRefreshToken / getClaim)
์ด ์น๊ตฌ๋ ๋งค๊ฐ ๋ณ์(ํ๋ผ๋ฏธํฐ)๋ก ์์ผ๋ก ์์ฑํ๊ฒ ๋ Member Entity์ @Id๊ฐ, DB๋ก ๋งํ์๋ฉด PK๊ฐ์ด ๋ memberId์ member์ ๋ฑ๊ธ์ ํ์ธํ๊ฒ ๋ memberGrade๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌํ๊ฒ ๋ฉ๋๋ค.
์ด๋ ๊ฒ ํ ์ด์ ๋ JWT๋ฅผ ์ฌ์ฉํ๋ ๊ถ๊ทน์ ์ธ ์ด์ ๋ ์ด์ฉ์์ ๊ถํ์ ํ์ธํด์ ์ฐ๋ฆฌ๊ฐ ์๋น์ค ํ๊ณ ์ ํ๋ ์๋น์ค๋ฅผ ์ด์ฉํ๊ธฐ์ ์ ํฉํ์ง๋ฅผ ํ์ธํ๋ ๊ฒ๋ ์๊ธฐ ๋๋ฌธ์ ํ์ ๋ฑ๊ธ ๊ฐ์ ๋ฐ๊ณ ์๋ ๊ฒ์ด์์.
JWT์ ๋ง๋ฃ ๋ฐ ๋ฐ๊ธ ์๊ฐ ์ค์ ๋ฑ์ ์ํด Date()๋ฅผ ํ๋ ์์ฑ์ ํด์ค ๋ค Jwts์ Builder Pattern์ ํตํด ์ค์ ์ ํ๋ ๊ฒ์ด์์.
์ผ๋จ ๊ฐ๋ตํ๊ฒ JWT๋ ๋ฌด์์ธ์ง ์์๋ณผ๊น ํด์!
'Json Web Token'์ ์ฝ์ ์ด๋ฉฐ, 'Json format'์ ์ด์ฉ ์น์์ ์ฌ์ฉํ ์ ์๋ ์์ธ์ค ํ ํฐ์ ๋ค๋ฃจ๋ ํ์ค์ธ ๊ฒ์ด์์. JWT๋ฅผ ์ด์ฉ ์ ์ ํ ์๋ฏธ๋ฅผ ๊ฐ์ง ํ ํฐ์ ๋ง๋ค ์ ์๋ ๊ฒ์ด์์.
JWT๋ ํฌ๊ฒ 3๊ฐ์ง ๋ถ๋ถ์ผ๋ก ๋๋์ด์ ธ ์๋ ๊ฒ์ด์์.
Part | Description |
Header | - ์ด๋ค ํ์
์ ๋ฐ์ดํฐ์ธ์ง. - ์ด๋ค ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ ์ง. |
Payload(Claims) | - ์ค์ ์ด๋ค ๋ฐ์ดํฐ๊ฐ ๋ด๊ฒจ ์๋์ง(์ํธํ๊ฐ ์ ๋์ด ์์ด ์น์ผ๋ก ๋
ธ์ถํ๋ฉด ์๋๋ ๋ฐ์ดํฐ๋ฅผ ๋ด์์๋ ์๋จ.) - ๊ฐ๋ฅํ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ง ์๋ ๊ฒ์ด ์ข๋ค. - Production์ผ๋ก ์ฌ์ฉ ํ ์์๋ ํ ํฐ์ด ์ธ์ ๊น์ง ์ ํจํ์ง ๋ด๋ ๊ฒ์ด ์ข์. |
Signature | - ๋ฐ์ดํฐ์ ํ ํฐ์ด ์/๋ณ์กฐ ๋์ง ์์์ ์ฆ๋ช
. - ๋ฐ์ดํฐ๋ฅผ ์ผ์ ํ๊ฒ ํด์ฑํ๊ณ ํด์ฑํ ๋ฐ์ดํฐ๋ฅผ ์ํธํ ํด์ ๋ฐ์ดํฐ๊ฐ ์/๋ณ์กฐ ๋์ง ์์ ์ฆ๋ช . - HMAC-SHAXXX ์ฌ์ฉ. (ํด๋น ์๊ณ ๋ฆฌ์ฆ ์ฌ์ฉ ์ํด ๋น๋ฐํค ์์ฑ์ ํ๋๋ฐ, ๋น๋ฐํค๋ ๋ฐ๋์ ๋ ธ์ถ์ด ๋์ง ์๋๋ก ์กฐ์ฌ!) |
์์ Json ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ฅ ๋ณด๋ด๋ ๊ฒ์ด ์๋๋ผ Base64 URL Encoding ํด์ค์ผ ํ๋ ๊ฒ์ด์์.
Cliaims ์ฆ, JWT์ Payload์ ํ์ Table์ PK๊ฐ์ด ๋ memberId์ ํ์ ๋ฑ๊ธ์ธ memberGrade๊ฐ์ ๋ฃ์ด์ฃผ๊ณ , Access Token ์ด๋ฆ๊ฐ์ ๊ฐ์ด ๋ฃ์ด ์ฃผ๋ ๊ฒ์ด์์.
43๋ฒ์งธ ์ค์๋ JWT ๋ฐ๊ธ ์๊ฐ์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ธฐ ์ํด ํ์ฌ Server์ ์๊ฐ์ ๊ฐ์ ธ์ฌ ์ธ์คํด์ค๋ฅผ ๋ฃ์ด์ค ๊ฒ์ด์์.
45๋ฒ์งธ ์ค์๋ JWT ๋ง๋ฃ ์๊ฐ์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ธฐ ์ํด ํ์ฌ Server ์๊ฐ๊ณผ ๋ฐ๊ธ ์๊ฐ์ ๋ฃ์ด์ฃผ๊ณ , ACCESS_VALID_TIME ๊ฐ(60๋ถ)์ ๋ฃ์ด์ now.getTime() ์ฆ, ๋ฐ๊ธ ์๊ฐ์ ์๊ฐ์ ๊ฐ์ ธ์์ ACCESS_VALID_TIME, 60๋ถ์ ๋ํด์ ์๋ก์ด Date ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ๊ฐ์ ๋ง๋ฃ ์๊ฐ์ผ๋ก ์ค์ ํ ๊ฒ์ด์์.
46๋ฒ์งธ ์ค์ ๋ณตํธํํ ๋ ์ฌ์ฉํ๋ Signature๋ฅผ ์ค์ ํ ๊ฒ์ด์์. Signature๋ Header์ ์ธ์ฝ๋ฉ ๊ฐ๊ณผ Payload์ ์ธ์ฝ๋ฉ ๊ฐ์ ํฉ์น ๋ค, ์๋ฒ๋ง์ด ์๊ณ ์๋ ๋น๋ฐํค๋ก ํด์ฌ๋ฅผ ํ์ฌ ์์ฑํ๋ ๊ฒ์ด์์.
signWith api๋ ํด์ฑํ ์๊ณ ๋ฆฌ์ฆ๊ณผ ๋น๋ฐํค๋ฅผ ํ์๋กํฉ๋๋ค. key๊ฐ ๋น๋ฐํค๋ฅผ ๋ฐํํ๋ ๊ฒ์ด๋๋๋ค!
Refresh Token ๋ฐ๊ธ๋ ์ Access Token๊ณผ ๋น์ทํ๊ฒ ์์ฑํ์ง๋ง, ๋ง๋ฃ ์๊ฐ์ 2์ฃผ๋ก ์ค์ ํ ๊ฒ์ด์์.
๊ทธ๋์ผ ์ด์ฉ์์ ํ๋ Login ์์
์ ์กฐ๊ธ์ ํธ๋ฆฌํ๊ฒ ๋ง๋ค์ด ์ค ์ ์๊ธฐ ๋๋ฌธ์ด์์.
๋ณด์์ ๊ฐํํ๋ฉด ํธ๋ฆฌ์ฑ์ด ๋จ์ด์ง๊ณ , ํธ๋ฆฌ์ฑ์ ์ฌ๋ฆฐ๋ค๋ฉด ๋ณด์์ด ๋จ์ด์ง๋ ๊ฒ์ด์์.
์ด ๋๋ ๋ง๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด ์ ํฌ ๊ฐ์กฑ ์ปค๋ฎค๋ํฐ์์๋ ์ด ์ ๋ ๊ฐ์ด ์ ์ ํ๊ฒ ๋ค๊ณ ํ๋จํ ๊ฒ์ด์์.
์ด๋ฒ Method๋ Token์ ๋ง๋ฃ ์ผ์์ ๋ณตํธํ๋ฅผ ํ์ธ ์ฆ! ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ธฐ ์ํ Method์ธ ๊ฒ์ด์์.
์์์ ์์ฑ๋ Token์ ์ด์ฉ์๋ ๋ฐ์์ Server์ ์ ๋ฌ์ ํ ๊ฒ์ด๊ณ , Server๋ ํด๋น Method๋ฅผ ํตํด ์ ํจ์ฑ์ ๊ฒ์ฌํ ๊ฒ์ด์์.
Exception์ด ํฐ์ง ์ ์์ผ๋ Try - catch๋ฌธ์ผ๋ก ๊ฐ์ธ๊ณ , JWT๋ String ์ฆ, ๋ฌธ์์ด๋ก ์ด๋ค์ ธ ์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ์ฌ์ฉํ๊ธฐ ์ํด Jwts.paser๋ฅผ ํตํด Parsing์ ํ๊ณ , Token์ ๋ง๋ค ๋, ์ฌ์ฉํ๋ key๋ฅผ ๊ฐ์ง๊ณ , setter๋ฅผ ํตํด ๊ฐ์ ๋ฃ์ด์ค์ผ ํ๋ ๊ฒ์ด์์. ๋ค์ด์จ Token์ SigningKey ์ฆ, Data์ Token ๋ฑ์ด ์ / ๋ณ์กฐ ๋์๋์ง๋ฅผ ํ์ธํ๊ณ , parseClaimsJws๋ฅผ ํตํด Token์ Jws๋ก Parsingํ๋ ๊ฒ์ด์์.
๋ง์ง๋ง์ผ๋ก getBody()๋ฅผ ์ด์ฉํด์ ์์์ Token์ ์ ์ฅํ๋ Data๋ค์ด ๋ด๊ธด Claims (Payload)๋ฅผ ์ป์ด์จ ๊ฒ์ ๋ฐํํ๋๋ก ํ ๊ฒ์ด์์.
๊ทธ๋ฐ๋ฐ? ๋ฌธ์ ๊ฐ ์๋ค๋ฉด? null์ ๋ฐํํ๋๋ก ํ์์ต๋๋ค!
์์ ๊ด๋ จ๋ JWT์ ๊ด๋ จํ ๋ด์ฉ์ ์ด ๊ณณ์ ์ ๋ฆฌ ํด ๋์์ด์! ์ฐธ๊ณ ๋ก ์ด ๊ธ์ ํ ํ๋ก์ ํธ๋ฅผ ํ ๋, ์ฃผ๋ํ๋์ด ์ ๋ฆฌํ ์๋ฃ ์ ๋๋ค.