Designing an Enterprise‑Level CAS SSO Architecture: A Practical Blueprint

This article presents a detailed technical blueprint for building an enterprise‑grade single sign‑on system that combines Apereo CAS, OAuth2, and JWT across four microservices, covering service responsibilities, data models, API contracts, deployment trade‑offs, and operational safeguards.

samdeepthink
samdeepthink
samdeepthink
Designing an Enterprise‑Level CAS SSO Architecture: A Practical Blueprint

Why Split Into Four Services

Responsibilities are separated to keep authentication, token issuance, user data, and gateway logic independent:

CAS (base‑oauth2) handles identity – issuing Ticket‑Granting Tickets (TGT) and Service Tickets (ST) for traditional web apps.

OAuth2 enables front‑end applications to delegate authentication to the CAS centre and obtain an access token.

Business JWT carries claims ( userId, job_number, email) for the management backend.

Business JWT is issued by service‑umd, not by CAS, keeping the identity provider and permission system decoupled.

Overall Architecture

Four microservices are deployed in three logical layers: authentication, gateway, and user/identity services.

Architecture diagram
Architecture diagram

Management backend uses OAuth2 without a CAS client; collaboration platforms and knowledge bases validate CAS tickets directly.

Technology Stack

IdP : Apereo CAS 6.3 Overlay – CAS protocol, OAuth2 grant, SLO support.

Gateway : Spring Cloud Gateway WebFlux – OAuth2 client, JWT validation.

User Service : Spring Boot 2.x + MyBatis Plus – user master data and JWT issuance.

Cache : Redis – stores TGT/ST, logout markers, JWT blacklist.

Package Prefix : com.column.sso.* – unified prefix for business extensions.

Four Services and Their Core Capabilities

base‑oauth2 – authentication centre: login page, multi‑method login, TGT/ST issuance, service registration, global logout orchestration. Does not issue business JWT.

service‑gateway – unified entry for the management backend: OAuth2 login, code‑to‑access‑token exchange, token‑to‑JWT conversion, Bearer validation, routing. Never accesses the user database directly.

base‑umd – employee master‑data platform: employee table, organization info, email/DingTalk scan resolution. Used by CAS for credential lookup before JDBC authentication.

service‑umd – management‑side user service: JWT signing, SSO logout callbacks, JWT blacklist management. During SLO, base‑oauth2 writes logout markers to Redis; service‑gateway reads them to block stale tokens.

Three SSO Flows

Flow A – Front‑End Management Backend (OAuth2 → JWT)

Flow A diagram
Flow A diagram

After successful OAuth2 login, the gateway extracts loginName from the OAuth2 identity and calls service‑umd to obtain a JWT whose issuer is umd‑sso. The front‑end receives the JWT via the query parameter x-access-token and sends it in the Authorization: Bearer {token} header for subsequent requests.

Flow B – Collaboration Platform / Knowledge Base (CAS ST)

Flow B diagram
Flow B diagram

Traditional web apps keep a server‑side session. After CAS ticket validation, a local session is created, avoiding the need to rewrite the app to handle JWT.

Flow C – Global Logout (SLO → Redis → Gateway Intercept)

Flow C diagram
Flow C diagram

Logout must clear both third‑party sessions and still‑valid JWTs. CAS sends a Back‑Channel SLO notification to delete the local session. Simultaneously, service‑umd writes a logout timestamp to Redis (key service‑gateway:user:sso:logout:{userId}); the gateway checks this timestamp and the JWT blacklist ( service‑gateway:user:logout:{token}) on each request.

Data Design

MySQL stores employee master data, RBAC, and login audit. Redis stores TGT/ST, logout markers, and JWT blacklist. Service start‑up loads RegisteredService definitions from JSON files.

MySQL – employee master data, RBAC, login audit. Accessed by base‑oauth2, base‑umd, service‑umd.

Redis – TGT/ST (Apereo CAS Redis Ticket Registry), SSO logout markers, JWT blacklist. Accessed by base‑oauth2, service‑gateway, service‑umd.

JSON files – services/*.json define RegisteredService per environment; loaded by base‑oauth2 at start‑up.

CAS‑generated TGT and ST are temporary tickets stored only in Redis because they have no long‑term business value.

User Master Table ( umd_user )

Columns used by SSO: id – primary key; maps to JWT sub and Redis logout key {userId}. email – corporate email; used for email login and CAS JDBC match. job_number – employee number; primary CAS principal. mobile – phone number; used for password recovery and legacy login. password – password hash; verified by JDBC (bcrypt, MD5 fallback). ding_userid – DingTalk user ID; links DingTalk scan to main user. status – enabled flag; CAS SQL includes status=1. delete_flag – soft‑delete flag; CAS SQL includes delete_flag=0. resetting_password – password‑reset marker; participates in password‑expiry policy. password_updated_at – last password change timestamp; 180‑day mandatory change.

SQL used by CAS for credential lookup:

WHERE (email = :username OR job_number = :username) AND delete_flag = 0 AND status = 1

Login API expects loginName which may be email, job_number, or mobile; CAS prefers job_number as the principal.

DingTalk Scan Helper Table ( umd_ding_user )

Columns: ding_userid, unionid, job_number, email, mobile, delete_flag. Used to resolve a scan code to a user record.

Management Backend Authorization Tables

Five tables ( umd_user_role, umd_role, umd_role_priv, umd_priv, umd_module) implement RBAC. They are not involved in CAS authentication.

Base‑oauth2 Helper Table

COM_AUDIT_TRAIL

records login audit and provides data for JDBC throttling. Service registration is managed via JSON files under services/*.json, not via a database.

Redis Key Conventions

service-gateway:user:sso:logout:{userId}

– global SSO logout Unix timestamp. service-gateway:user:logout:{token} – single JWT blacklist entry. service-gateway:user:logout:at:{userId} – user‑level logout time.

JWT Conventions (issued by service‑umd )

Issuer is umd‑sso. The sub claim contains userId. Additional claims include id, job_number, and email. The token is delivered via the URL query parameter x-access-token after OAuth2 callback and then sent in the Authorization: Bearer {token} header.

API Boundaries (Naming Conventions)

base‑oauth2 → base‑umd

GET /center/base-umd/user/detail/email/{email} – resolve user by email for login.

GET /center/base-umd/user/detail/ding/scan/{code} – resolve user via DingTalk scan code.

service‑gateway → service‑umd

GET /api/service-umd/admin/user/token?loginName= – exchange OAuth2 success for business JWT.

base‑oauth2 → service‑umd (SLO callback)

POST /api/service-umd/admin/user/logout/email/{emailOrJobNumber} – write global logout marker to Redis.

service‑gateway External API Prefixes

/api/service-gateway/admin/openapi/**

– OAuth2 callback and whitelist.

Other protected routes – JWT authentication filter enforcement.

Service Registration Strategy (base‑oauth2)

The same CAS centre registers two kinds of services using different RegisteredService types:

Management Backend OAuth2 – OAuthRegisteredService with clientId and redirectUri regex.

Collaboration Platform / Knowledge Base – RegexRegisteredService with serviceId regex and logoutType=BACK_CHANNEL.

JSON files are split per environment (dev/test/pre/prod) and ordered by evaluationOrder to control matching priority.

Code‑Level Extension Points

base‑oauth2 extends CustomJdbcAuthenticationHelper to route loginType (email, job_number, DingTalk) to base‑umd for identity resolution; ExternalLogoutNotifier (extends DefaultLogoutManager) posts logout events to service‑umd and logs warnings on failure.

service‑gateway configures the OAuth2 login chain via GatewaySecurityConfig; on success, OAuth2AuthenticationSuccessHandler calls service‑umd to obtain a JWT and redirects the front‑end; JwtAuthGatewayFilterFactory validates Bearer tokens, checks the issuer umd‑sso, and consults Redis for logout timestamps and blacklist.

service‑umd exposes AdminUserController for token issuance and logout; JwtTokenProvider.createAdminToken embeds user claims and the issuer.

base‑umd provides UserQueryController for email and DingTalk scan lookups.

Key Design Trade‑offs

Why Keep Both CAS and OAuth2

Legacy web apps require CAS tickets (Session), while modern SPA back‑ends need OAuth2 (JWT). Using a single protocol would force costly refactoring. Maintaining both protocols adds configuration overhead but dramatically reduces client migration effort.

Why Business JWT Is Separate From CAS

Management backend needs custom claims (userId, job_number, email) and a unified logout/blacklist strategy. Embedding this logic in CAS would tightly couple authentication and authorization, making future changes to keys, claims, or logout policies harder. Delegating JWT issuance to service‑umd keeps responsibilities clean.

Why the Gateway Does Not Access the User Database Directly

The gateway deliberately omits JDBC/MyBatis dependencies. Authorization relies on JWT signature verification and Redis‑cached logout markers. Role and permission data are read from Redis (or local cache) to avoid database round‑trips, keeping the gateway lightweight and stateless.

Why SLO Callback Failures Do Not Block the Flow

If service‑umd is temporarily unavailable, base‑oauth2 logs a warning and continues the Back‑Channel SLO to third‑party apps. This prevents a situation where users think they are logged out while the backend still holds a valid JWT.

Why Redis Fail‑Open Is Preferred

When Redis throws an exception, service‑gateway treats the token as not logged out and allows the request to proceed. This design favors availability; a brief loss of logout enforcement is acceptable compared to a complete authentication outage. Monitoring and alerts must detect Redis failures.

Non‑Functional Design

High Availability & Horizontal Scaling

base‑oauth2

stores TGT/ST in Redis (Apereo CAS Redis Ticket Registry); multiple instances share the same ticket store, eliminating sticky sessions. service‑gateway is stateless; routing uses service discovery (e.g., lb://service-umd) for load balancing.

TGT default TTL is 7 days ( time-to-kill-in-seconds: 604800); OAuth2 access token TTL is defined per JSON config; JWTs have explicit logout and SSO invalidation mechanisms.

Security Hardening

Login throttling in base‑oauth2 via COM_AUDIT_TRAIL (IP + username failure count) and captcha. service‑umd tracks password‑error counts in Redis (lock after 5 attempts for 5 min, captcha failures lock after 3 attempts for 1 min). service‑gateway applies token‑bucket rate limiting per method+path using Redis Lua scripts.

Passwords stored with bcrypt; legacy MD5 hashes are supported via MultiAlgorithmPasswordEncoder and upgraded on next password change. Password policy: 8‑16 characters, complexity, mandatory change every 180 days.

JWT revocation via blacklist, user‑level logout timestamp, and global SSO logout timestamp.

Transport layer termination at Nginx with TLS; client IP restored via X-Forwarded-*. Session cookies set HttpOnly.

Graceful Degradation

Authentication failures return unified error messages; specific reasons (captcha, disabled account, throttling) have distinct prompts. base‑umd aborts the authentication chain on parsing failures, preventing empty usernames from reaching JDBC.

During global logout, service‑umd logs warnings on callback failures but does not block Back‑Channel SLO.

Redis exceptions in blacklist or logout checks are logged and treated as "not logged out" to keep the service responsive.

Extensibility

New applications are added by dropping a JSON file into services/*.json without code changes.

New login methods are supported by extending CustomJdbcAuthenticationHelper with additional customFields[loginType] branches.

Thresholds, DingTalk app IDs, and captcha whitelists are configurable via a central configuration service with @RefreshScope for live reload.

RBAC caching: gateway shares Redis cache plus local EhCache; permission changes are propagated via Fanout MQ.

Observability

base‑oauth2

writes login audit to COM_AUDIT_TRAIL and a dedicated cas_audit.log file. service‑gateway streams audit logs to MQ and adds a trace ID header X-trace-id to responses. service‑umd records operation logs in umd_sys_log aligned with the trace ID.

Risk Matrix

CAS and OAuth2 configuration drift – Mitigation: include JSON service registration in configuration review process.

Credential‑brute‑force attacks – Mitigation: combine JDBC throttling, captcha, Redis counters, and API rate limiting.

Global logout not intercepted – Mitigation: enforce SSO Redis validation using the umd‑sso issuer.

Redis outage – Mitigation: fail‑open strategy with monitoring and alerts.

Third‑party integration omissions – Mitigation: maintain PRD checklist and validation in upcoming implementation articles.

Password migration issues – Mitigation: support bcrypt and MD5 compatibility; enforce 180‑day password change.

Conclusion

The architecture consists of four services, three integration flows, and a unified Redis key scheme. Subsequent articles will flesh out the authentication handler in base‑oauth2, the OAuth2 flow in service‑gateway, CAS client integration, and the complete SSO logout loop.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

microservicesRedisCASJWTOAuth2Spring Cloud GatewaySSO
samdeepthink
Written by

samdeepthink

Knowledge Planet: Old Dock's Tech Chronicles Zhihu: SamDeepThinking A technical manager who still codes heavily on the front line. From junior developer to tech lead, then tech manager, now leading the whole front‑ and back‑end development team—leveling up along the way. I have some insights on programming, career development, and tech management.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.