# IDM Portal SSO �� Servlet + LtpaToken Reference (YHPro) Reference for `kdshrsso` skill **Option B**. Adapt paths, IdP URLs, and user-mapping field per project. Related: [portal-login-reference.md](portal-login-reference.md), [migration-guide.md](migration-guide.md) ## Source layout (YHPro) ``` src/com/kingdee/eas/custom/sso/ ������ OAToSHR.java # SHR portal SSO entry servlet ������ SHRToIDMLogout.java # Federated logout (returns idmLogoutUrl JSON) ������ OAToMBos.java # Mobile MBOS SSO (separate config) ������ SsoUtil.java # Config, HTTP, user lookup, bill validation ������ utils/ExpiringMapCache.java ������ filter/authentication/PortalAuthenticationFilter.java # WIP �� not in web.xml ������ filter/validate/TicketValidationFilter.java # WIP �� not in web.xml ������ validation/PortalServiceTicketValidator.java # WIP validator src/org/jasig/cas/client/Protocol.java # Protocol.YH("code","redirect_uri") runtime/server/properties/yuhu/OASSOConfig.properties runtime/server/deploy/easweb.ear/shr_web.war/WEB-INF/web.xml runtime/server/deploy/easweb.ear/shr_web.war/WEB-INF/sso/ssoParamemter.properties runtime/server/deploy/eas.ear/sso_web.war/common/js/login_withForm.js # portal bridge runtime/server/deploy/easweb.ear/shr_web.war/scripts/gui/logoutHR.js # SHR logout ``` --- ## web.xml (SHR servlets) ```xml OAToSHR com.kingdee.eas.custom.sso.OAToSHR OAToSHR /api/oAToSHR SHRToIDMLogout com.kingdee.eas.custom.sso.SHRToIDMLogout SHRToIDMLogout /api/SHRToIDMLogout ``` CAS filters unchanged (`casAuthenticationFilter` / `casTicketValidationFilter`). Exclude in `ssoParamemter.properties`: ``` cas.client.UrlPattern=...;/index2sso.jsp;...;/api/oAToSHR;/api/SHRToIDMLogout;/api/oatombos; ``` --- ## Full call chain (with portal) ``` Portal CAS �� eassso/login?service=https://shr.../shr/dynamic.do?...&AssignmentId=... �� login_withForm.js intercept �� /shr/api/oAToSHR?service=encodeURIComponent(service) (portal may also use ?redirect= for direct links) OAToSHR.doPost() ������ no code �� authorize() �� dynamicRedirect = redirect | service | default homepage �� state = URLEncoder.encode(dynamicRedirect) �� dynamic, not fixed �� 302 �� IDM authorizePath?response_type=code&scope=server&client_id&state&redirect_uri=ssoUrl ������ code �� callBack() decode state �� redirectUrl GET getAccessTokenPath?code&client_id&client_secret&redirect_uri user_info.id �� checkUserExist(cfidmusercode) �� T_PM_USER.FNumber 302 �� /shr/index2sso.jsp?username&password(Ltpa)&redirectTo index2sso.jsp �� SHR session �� redirectUrl (todo / homepage) ``` --- ## Dynamic redirect (`state`) | Stage | Behavior | |-------|----------| | Input | `redirect` (1st) or `service` (2nd) on OAToSHR request | | Authorize | `state = URLEncoder.encode(targetUrl)` �� **changes every request** | | Callback | `URLDecoder.decode(state)` �� final redirectTo | | Default | `{serverName}/shr/dynamic.do?uipk=shr.perself.homepage` | --- ## Logout chain ``` logoutHR.js �� platform logout AJAX chain (/eassso/logout, shr_loginout, easweb/logout, ...) �� clear cookies / storage �� redirect to idmLogoutUrl (built in JS or from servlet �� prefer servlet) SHRToIDMLogout (POST /shr/api/SHRToIDMLogout) �� build IDM authorize URL with state = portal home �� idmLogoutUrl = {idmBaseUrl}/token/code-logout?redirect_url={encoded authorize URL} �� session.invalidate() �� JSON { code:"1", idmLogoutUrl:"..." } ``` **Improvement:** have `logoutHR.js` call `SHRToIDMLogout` instead of duplicating IDM URL in JS. --- ## Config template Path: `{EAS_HOME}/server/properties/yuhu/OASSOConfig.properties` ```properties authorizePath=https://idm-test.example.com/oauth2/authorize getAccessTokenPath=https://idm-test.example.com/token/codeInfo client_id=shr client_secret= response_type=code ssoUrl=https://shr-test.example.com/shr/api/oAToSHR serverName=https://shr-test.example.com idmBaseUrl=https://idm-test.example.com lastRedirectUrl=/shr/home.do ``` **Production pitfall:** sample file had `redirect_uri1/2` for MBOS but `OAToSHR` requires **`ssoUrl`** (strict). Missing `ssoUrl` causes init/runtime errors. --- ## User mapping SQL ```sql SELECT uinfo.FNumber AS userNum FROM T_bd_person person LEFT JOIN T_PM_USER uinfo ON person.fid = uinfo.FPERSONID WHERE uinfo.FIsDelete = '0' AND uinfo.FForbidden = '0' AND person.cfidmusercode = ? ``` IDM field: `data.user_info.id` from token response. --- ## IDM token API GET `{getAccessTokenPath}?code=&client_id=&client_secret=&redirect_uri=` ```json { "code": "0", "data": { "access_token": "...", "user_info": { "id": "...", "username": "..." } } } ``` --- ## Portal login_withForm.js (YHPro bridge) See [portal-login-reference.md](portal-login-reference.md) for full analysis. Summary: - IIFE at top of `login_withForm.js` runs before login form renders - Whitelist `/shr/home.do` for native eassso login - Other SHR paths �� `oaSsoUrl + "?service=" + encodeURIComponent(serviceParam)` - **Anti-patterns found:** hardcoded ENV_CONFIG, runtime-only edit, prod URL placeholder, loose whitelist `includes()` --- ## OAToMBos (mobile, separate) Uses `ExpiringMapCache` �� state carries cache key not full URL: ``` state = ssoUrl + "?redirect=" + cacheKey ``` Do not confuse with OAToSHR direct URL-in-state pattern. --- ## Deploy checklist ``` - [ ] Compile src/com/kingdee/eas/custom/sso/** - [ ] Deploy to shr_web.war/WEB-INF/classes - [ ] web.xml servlets + ssoParamemter.properties UrlPattern - [ ] OASSOConfig.properties on server (include ssoUrl, idmBaseUrl) - [ ] IDM redirect_uri = ssoUrl - [ ] person.cfidmusercode for all SSO users - [ ] login_withForm.js overlay in sso_web.war (src tracked, not runtime-only) - [ ] logoutHR.js calls SHRToIDMLogout or shares config - [ ] Test: portal todo, home.do whitelist, logout ``` --- ## Troubleshooting | Symptom | Check | |---------|--------| | 500 on servlet init | OASSOConfig missing; ssoUrl empty | | authorize fails | authorizePath, client_id, IDM app registration | | Token exchange fails | GET API; redirect_uri must match authorize | | User not found | cfidmusercode vs user_info.id; duplicate rows | | Login page flash | mask + replace(); onbeforeunload cleared | | index2sso loop | UrlPattern excludes servlet + index2sso.jsp | | Deep link wrong | state decode; redirect vs service param | | Logout 401 IDM | idmLogoutUrl spec; redirect_url encoding | --- ## WIP filter code (not production) `PortalAuthenticationFilter` + `TicketValidationFilter` + `PortalServiceTicketValidator`: - Mirror Option A structure but **not wired in web.xml** - Hardcoded test URLs; POST token vs GET in OAToSHR - Use only as reference for [idm-filter-reference.md](idm-filter-reference.md) migration Production login path: **OAToSHR servlet only**.