Reference for kdshrsso skill Option B. Adapt paths, IdP URLs, and user-mapping field per project.
Related: portal-login-reference.md, migration-guide.md
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
<!-- SSO ��ʼ -->
<servlet>
<servlet-name>OAToSHR</servlet-name>
<servlet-class>com.kingdee.eas.custom.sso.OAToSHR</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>OAToSHR</servlet-name>
<url-pattern>/api/oAToSHR</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>SHRToIDMLogout</servlet-name>
<servlet-class>com.kingdee.eas.custom.sso.SHRToIDMLogout</servlet-class>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SHRToIDMLogout</servlet-name>
<url-pattern>/api/SHRToIDMLogout</url-pattern>
</servlet-mapping>
CAS filters unchanged (casAuthenticationFilter / casTicketValidationFilter). Exclude in ssoParamemter.properties:
cas.client.UrlPattern=...;/index2sso.jsp;...;/api/oAToSHR;/api/SHRToIDMLogout;/api/oatombos;
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)
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 |
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.
Path: {EAS_HOME}/server/properties/yuhu/OASSOConfig.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.
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.
GET {getAccessTokenPath}?code=&client_id=&client_secret=&redirect_uri=
{ "code": "0", "data": { "access_token": "...", "user_info": { "id": "...", "username": "..." } } }
See portal-login-reference.md for full analysis.
Summary:
login_withForm.js runs before login form renders/shr/home.do for native eassso loginoaSsoUrl + "?service=" + encodeURIComponent(serviceParam)includes()Uses ExpiringMapCache �� state carries cache key not full URL:
state = ssoUrl + "?redirect=" + cacheKey
Do not confuse with OAToSHR direct URL-in-state pattern.
- [ ] 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
| 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 |
PortalAuthenticationFilter + TicketValidationFilter + PortalServiceTicketValidator:
Production login path: OAToSHR servlet only.