๐ ํ์ฌ ๋ฌธ์ ์
ํ์ฌ ๊ด๋ฆฌ์ ์น ํ๋ฉด์ ์ธ์ฆ/๋ก๊ทธ์ธ ๋ฐฉ์์ด ์๋ชป ์ค๊ณ๋์ด ์์ต๋๋ค.
ํ์ฌ ๋์ ํ๋ฆ
- ๋ก๊ทธ์ธ ์ฑ๊ณต ํ JWT
accessToken์ ๋ธ๋ผ์ฐ์ localStorage์ ์ ์ฅํฉ๋๋ค.
- ๊ด๋ฆฌ์ ํ์ด์ง๋ก ์ด๋ํ ๋๋ง๋ค URL ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ๋ก
?accessToken=xxx๋ฅผ ์ง์ ๋ถ์ฌ์ ์ ๋ฌํฉ๋๋ค.
- ์๋ฒ๋ ๊ฐ
/admin/** ๋งคํ์์ ์ด ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ ํ ํฐ์ jwtUtil.validateToken()์ผ๋ก ๊ฒ์ฆํฉ๋๋ค.
๊ด๋ จ ํ์ผ
SM-Web/src/main/java/com/balsamic/sejongmalsami/web/controller/view/AdminPageController.java
/admin/question, /admin/document, /admin/dashboard, /admin/member, /admin/subject, /admin/notice, /admin/notification ๋ฑ ๊ฑฐ์ ๋ชจ๋ /admin/** GET ๋งคํ์ด @RequestParam String accessToken์ ๋ฐ์ jwtUtil.validateToken(accessToken)์ผ๋ก ๊ฒ์ฆํฉ๋๋ค.
- ์ผ๋ถ ๋งคํ์
log.info(accessToken) ํํ๋ก ํ ํฐ์ ๋ก๊ทธ์ ๊ทธ๋๋ก ์ถ๋ ฅํ๊ธฐ๊น์ง ํฉ๋๋ค.
SM-Web/src/main/resources/static/js/auth.js
navigate()๊ฐ localStorage์ ํ ํฐ์ ์ฝ์ด ์ด๋ URL ๋์ ?accessToken=...๋ฅผ ๋ถ์
๋๋ค.
- ํ์ด์ง ์ง์
์
checkAccessToken()์ผ๋ก ํด๋ผ์ด์ธํธ ์ธก์์๋ง ํ ํฐ ์กด์ฌ ์ฌ๋ถ๋ฅผ ๊ฒ์ฌํฉ๋๋ค.
SM-Web/src/main/resources/templates/login.html
์ ์๋ชป๋์๋๊ฐ
- ๋ณด์ ์ทจ์ฝ โ
accessToken์ด URL ์ฟผ๋ฆฌ์คํธ๋ง์ ๋
ธ์ถ๋ฉ๋๋ค. ๋ธ๋ผ์ฐ์ ํ์คํ ๋ฆฌ, ์๋ฒ access log, Referer ํค๋์ ํ ํฐ์ด ํ๋ฌธ์ผ๋ก ๋จ์ ํ ํฐ์ด ์ ์ถ๋ ์ํ์ด ํฝ๋๋ค. (์ค์ ๋ก ์ปจํธ๋กค๋ฌ ์ผ๋ถ๋ ํ ํฐ์ ๋ก๊ทธ๋ก ๋จ๊ธฐ๊ณ ์์ต๋๋ค.)
- UX ๋ถ์์ โ ํ์ด์ง๋ฅผ ์ด๋ํ ๋๋ง๋ค ํ ํฐ์ URL์ ์๋์ผ๋ก ๋ถ์ฌ์ผ ํ๋ฏ๋ก ๋ค๋น๊ฒ์ด์
์ด ์ฝ๊ฒ ๊นจ์ง๊ณ ์ฌ์ฉ ๊ฒฝํ์ด ๋์ฉ๋๋ค.
- ํ์ค์์ ๋ฒ์ด๋จ โ ์ผ๋ฐ์ ์ธ ๊ด๋ฆฌ์ ์ธ์ฆ(Spring Security ์ธ์
๊ธฐ๋ฐ ํผ๋ก๊ทธ์ธ + ์ฟ ํค, ๋๋ HttpOnly ์ฟ ํค ๊ธฐ๋ฐ JWT)๊ณผ ๋๋จ์ด์ง ๋นํ์ค ๋ฐฉ์์
๋๋ค.
๐ ๏ธ ํด๊ฒฐ ๋ฐฉ์ / ์ ์ ๊ธฐ๋ฅ
- URL ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ๋ก ํ ํฐ์ ์ ๋ฌํ๋ ๋ฐฉ์์ ํ๊ธฐํฉ๋๋ค.
- ๋ค์ ์ค ํ๋์ ํ์ค ๋ฐฉ์์ผ๋ก ์ ํํฉ๋๋ค.
- (๊ถ์ฅ) Spring Security ์ธ์
๊ธฐ๋ฐ ํผ๋ก๊ทธ์ธ โ
/admin/** ๊ฒฝ๋ก ์ ์ฉ SecurityFilterChain์ ๋๊ณ ์ธ์
+ ์ฟ ํค(JSESSIONID)๋ก ์ธ์ฆ์ ๊ด๋ฆฌํฉ๋๋ค.
- ๋๋ HttpOnly ์ฟ ํค ๊ธฐ๋ฐ JWT โ ํ ํฐ์ JS์์ ์ ๊ทผ ๋ถ๊ฐ๋ฅํ HttpOnly ์ฟ ํค์ ๋ด์ ์ ๋ฌํฉ๋๋ค.
- ์ ํ ํ์๋ ์ปจํธ๋กค๋ฌ์์
@RequestParam String accessToken ์์ ยท๊ฒ์ฆ ๋ก์ง๊ณผ auth.js์ URL ํ ํฐ ๋ถ์ฐฉ ๋ก์ง์ ์ ๊ฑฐํฉ๋๋ค.
์ฐธ๊ณ ๋ ํผ๋ฐ์ค
- ๊ฐ์ ์์ฑ์์ passQL ํ๋ก์ ํธ
AdminSecurityConfig๊ฐ ์ด๋ฏธ /admin/** ๊ฒฝ๋ก์ ๋ํด Spring Security ์ธ์
ํผ๋ก๊ทธ์ธ์ ์ ์ฉํ๊ณ ์์ต๋๋ค.
/admin/**์๋ง ์ ์ฉ๋๋ ๋ณ๋ SecurityFilterChain(@Order(1))์ ๋๊ณ , JWT API chain๊ณผ ์์ ํ ๋ถ๋ฆฌํฉ๋๋ค.
formLogin(loginPage/loginProcessingUrl/defaultSuccessUrl), logout(deleteCookies("JSESSIONID")), SessionCreationPolicy.IF_REQUIRED, userDetailsService + BCryptPasswordEncoder ๊ตฌ์ฑ์ ๊ทธ๋๋ก ์ฐธ๊ณ ํ ์ ์์ต๋๋ค.
โ๏ธ ์์
๋ด์ฉ
/admin/** ์ ์ฉ Security ์ค์ (์ธ์
ํผ๋ก๊ทธ์ธ ๋๋ HttpOnly ์ฟ ํค JWT) ๋์
AdminPageController์ ๋ชจ๋ /admin/** ๋งคํ์์ accessToken ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ ์์ ยท๊ฒ์ฆ ๋ก์ง ์ ๊ฑฐ (ํ ํฐ ๋ก๊ทธ ์ถ๋ ฅ ์ ๊ฑฐ ํฌํจ)
auth.js์ URL ํ ํฐ ๋ถ์ฐฉยทํด๋ผ์ด์ธํธ ํ ํฐ ์ฒดํฌ ๋ก์ง ์ ๋ฆฌ
- ๋ก๊ทธ์ธ/๋ก๊ทธ์์ ํ๋ฆ(
login.html, /logout) ํ์ค ๋ฐฉ์์ ๋ง์ถฐ ์ฌ์ ๋น
โ ๏ธ ๋ณธ ์ด์๋ "๊ด๋ฆฌ์ ํ๋ฉด UI๋ฅผ daisyUI๋ก ์ ๋ฉด ๊ต์ฒดํ๋ ์์
"๊ณผ๋ ๋ณ๊ฐ์ ํ์ ๊ฐ์ ๊ณผ์ ์
๋๋ค. UI ๊ต์ฒด ์์
์์๋ ์ธ์ฆ์ ์ผ๋จ ๊ธฐ์กด ๋ฐฉ์ ๊ทธ๋๋ก ๋๊ณ , ๊ทธ ์ดํ ๋ณธ ์ด์์์ ์ธ์ฆ์ ํ์ค ๋ฐฉ์์ผ๋ก ์ ๋๋ก ๊ณ ์น๊ธฐ ์ํ ํธ๋ํน์ฉ ์ด์์
๋๋ค.
๐โโ๏ธ ๋ด๋น์
- ๋ฐฑ์๋: ์ด๋ฆ
- ํ๋ก ํธ์๋: ์ด๋ฆ
- ๋์์ธ: ์ด๋ฆ
๐ ํ์ฌ ๋ฌธ์ ์
ํ์ฌ ๊ด๋ฆฌ์ ์น ํ๋ฉด์ ์ธ์ฆ/๋ก๊ทธ์ธ ๋ฐฉ์์ด ์๋ชป ์ค๊ณ๋์ด ์์ต๋๋ค.
ํ์ฌ ๋์ ํ๋ฆ
accessToken์ ๋ธ๋ผ์ฐ์ localStorage์ ์ ์ฅํฉ๋๋ค.?accessToken=xxx๋ฅผ ์ง์ ๋ถ์ฌ์ ์ ๋ฌํฉ๋๋ค./admin/**๋งคํ์์ ์ด ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ ํ ํฐ์jwtUtil.validateToken()์ผ๋ก ๊ฒ์ฆํฉ๋๋ค.๊ด๋ จ ํ์ผ
SM-Web/src/main/java/com/balsamic/sejongmalsami/web/controller/view/AdminPageController.java/admin/question,/admin/document,/admin/dashboard,/admin/member,/admin/subject,/admin/notice,/admin/notification๋ฑ ๊ฑฐ์ ๋ชจ๋/admin/**GET ๋งคํ์ด@RequestParam String accessToken์ ๋ฐ์jwtUtil.validateToken(accessToken)์ผ๋ก ๊ฒ์ฆํฉ๋๋ค.log.info(accessToken)ํํ๋ก ํ ํฐ์ ๋ก๊ทธ์ ๊ทธ๋๋ก ์ถ๋ ฅํ๊ธฐ๊น์ง ํฉ๋๋ค.SM-Web/src/main/resources/static/js/auth.jsnavigate()๊ฐlocalStorage์ ํ ํฐ์ ์ฝ์ด ์ด๋ URL ๋์?accessToken=...๋ฅผ ๋ถ์ ๋๋ค.checkAccessToken()์ผ๋ก ํด๋ผ์ด์ธํธ ์ธก์์๋ง ํ ํฐ ์กด์ฌ ์ฌ๋ถ๋ฅผ ๊ฒ์ฌํฉ๋๋ค.SM-Web/src/main/resources/templates/login.html์ ์๋ชป๋์๋๊ฐ
accessToken์ด URL ์ฟผ๋ฆฌ์คํธ๋ง์ ๋ ธ์ถ๋ฉ๋๋ค. ๋ธ๋ผ์ฐ์ ํ์คํ ๋ฆฌ, ์๋ฒ access log,Refererํค๋์ ํ ํฐ์ด ํ๋ฌธ์ผ๋ก ๋จ์ ํ ํฐ์ด ์ ์ถ๋ ์ํ์ด ํฝ๋๋ค. (์ค์ ๋ก ์ปจํธ๋กค๋ฌ ์ผ๋ถ๋ ํ ํฐ์ ๋ก๊ทธ๋ก ๋จ๊ธฐ๊ณ ์์ต๋๋ค.)๐ ๏ธ ํด๊ฒฐ ๋ฐฉ์ / ์ ์ ๊ธฐ๋ฅ
/admin/**๊ฒฝ๋ก ์ ์ฉ SecurityFilterChain์ ๋๊ณ ์ธ์ + ์ฟ ํค(JSESSIONID)๋ก ์ธ์ฆ์ ๊ด๋ฆฌํฉ๋๋ค.@RequestParam String accessToken์์ ยท๊ฒ์ฆ ๋ก์ง๊ณผauth.js์ URL ํ ํฐ ๋ถ์ฐฉ ๋ก์ง์ ์ ๊ฑฐํฉ๋๋ค.์ฐธ๊ณ ๋ ํผ๋ฐ์ค
AdminSecurityConfig๊ฐ ์ด๋ฏธ/admin/**๊ฒฝ๋ก์ ๋ํด Spring Security ์ธ์ ํผ๋ก๊ทธ์ธ์ ์ ์ฉํ๊ณ ์์ต๋๋ค./admin/**์๋ง ์ ์ฉ๋๋ ๋ณ๋SecurityFilterChain(@Order(1))์ ๋๊ณ , JWT API chain๊ณผ ์์ ํ ๋ถ๋ฆฌํฉ๋๋ค.formLogin(loginPage/loginProcessingUrl/defaultSuccessUrl),logout(deleteCookies("JSESSIONID")),SessionCreationPolicy.IF_REQUIRED,userDetailsService+BCryptPasswordEncoder๊ตฌ์ฑ์ ๊ทธ๋๋ก ์ฐธ๊ณ ํ ์ ์์ต๋๋ค.โ๏ธ ์์ ๋ด์ฉ
/admin/**์ ์ฉ Security ์ค์ (์ธ์ ํผ๋ก๊ทธ์ธ ๋๋ HttpOnly ์ฟ ํค JWT) ๋์AdminPageController์ ๋ชจ๋/admin/**๋งคํ์์accessToken์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ ์์ ยท๊ฒ์ฆ ๋ก์ง ์ ๊ฑฐ (ํ ํฐ ๋ก๊ทธ ์ถ๋ ฅ ์ ๊ฑฐ ํฌํจ)auth.js์ URL ํ ํฐ ๋ถ์ฐฉยทํด๋ผ์ด์ธํธ ํ ํฐ ์ฒดํฌ ๋ก์ง ์ ๋ฆฌlogin.html,/logout) ํ์ค ๋ฐฉ์์ ๋ง์ถฐ ์ฌ์ ๋น๐โโ๏ธ ๋ด๋น์