單點登錄(SSO)核心課題是:多子系統如何共享登錄狀態。本文基於「主域名相同、子域名不同」場景(如 www.bbs.itcast.cn、www.news.itcast.cn),詳細實現基於 Cookie 跨域共享 + Filter 自動登錄的完整方案,並對比 CAS 標準流程。
path=/(全路徑有效)+ domain=.itcast.cn(主域名共享,覆蓋所有子域名)maxAge=0 刪除 Cookie# C:\Windows\System32\drivers\etc\hosts 127.0.0.1 localhost 127.0.0.1 www.bbs.itcast.cn 127.0.0.1 www.news.itcast.cn 127.0.0.1 www.server.com # SSO 認證中心
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
String nm = req.getParameter("name");
String pwd = req.getParameter("pwd");
String chk = req.getParameter("chk"); // 是否記住 7 天
String forward = "/index.jsp";
// 簡單驗證:用戶名以 it 開頭,密碼以 pwd 開頭
if (nm != null && nm.startsWith("it") && pwd != null && pwd.startsWith("pwd")) {
forward = "/jsps/welcome.jsp";
// 關鍵:無論是否記住,都寫入 Cookie 保證跳轉時攜帶
Cookie cookie = new Cookie("autologin", nm + "@" + pwd);
cookie.setPath("/");
cookie.setDomain(".itcast.com"); // 跨子域名共享
if (chk != null) { // 勾選記住我
cookie.setMaxAge(60 * 60 * 24 * 7); // 7 天秒數
}
resp.addCookie(cookie);
req.getSession().setAttribute("user", nm);
}
req.getRequestDispatcher(forward).forward(req, resp);
}
}
public class AutoLogin implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
HttpSession session = request.getSession();
// 已登錄直接放行
if (session.getAttribute("user") != null) {
chain.doFilter(req, resp);
return;
}
// 嘗試從 Cookie 自動登錄
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie ck : cookies) {
if ("autologin".equals(ck.getName())) {
String[] vals = ck.getValue().split("@");
session.setAttribute("user", vals[0]); // 寫入 Session
break;
}
}
}
chain.doFilter(req, resp);
}
// init/destroy 略
}
web.xml 配置:<filter-mapping><url-pattern>/*</url-pattern></filter-mapping> 全路徑攔截。
public class LoginOutServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
HttpSession session = req.getSession();
// 刪除 Cookie:同名、同路徑、同域、maxAge=0
Cookie cookie = new Cookie("autologin", "");
cookie.setPath("/");
cookie.setDomain(".itcast.com");
cookie.setMaxAge(0);
resp.addCookie(cookie);
session.removeAttribute("user");
resp.sendRedirect(req.getContextPath() + "/index.jsp");
}
}
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param><param-name>casServerLoginUrl</param-name><param-value>https://www.server.com:8080/login</param-value></init-param>
<init-param><param-name>serverName</param-name><param-value>https://www.bbs.com:8080</param-value></init-param>
</filter>
<filter>
<filter-name>CAS Validation Filter</filter-name> ...
</filter>
<filter-mapping><filter-name>CAS Authentication Filter</filter-name><url-pattern>/protected/*</url-pattern></filter-mapping>
<filter-mapping><filter-name>CAS Validation Filter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
關鍵差異:CAS 採用「集中式認證中心 + Ticket 驗證 + 代理回調」模式,更安全、支持單點登出、代理認證,但部署複雜;Cookie+Filter 方案輕量、易理解、適合中小型同域系統。
SessionMappingStorage:HashMap 存儲 sessionId ↔ Ticket 映射SingleSignOutHttpSessionListener:Session 銷毀時清理映射SingleSignOutFilter:攔截登出請求,通知 CAS Server 廣播登出通知SimpleServerLogoutHandler:將本地登出轉發至 CAS Server| 維度 | Cookie + Filter | CAS 標準 |
|---|---|---|
| 部署複雜度 | 低(單應用內完成) | 高(獨立 CAS Server) |
| 跨域支持 | 同主域名子域名 | 任意域名(需 HTTPS) |
| 安全性 | 中(Cookie 明文傳輸) | 高(Ticket 一次性、加密傳輸) |
| 單點登出 | 需自行實現廣播 | 內置支持 |
| 適用場景 | 內部系統、同域子系統、快速原型 | 企業級、異構系統、高安全要求 |
下篇將展示 CAS 完整流程圖與效果演示。