๐ ํ์ฌ ๋ฌธ์ ์
- ์ธ์ข
๋ง์ธ๋ฏธ ์๋น์ค์ ๋ก๊ทธ์ธ์ ์ธ์ข
๋ํ๊ต๊ฐ ์ ๊ณตํ๋ ๊ณต์ OAuth/SSO API๊ฐ ์์ด, ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์ธ์ข
ํฌํธ ํ๋ฒยท๋น๋ฐ๋ฒํธ๋ก ์ง์ ์ธ์ข
ํฌํธ์ ๋ก๊ทธ์ธํ ๋ค ๊ฒฐ๊ณผ ํ์ด์ง(๊ณ ์ ๋
์์ธ์ฆํํฉ)๋ฅผ ํฌ๋กค๋ง(์คํฌ๋ํ) ํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋์ด ์๋ค.
- ์ด ๋ฐฉ์์ ์ธ๋ถ ์ฌ์ดํธ(์ธ์ข
ํฌํธ)์ HTML ๊ตฌ์กฐยท์๋ํฌ์ธํธยท์ธ์
์ ์ฑ
์ ๊ฐํ๊ฒ ๊ฒฐํฉ๋์ด ์์ด, ์ธ์ข
๋ ์ธก ๋ณ๊ฒฝ ์ ๋ก๊ทธ์ธ ์ ์ฒด๊ฐ ๊นจ์ง ์ ์๋ ๊ตฌ์กฐ๋ค.
- ๋ํ ์ธ์ฆ ์ ๋ขฐ์ฑ์ ์ํด SSL ์ธ์ฆ์ ๊ฒ์ฆ์ ์ ๋ฉด ๋นํ์ฑํํ๋ ๋ฑ ๋ณด์์ ์ง์ด์ผ ํ ์ง์ ์ด ์๋ค.
- ๋ณธ ์ด์๋ ๋ณ๋ ์์
(์ฝ๋ ๋ณ๊ฒฝ)์ ์ํ ๊ฒ์ด ์๋๋ผ, ํ์ฌ ์ธ์ฆ ๊ตฌํ์ด ์ด๋ป๊ฒ ๋์ํ๋์ง๋ฅผ ์ฝ๋ ๊ธฐ์ค์ผ๋ก ์ ํํ ๋ฌธ์ํํ๊ธฐ ์ํ ๋ถ์ยท๊ธฐ๋ก์ฉ ์ด์๋ค. (์์
X / ํํฉ ๋ถ์ ์ ์ฉ)
๐ ์ธ์ฆ ์ ์ฒด ํ๋ฆ ์์ฝ
[Client] ํ๋ฒ + ๋น๋ฐ๋ฒํธ ์
๋ ฅ
โ POST /api/auth/signin (์น) ๋๋ /api/auth/mobile/signin (๋ชจ๋ฐ์ผ)
โผ
AuthController
โ
โผ
AuthApplicationService.signIn() / signInForMobile()
โ
โผ
SejongPortalAuthenticator.getMemberAuthInfos()
โโ 1. portal.sejong.ac.kr/jsp/login/login_action.jsp โ id/password POST ๋ก๊ทธ์ธ
โโ 2. classic.sejong.ac.kr SSO ๋ฆฌ๋ค์ด๋ ํธ(sso-return.jsp) โ ์ธ์
์ฟ ํค ํ๋ณด
โโ 3. classic.sejong.ac.kr/classic/reading/status.do (๊ณ ์ ๋
์์ธ์ฆํํฉ) GET
โโ 4. Jsoup ์ผ๋ก "์ฌ์ฉ์ ์ ๋ณด" ํ
์ด๋ธ HTML ํ์ฑ โ ํ๊ณผ/ํ๋ฒ/์ด๋ฆ/ํ๋
/๋ฑ๋ก์ํ ์ถ์ถ
โ
โผ
[Member ์กฐํ or ์ ๊ท ๋ฑ๋ก] โ ์ฒซ ๋ก๊ทธ์ธ ์ Exp ์์ฑ + ์ฝ์ ๋ณด์ ์ง๊ธ + Faculty ๋งคํ
โ
โผ
[JWT ๋ฐ๊ธ] AccessToken(1h) + RefreshToken(7d)
โโ ์น : RefreshToken ์ HttpOnly ์ฟ ํค(Set-Cookie)๋ก ๋ด๋ ค์ค, AccessToken ์ ์๋ต ๋ฐ๋
โโ ๋ชจ๋ฐ์ผ: AccessToken + RefreshToken ๋ชจ๋ JSON ์๋ต ๋ฐ๋๋ก ๋ด๋ ค์ค
โ
โผ
์ดํ ๋ชจ๋ ์์ฒญ์ TokenAuthenticationFilter ๊ฐ Bearer AccessToken ๊ฒ์ฆ (STATELESS)
๐งฉ ํต์ฌ ์ปดํฌ๋ํธ / ํ์ผ ๊ฒฝ๋ก
| ๊ณ์ธต |
ํด๋์ค |
๊ฒฝ๋ก |
| Controller |
AuthController |
SM-Web/.../web/controller/api/AuthController.java |
| Application |
AuthApplicationService |
SM-Application/.../application/service/AuthApplicationService.java |
| Domain(์ธ์ฆํต์ฌ) |
SejongPortalAuthenticator |
SM-Domain-Auth/.../auth/service/SejongPortalAuthenticator.java |
| Domain(ํ ํฐ) |
JwtUtil |
SM-Domain-Auth/.../auth/util/JwtUtil.java |
| Domain(ํํฐ) |
TokenAuthenticationFilter |
SM-Domain-Auth/.../auth/filter/TokenAuthenticationFilter.java |
| Domain(์ ์ ๋ก๋) |
CustomUserDetailsService |
SM-Domain-Auth/.../auth/service/CustomUserDetailsService.java |
| Config |
WebSecurityConfig |
SM-Web/.../web/config/WebSecurityConfig.java |
| Common(ํ์ดํธ๋ฆฌ์คํธ) |
SecurityUrls |
SM-Common/.../dto/SecurityUrls.java |
| DTO |
AuthCommand / AuthDto |
SM-Domain-Auth/.../auth/dto/ |
๐ฐ๏ธ 1. ์ธ์ข
ํฌํธ ํฌ๋กค๋ง ์ธ์ฆ (SejongPortalAuthenticator)
- ์ง์
์ :
getMemberAuthInfos(AuthCommand) (L48)
sejongPortalId, sejongPortalPassword ๋ ๊ฐ์ ์
๋ ฅ์ผ๋ก ๋ฐ์ (AuthCommand L26~27).
- ํ
์คํธ ๊ณ์ ๋ถ๊ธฐ (L52~55): ํ๋ฒ์ด
"9"๋ก ์์ํ๋ฉด ์ธ์ข
ํฌํธ์ ๊ฑฐ์น์ง ์๊ณ getTestInfos()๋ก ์ฐํ โ test_member ํ
์ด๋ธ์์ ์กฐํ ํ BCrypt ๋น๋ฐ๋ฒํธ ๋งค์นญ(L92~112).
- ์ค์ ์ธ์ฆ ๋จ๊ณ:
- ํฌํธ ๋ก๊ทธ์ธ
doPortalLogin() (L117~143)
POST https://portal.sejong.ac.kr/jsp/login/login_action.jsp
- form ํ๋ผ๋ฏธํฐ:
mainLogin=N, rtUrl=library.sejong.ac.kr, id, password
- ํค๋:
Host, Referer, Cookie: chknos=false
- SSO ๋ฆฌ๋ค์ด๋ ํธ (L65~72)
GET http://classic.sejong.ac.kr/_custom/sejong/sso/sso-return.jsp?returnUrl=... ๋ก ์ธ์
(์ฟ ํค) ํ๋ณด
- ๋ฐ์ดํฐ ํ์ด์ง ์์ง
fetchReadingStatusHtml() (L150~165)
GET https://classic.sejong.ac.kr/classic/reading/status.do โ HTML ๋ฌธ์์ด ๋ฐํ
- HTML ํ์ฑ
parseHTMLAndGetMemberInfo() (L173~198)
- Jsoup selector:
.b-con-box:has(h4.b-h4-tit01:contains(์ฌ์ฉ์ ์ ๋ณด)) table.b-board-table tbody tr
- ํ
์ด๋ธ ํ ์์๋ก
ํ๊ณผ(0) / ํ๋ฒ(1) / ์ด๋ฆ(2) / ํ๋
(3) / ๋ฑ๋ก์ํ(4) ์ถ์ถ โ AuthDto ๋น๋
- HTTP ํด๋ผ์ด์ธํธ
buildClient() (L234~263)
OkHttpClient ์ฌ์ฉ. ์ฟ ํค ์๋๊ด๋ฆฌ(JavaNetCookieJar, CookiePolicy.ACCEPT_ALL).
- โ ๏ธ SSL ๊ฒ์ฆ ์ ๋ฉด ๋นํ์ฑํ:
trustAllManager()(๋ชจ๋ ์ธ์ฆ์ ์ ๋ขฐ, L268~279) + hostnameVerifier = (h,s) -> true(L242).
- ์ฌ์๋
executeWithRetry() (L211~226): SocketTimeoutException ์ ์ต๋ 3ํ ์ฌ์๋, ์คํจ ์ SEJONG_AUTH_CONNECTION_ERROR.
๐ค 2. ํ์ ์ฒ๋ฆฌ (AuthApplicationService)
- ์ธ์ฆ ๊ฒฐ๊ณผ(
AuthDto)์ ํ๋ฒ์ผ๋ก memberRepository.findByStudentId() ์กฐํ, ์์ผ๋ฉด ์ ๊ท ๋ฑ๋ก (signIn L78117 / signInForMobile L242281).
- ์ ๊ท ํ์ ๊ธฐ๋ณธ ๊ถํ
ROLE_USER, AdminProperties.isAdmin(studentId)๋ฉด ROLE_ADMIN ์ถ๊ฐ.
- ์ ๊ท ํ์์ Exp ์ํฐํฐ(ํฐ์ด R, exp 0) ์์ฑ.
- ๋๋ค์์
UUID ์ 6์๋ฆฌ๋ก ์์ ์์ฑ(uuidNickname).
- ์ฒซ ๋ก๊ทธ์ธ(
isFirstLogin)์ด๋ฉด ์ฝ์ ๋ณด์ ์ง๊ธ(yeopjeonProperties.getCreateAccount()) ํ ํ๋๊ทธ ํด์ (L141~154).
- ํ์์
major๋ก Faculty(๋จ๊ณผ๋) ๋งคํ (departmentRepository.findDeptMPrintOrDeptSPrint, L124~138). ๋งคํ ์คํจ ์ NOT_FOUND ์ฒ๋ฆฌ.
lastLoginTime ๊ฐฑ์ ํ ์ ์ฅ.
- โ ๏ธ ๋น๋ฐ๋ฒํธ๋ ์ฐ๋ฆฌ DB์ ์ ์ฅํ์ง ์์: ์ธ์ข
ํฌํธ ์ธ์ฆ์๋ง ์ฌ์ฉํ๊ณ ,
Member์๋ ํ๋ฒยท์ด๋ฆยทํ๊ณผยทํ๋
ยท์ํ๋ง ์ ์ฅ.
๐ 3. JWT ํ ํฐ (JwtUtil)
- ์ค์ ๊ฐ(
@Value): jwt.secret-key, jwt.access-exp-time(=3600000ms / 1h), jwt.refresh-exp-time(=604800000ms / 7d), jwt.issuer(=SEJONG_MALSAMI).
- ํ ํฐ ์์ฑ
createToken() (L104~115): subject=memberId, claim์ผ๋ก category(access|refresh), username(memberId), issuer, iat, exp ํฌํจ. ์๋ช
์ HMAC-SHA (Base64 ๋์ฝ๋ฉํ secret-key โ Keys.hmacShaKeyFor).
validateToken() (L123~144): ๋ง๋ฃ(ExpiredJwtException)๋ ํธ์ถ๋ถ๋ก ์ฌ๋์ง๊ณ , ๊ทธ ์ธ(์ง์์ํจ/ํ์์ค๋ฅ/์๋ช
์ค๋ฅ/๋น๊ฐ)๋ false ๋ฐํ.
- RefreshToken ์ ์ฅ: MongoDB
RefreshToken(token, memberId, expiryDate) ์ปฌ๋ ์
์ ์ ์ฅ.
ํ ํฐ ์ ๋ฌ ๋ฐฉ์ (์น vs ๋ชจ๋ฐ์ผ)
| ๊ตฌ๋ถ |
AccessToken |
RefreshToken |
์น (/signin) |
์๋ต ๋ฐ๋(AuthDto.accessToken) |
HttpOnly ์ฟ ํค (Set-Cookie, SameSite=None; Secure; HttpOnly) |
๋ชจ๋ฐ์ผ (/mobile/signin) |
์๋ต ๋ฐ๋ |
์๋ต ๋ฐ๋(JSON, ์ฟ ํค ๋ฏธ์ฌ์ฉ) |
- ํ ํฐ ๊ฐฑ์ : ์น
refreshAccessToken()(์ฟ ํค์์ RefreshToken ์ถ์ถ โ Access ์ฌ๋ฐ๊ธ), ๋ชจ๋ฐ์ผ refreshTokensForMobile()(Access+Refresh ๋ชจ๋ ์ฌ๋ฐ๊ธ, ๊ธฐ์กด Refresh ์ญ์ ํ ์ ๊ท ์ ์ฅ).
๐ก๏ธ 4. Spring Security / ํํฐ (WebSecurityConfig + TokenAuthenticationFilter)
- ์ธ์
STATELESS,
csrf/httpBasic/formLogin ๋นํ์ฑํ (WebSecurityConfig L56~111).
- ์ธ๊ฐ ๊ท์น:
SecurityUrls.AUTH_WHITELIST permitAll, SecurityUrls.ADMIN_PATHS ๋ ROLE_ADMIN, ๊ทธ ์ธ ์ธ์ฆ ํ์.
- ํ์ดํธ๋ฆฌ์คํธ ์:
/api/auth/signin, /api/auth/mobile/signin, /api/auth/refresh, Swagger(/docs/**, /v3/api-docs/**), ๊ด๋ฆฌ์ ํ์ด์ง ๋ก๊ทธ์ธ(/login) ๋ฑ (SecurityUrls L14~).
- CORS: ํ์ฉ Origin 6์ข
(์๋๋ก์ง ์ธ๋ถ ๋๋ฉ์ธ 2 + localhost 4),
allowCredentials=true (L4249, L132144).
TokenAuthenticationFilter (addFilterBefore, UsernamePasswordAuthenticationFilter ์):
- ํ์ดํธ๋ฆฌ์คํธ๋ฉด ํต๊ณผ(L44~47).
/api/**: Authorization: Bearer <token>์์ ์ถ์ถ(L57~62).
/admin/**: Authorization ํค๋ ์ฐ์ , ์์ผ๋ฉด accessToken ํ๋ผ๋ฏธํฐ(L63~74).
- ๊ฒ์ฆ ์ฑ๊ณต ์
CustomUserDetailsService.loadUserByUsername(memberId) ๋ก๋ โ SecurityContext ์ธ์ฆ ์ค์ (L78~82).
- ์คํจ ์ API๋ JSON ์๋ฌ(
MISSING_AUTH_TOKEN / INVALID_ACCESS_TOKEN / EXPIRED_ACCESS_TOKEN), ๊ด๋ฆฌ์ ํ์ด์ง๋ /login ๋ฆฌ๋ค์ด๋ ํธ.
CustomUserDetailsService (L22~38): username = memberId(UUID) ๊ธฐ์ค์ผ๋ก Member ์กฐํ โ CustomUserDetails ๋ฐํ. ๊ถํ์ Member.roles ๊ธฐ๋ฐ.
๐งช 5. ํ
์คํธ ๊ณ์ ์ฐํ (ํ๋ฒ 9...)
- ํ๋ฒ์ด
9๋ก ์์ํ๋ฉด ์ธ์ข
ํฌํธ์ ํธ์ถํ์ง ์๊ณ PostgreSQL test_member์์ ์กฐํ(SejongPortalAuthenticator.getTestInfos L92~112).
- ๋น๋ฐ๋ฒํธ๋ BCrypt(
PasswordEncoder.matches)๋ก ๋งค์นญ.
- ์ฑ ์ฌ์ฌยท๋ก์ปฌ/QA ํ
์คํธ ๋ฑ ์ค์ ์ธ์ข
ํฌํธ ์ธ์ฆ์ด ์ด๋ ค์ด ํ๊ฒฝ์ ์ํ ์ฅ์น๋ก ๋ณด์.
๐ ์ฐธ๊ณ : ๋ถ์์์ ๊ด์ฐฐ๋ ์ ์ฌ ๋ฆฌ์คํฌ (์์
์๋, ๊ธฐ๋ก์ฉ)
- ์ธ๋ถ HTML ๊ตฌ์กฐ ๊ฒฐํฉ: ์ธ์ข
ํฌํธ ํ์ด์ง ๊ตฌ์กฐ/์๋ํฌ์ธํธ ๋ณ๊ฒฝ ์ ๋ก๊ทธ์ธ ์ ์ฒด๊ฐ ์ฆ์ ์คํจ ๊ฐ๋ฅ (Jsoup selector ์์กด).
- SSL ๊ฒ์ฆ ์ ๋ฉด ๋นํ์ฑํ(
trustAllManager + hostnameVerifier true): MITM ๊ด์ ์์ ์ํ. (์ธ์ข
ํฌํธ ์ธ์ฆ์ ์ด์ ํํผ ๋ชฉ์ ์ผ๋ก ์ถ์ )
- ์ฟ ํค Secure/SameSite ๋ถ๊ธฐ: ์ฝ๋์
setSecure(false) + ์๋ Set-Cookie์์๋ Secure; SameSite=None ์ ๋ถ์ด๋ ๋ฑ ํ๊ฒฝ๋ณ ๋ถ๊ธฐ์ FIXME ์ฃผ์ ๋ค์ ์กด์ฌ.
- ๋ฏผ๊ฐ์ ๋ณด ๋ก๊น
:
AuthApplicationService์ accessToken/refreshToken ๊ฐ์ log.info๋ก ์ถ๋ ฅํ๋ ์ฝ๋ ์กด์ฌ(L168169, L332333 ๋ฑ).
์ ๋ฆฌ์คํฌ๋ค์ ๋ณธ ์ด์์์ ์์ ํ์ง ์๋๋ค. ์ถํ ๊ฐ์ ์ด ํ์ํ๋ค๋ฉด ๊ฐ ํญ๋ชฉ๋ณ๋ก ๋ณ๋ ์ด์๋ฅผ ๋ถ๋ฆฌํด ๋ค๋ฃฌ๋ค.
๐ ๏ธ ํด๊ฒฐ ๋ฐฉ์ / ์ ์
- ๋ณธ ์ด์๋ ํํฉ ๋ถ์ยท๋ฌธ์ํ ์ ์ฉ์ด๋ฉฐ ๋ณ๋ ์ฝ๋ ์์
์ ๋๋ฐํ์ง ์๋๋ค.
- ๋ณธ๋ฌธ ๋ด์ฉ์ ์ถํ ์ธ์ฆ ๋ฆฌํฉํ ๋งยท๋ณด์ ๊ฐ์ ยท์ธ์ข
ํฌํธ ๋ณ๊ฒฝ ๋์ ๋
ผ์ ์ ๊ณตํต ๋ ํผ๋ฐ์ค๋ก ํ์ฉํ๋ค.
๐โโ๏ธ ๋ด๋น์
- ๋ฐฑ์๋: -
- ํ๋ก ํธ์๋: -
- ๋์์ธ: -
๐ ํ์ฌ ๋ฌธ์ ์
๐ ์ธ์ฆ ์ ์ฒด ํ๋ฆ ์์ฝ
๐งฉ ํต์ฌ ์ปดํฌ๋ํธ / ํ์ผ ๊ฒฝ๋ก
AuthControllerSM-Web/.../web/controller/api/AuthController.javaAuthApplicationServiceSM-Application/.../application/service/AuthApplicationService.javaSejongPortalAuthenticatorSM-Domain-Auth/.../auth/service/SejongPortalAuthenticator.javaJwtUtilSM-Domain-Auth/.../auth/util/JwtUtil.javaTokenAuthenticationFilterSM-Domain-Auth/.../auth/filter/TokenAuthenticationFilter.javaCustomUserDetailsServiceSM-Domain-Auth/.../auth/service/CustomUserDetailsService.javaWebSecurityConfigSM-Web/.../web/config/WebSecurityConfig.javaSecurityUrlsSM-Common/.../dto/SecurityUrls.javaAuthCommand/AuthDtoSM-Domain-Auth/.../auth/dto/๐ฐ๏ธ 1. ์ธ์ข ํฌํธ ํฌ๋กค๋ง ์ธ์ฆ (SejongPortalAuthenticator)
getMemberAuthInfos(AuthCommand)(L48)sejongPortalId,sejongPortalPassword๋ ๊ฐ์ ์ ๋ ฅ์ผ๋ก ๋ฐ์ (AuthCommandL26~27)."9"๋ก ์์ํ๋ฉด ์ธ์ข ํฌํธ์ ๊ฑฐ์น์ง ์๊ณgetTestInfos()๋ก ์ฐํ โtest_memberํ ์ด๋ธ์์ ์กฐํ ํ BCrypt ๋น๋ฐ๋ฒํธ ๋งค์นญ(L92~112).doPortalLogin()(L117~143)POST https://portal.sejong.ac.kr/jsp/login/login_action.jspmainLogin=N,rtUrl=library.sejong.ac.kr,id,passwordHost,Referer,Cookie: chknos=falseGET http://classic.sejong.ac.kr/_custom/sejong/sso/sso-return.jsp?returnUrl=...๋ก ์ธ์ (์ฟ ํค) ํ๋ณดfetchReadingStatusHtml()(L150~165)GET https://classic.sejong.ac.kr/classic/reading/status.doโ HTML ๋ฌธ์์ด ๋ฐํparseHTMLAndGetMemberInfo()(L173~198).b-con-box:has(h4.b-h4-tit01:contains(์ฌ์ฉ์ ์ ๋ณด)) table.b-board-table tbody trํ๊ณผ(0) / ํ๋ฒ(1) / ์ด๋ฆ(2) / ํ๋ (3) / ๋ฑ๋ก์ํ(4)์ถ์ถ โAuthDto๋น๋buildClient()(L234~263)OkHttpClient์ฌ์ฉ. ์ฟ ํค ์๋๊ด๋ฆฌ(JavaNetCookieJar,CookiePolicy.ACCEPT_ALL).trustAllManager()(๋ชจ๋ ์ธ์ฆ์ ์ ๋ขฐ, L268~279) +hostnameVerifier = (h,s) -> true(L242).executeWithRetry()(L211~226):SocketTimeoutException์ ์ต๋ 3ํ ์ฌ์๋, ์คํจ ์SEJONG_AUTH_CONNECTION_ERROR.๐ค 2. ํ์ ์ฒ๋ฆฌ (AuthApplicationService)
AuthDto)์ ํ๋ฒ์ผ๋กmemberRepository.findByStudentId()์กฐํ, ์์ผ๋ฉด ์ ๊ท ๋ฑ๋ก (signInL78117 /281).signInForMobileL242ROLE_USER,AdminProperties.isAdmin(studentId)๋ฉดROLE_ADMIN์ถ๊ฐ.UUID ์ 6์๋ฆฌ๋ก ์์ ์์ฑ(uuidNickname).isFirstLogin)์ด๋ฉด ์ฝ์ ๋ณด์ ์ง๊ธ(yeopjeonProperties.getCreateAccount()) ํ ํ๋๊ทธ ํด์ (L141~154).major๋ก Faculty(๋จ๊ณผ๋) ๋งคํ (departmentRepository.findDeptMPrintOrDeptSPrint, L124~138). ๋งคํ ์คํจ ์NOT_FOUND์ฒ๋ฆฌ.lastLoginTime๊ฐฑ์ ํ ์ ์ฅ.Member์๋ ํ๋ฒยท์ด๋ฆยทํ๊ณผยทํ๋ ยท์ํ๋ง ์ ์ฅ.๐ 3. JWT ํ ํฐ (JwtUtil)
@Value):jwt.secret-key,jwt.access-exp-time(=3600000ms / 1h),jwt.refresh-exp-time(=604800000ms / 7d),jwt.issuer(=SEJONG_MALSAMI).createToken()(L104~115):subject=memberId, claim์ผ๋กcategory(access|refresh),username(memberId),issuer,iat,expํฌํจ. ์๋ช ์ HMAC-SHA (Base64 ๋์ฝ๋ฉํ secret-key โKeys.hmacShaKeyFor).validateToken()(L123~144): ๋ง๋ฃ(ExpiredJwtException)๋ ํธ์ถ๋ถ๋ก ์ฌ๋์ง๊ณ , ๊ทธ ์ธ(์ง์์ํจ/ํ์์ค๋ฅ/์๋ช ์ค๋ฅ/๋น๊ฐ)๋false๋ฐํ.RefreshToken(token, memberId, expiryDate) ์ปฌ๋ ์ ์ ์ ์ฅ.ํ ํฐ ์ ๋ฌ ๋ฐฉ์ (์น vs ๋ชจ๋ฐ์ผ)
/signin)AuthDto.accessToken)SameSite=None; Secure; HttpOnly)/mobile/signin)refreshAccessToken()(์ฟ ํค์์ RefreshToken ์ถ์ถ โ Access ์ฌ๋ฐ๊ธ), ๋ชจ๋ฐ์ผrefreshTokensForMobile()(Access+Refresh ๋ชจ๋ ์ฌ๋ฐ๊ธ, ๊ธฐ์กด Refresh ์ญ์ ํ ์ ๊ท ์ ์ฅ).๐ก๏ธ 4. Spring Security / ํํฐ (WebSecurityConfig + TokenAuthenticationFilter)
csrf/httpBasic/formLogin๋นํ์ฑํ (WebSecurityConfigL56~111).SecurityUrls.AUTH_WHITELISTpermitAll,SecurityUrls.ADMIN_PATHS๋ROLE_ADMIN, ๊ทธ ์ธ ์ธ์ฆ ํ์./api/auth/signin,/api/auth/mobile/signin,/api/auth/refresh, Swagger(/docs/**,/v3/api-docs/**), ๊ด๋ฆฌ์ ํ์ด์ง ๋ก๊ทธ์ธ(/login) ๋ฑ (SecurityUrlsL14~).allowCredentials=true(L4249, L132144).TokenAuthenticationFilter(addFilterBefore, UsernamePasswordAuthenticationFilter ์):/api/**:Authorization: Bearer <token>์์ ์ถ์ถ(L57~62)./admin/**: Authorization ํค๋ ์ฐ์ , ์์ผ๋ฉดaccessTokenํ๋ผ๋ฏธํฐ(L63~74).CustomUserDetailsService.loadUserByUsername(memberId)๋ก๋ โSecurityContext์ธ์ฆ ์ค์ (L78~82).MISSING_AUTH_TOKEN/INVALID_ACCESS_TOKEN/EXPIRED_ACCESS_TOKEN), ๊ด๋ฆฌ์ ํ์ด์ง๋/login๋ฆฌ๋ค์ด๋ ํธ.CustomUserDetailsService(L22~38): username =memberId(UUID)๊ธฐ์ค์ผ๋กMember์กฐํ โCustomUserDetails๋ฐํ. ๊ถํ์Member.roles๊ธฐ๋ฐ.๐งช 5. ํ ์คํธ ๊ณ์ ์ฐํ (ํ๋ฒ
9...)9๋ก ์์ํ๋ฉด ์ธ์ข ํฌํธ์ ํธ์ถํ์ง ์๊ณ PostgreSQLtest_member์์ ์กฐํ(SejongPortalAuthenticator.getTestInfosL92~112).PasswordEncoder.matches)๋ก ๋งค์นญ.๐ ์ฐธ๊ณ : ๋ถ์์์ ๊ด์ฐฐ๋ ์ ์ฌ ๋ฆฌ์คํฌ (์์ ์๋, ๊ธฐ๋ก์ฉ)
trustAllManager+hostnameVerifier true): MITM ๊ด์ ์์ ์ํ. (์ธ์ข ํฌํธ ์ธ์ฆ์ ์ด์ ํํผ ๋ชฉ์ ์ผ๋ก ์ถ์ )setSecure(false)+ ์๋ Set-Cookie์์๋Secure; SameSite=None์ ๋ถ์ด๋ ๋ฑ ํ๊ฒฝ๋ณ ๋ถ๊ธฐ์FIXME์ฃผ์ ๋ค์ ์กด์ฌ.AuthApplicationService์ accessToken/refreshToken ๊ฐ์log.info๋ก ์ถ๋ ฅํ๋ ์ฝ๋ ์กด์ฌ(L168169, L332333 ๋ฑ).๐ ๏ธ ํด๊ฒฐ ๋ฐฉ์ / ์ ์
๐โโ๏ธ ๋ด๋น์