技术点:
1、设置Cookie的路径为setPath("/").即Tomcat的目录下都有效
2、设置Cookie的域setDomain(".itcast.com");即bbs.itcast.com,或是mail.itcast.com有效。即跨域。
3、设置Cookie的时间。即使用户不选择在几天内自动登录,也应该保存Cookie以保存在当前浏览器没有关闭的情况下有效。
4、使用Filter自动登录。
实现步骤
1:首先要准备出几个虚拟主机并配置hosts文件,即本机DNS修改本机的C:\Windows\System32\drivers\etc下的hosts文件。
<span style="font-size:18px;"><span style="font-size:18px;"># localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost 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.news.com 127.0.0.1 www.bbs.com 127.0.0.1 www.server.com </span></span>
增加几个Host节点,通过Cookie实现自动登录,必须配置的虚拟主页满足xxx.itcast.cn,即主域名必须保持一致。
一般web应用中一般部署在web.xml文件中,单点退出相关配置如下:
<span style="font-size:18px;"><span style="font-size:18px;"><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>http://www.server.com:8080/login</param-value> </init-param> <init-param> <!--我是谁--> <param-name>serverName</param-name> <param-value>http://www.bbs.com:8080</param-value> </init-param> <init-param> <param-name>renew</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>gateway</param-name> <param-value>false</param-value> </init-param> </filter> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://www.server.com:8080</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://www.bbs.com:8080</param-value> </init-param> <!-- <init-param> <param-name>proxyCallbackUrl</param-name> <param-value>https://localhost:8443/mywebapp/proxyCallback</param-value> </init-param> <init-param> <param-name>proxyReceptorUrl</param-name> <param-value>/mywebapp/proxyCallback</param-value> </init-param> --> </filter> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <!-- ************************* --> <!-- Sign out not yet implemented --> <!-- <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> --> <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> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/proxyCallback</url-pattern> </filter-mapping> <!-- *********************** --> <!-- Sign out not yet implemented --> <!-- <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> --> <!-- *********************** --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list></span></span>
说明:我们看到单点退出的相关类结构,web.xml配置了单点退出的相关类(1个监听器SingleSignOutHttpSessionListener,2个过滤器SingleSignOutFilter,SimpleServerLogoutHandler)。
实现利用了session存储机制,SessionStoreManager是个单例类,用于管理session的存储、删除;SessionMappingStorage是session的存储、删除的执行者,可以看到实际存储的结构是一个artifactId、sessionId为名值对的HashMap表;监听器SingleSignOutHttpSessionListener的作用是session销毁时,调用session管理单例类SessionStoreManager进行session的删除销毁;
SingleSignOutFilter的作用有2个:一个是在单点访问拦截安全资源时调用单例类SessionStoreManager存储session,另一个是在单点退出时调用单例类SessionStoreManager删除session;SimpleServerLogoutHandler的作用是将客户端的退出请求转发到SSO服务器端,集中处理做各个子系统的单点退出。
<span style="font-size:18px;"><span style="font-size:18px;">1、登录的主页如下: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> </head> <body> <p>在同一台服务器上,多个站点自动登录....>>:<%=session.getId()%></p> <c:if test="${empty sessionScope.user}"> <form name="f" method="post" action="<c:url value='/login'/>"> Name:<input type="text" name="name"/><br/> Pwd:<input type="text" name="pwd"/><br/> <input type="checkbox" name="chk" value="7">一周内自动登录<br/> <input type="submit" value="登录"/> </form> </c:if> <c:if test="${not empty sessionScope.user}"> 欢迎你:${user}。<a href="<c:url value='/loginout'/>">安全退出</a> </c:if> <br/> 相关站点:(只要在一边登录成功,即可以自动登录到另一个程序)<br/> <a href="http://mail.itcast.com:7777">mail.itcast.com</a><br/> <a href="http://bbs.itcast.com:7777">bbs.itcast.com</a><br/> </body> </html> </span></span>
<span style="font-size:18px;"><span style="font-size:18px;">/** * 用户登录 */ public class LoginServlet extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String nm = req.getParameter("name"); String pwd = req.getParameter("pwd"); String chk = req.getParameter("chk"); //是否选中了7天自动登录 String forward = "/index.jsp"; if(nm!=null && !nm.trim().equals("") && nm.startsWith("it")//用户名是it开始,且密码是pwd开始的可以登录 && pwd !=null && !pwd.trim().equals("") && pwd.startsWith("pwd")){ System.err.println("登录成功。。。。。"); forward = "/jsps/welcome.jsp"; //无论如何,都要设置cookie,如果没有选择自动登录,则只在当前页面的跳转时有效,否则设置有效期间为7天。 Cookie cookie = new Cookie("autologin",nm+"@"+pwd); cookie.setPath("/"); //如果路径为/则为整个tomcat目录有用 cookie.setDomain(".itcast.com"); //设置对所有*.itcast.com为后缀的域名效 if(chk!=null){ int time = 1*60*60*24*7; //1秒*60=1分*60分=1小时*24=1天*7=7天 cookie.setMaxAge(time); } resp.addCookie(cookie); req.getSession().setAttribute("user", nm); }else{ System.err.println("登录不成功。。。。。。"); } req.getRequestDispatcher(forward).forward(req, resp); } } </span></span>
<span style="font-size:18px;"><span style="font-size:18px;">/** * 自动登录 */ public class AutoLogin implements Filter { public void destroy() {} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { System.err.println("开始自动登录验证.....");//此类中应该对登录的servlet直接放行。根据判断url决定。 HttpServletRequest requ = (HttpServletRequest) req; HttpSession s = requ.getSession(); if (s.getAttribute("user") != null) {//如果用户已经登录则直接放行 System.err.println("用户已经登录,没有必须要再做自动登录。。。。"); } else { Cookie[] cookies = requ.getCookies(); if (cookies != null) { for (Cookie ck : cookies) { if (ck.getName().equals("autologin")) {// 是否是自动登录。。。。 System.err.println("自动登录成功。。。。。"); String val = ck.getValue(); String[] vals = val.split("@"); s.setAttribute("user", vals[0]); } } } } chain.doFilter(req, resp); } public void init(FilterConfig filterConfig) throws ServletException {} } </span></span>
<span style="font-size:18px;"><span style="font-size:18px;">/** * 安全退出删除Cookie */ public class LoginOutServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession s = req.getSession(); //获取Session Cookie cookie = new Cookie("autologin","");//必须声明一个完全相同名称的Cookie cookie.setPath("/");//路径也要完全相同 cookie.setDomain(".itcast.com");//域也要完全相同 cookie.setMaxAge(0);//设置时间为0,以直接删除Cookie resp.addCookie(cookie); s.removeAttribute("user"); System.err.println("安全退出。。。。。"); resp.sendRedirect(req.getContextPath()+"/index.jsp"); } } </span></span>
这种是基于最简单的方式实现的单点登录,效果图在下篇博客中的
使用基于CAS单点登录流程实例与效果图
下下篇
基于使用Shiro 和使用Spring security控制权限的方案