目的
登录信息共享三个网站(即单点登录)
aaa.chen.com 、bbb.chen.com 、ccc.chen.com
实现自动登录
技术点
1、设置Cookie的路径为setPath("/") .即Tomcat的目录下都有效
2、设置Cookie的域setDomain("chen.com");即aaa.chen.com 、bbb.chen.com 、ccc.chen.com有效。即跨域。
3、设置Cookie的时间。即使用户不选择在几天内自动登录,也应该保存Cookie以保存在当前浏览器没有关闭的情况下有效。
4、使用Filter自动登录。
5、模拟数据库存储加密密码
6、巧妙加解密cookie,防止别人修改过期时间等
实现
结构图
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>CCC站点</title>
</head>
<body>
<p>
在同一台服务器上,多个站点自动登录....>>:<%=session.getId()%></p>
<c:if test="${empty sessionScope.username}">
<form name="f" method="post" action="<c:url value='/login'/>">
Name:<input type="text" name="username" /><br /> Pwd:<input type="text"
name="password" /><br /> <input type="checkbox" name="chk" value="7">一周内自动登录<br />
<input type="submit" value="登录" />
</form>
</c:if>
<c:if test="${not empty sessionScope.username}">
欢迎你:${user}。<a href="<c:url value='/loginout'/>">安全退出</a>
</c:if>
<br /> 相关站点:(只要在一边登录成功,即可以自动登录到另一个程序)
<br />
<a href="http://aaa.chen.com">aaa.chen.com</a>
<br />
<a href="http://bbb.chen.com">bbb.chen.com</a>
<br />
</body>
</html>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>SSObyCookie</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>AutoLoginFilter</filter-name>
<filter-class>com.chen.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
AutoLoginFilter.java
package com.chen.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.chen.util.Util;
public class AutoLoginFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)request;
HttpServletResponse resp=(HttpServletResponse)response;
String username = (String) req.getSession().getAttribute("username");
if(username==null){
Cookie[] cookies = req.getCookies();
if(cookies!=null){
for (Cookie cookie : cookies) {
String name = cookie.getName();
if(name.equals("autologin")){
String encodeCookie = cookie.getValue();
if(Util.loginByCookie(encodeCookie)){//cookie是否有效
req.getSession().setAttribute("username", name);
}
}
}
}
}
chain.doFilter(req, resp);
}
@Override
public void destroy() {
}
}
LoginServlet.java
package com.chen.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.chen.db.MyDB;
import com.chen.util.Util;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String forward="/index.jsp";
String username = req.getParameter("username");
String password = req.getParameter("password");
String chk = req.getParameter("chk");
System.out.println("chk:"+chk);
if(username!=null&&!username.trim().equals("")&&password!=null&&!password.trim().equals("")){
//先加密密码
String MD5password = Util.EncodePassword(password);
//模拟从数据库中获取加密密码
String pwd = MyDB.getInstance().getMap().get(username);
if(MD5password.equals(pwd)){
req.getSession().setAttribute("username", username);
int time = 1*60*60*24*7;//7天
long timeLong=System.currentTimeMillis()+time;
String encodeCookie = Util.EncodeCookie(username, MD5password, String.valueOf(timeLong));
//即使没有选择自动登录,也要在浏览器关闭前这段时间使用cookie
Cookie cookie = Util.setCookie(encodeCookie, null);
if(chk!=null){
cookie.setMaxAge(time);//选择了自动登录
resp.addCookie(cookie);
}
}
}
//这里因为登陆页和成功登陆是同一个页面,所以没有改变forward的值
req.getRequestDispatcher(forward).forward(req, resp);
}
}
MyDB.java
package com.chen.db;
import java.util.HashMap;
import java.util.Map;
public class MyDB {
private static MyDB mydb;
private Map<String,String> map=new HashMap<>();
{
map.put("zhangsang", "117ec018fe460940b50ceb6fa053e879");//123
map.put("xiaoli", "01d7d30a3c7b731636c076e3fb12fd9c");//456
}
private MyDB(){}
public static MyDB getInstance(){
if(mydb==null){
mydb=new MyDB();
}
return mydb;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
Util.java
package com.chen.util;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.Cookie;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import com.chen.db.MyDB;
import sun.misc.BASE64Encoder;
public class Util {
private static String key = "4836";
// 加密的cookie信息
public static String EncodeCookie(String username, String MD5Password, String expiryTime) {
String str = username + ":" + expiryTime + ":" + MD5Password + ":" + Util.key;
String signatureValue = DigestUtils.md5Hex(str.getBytes());
String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
String cookie = new String(Base64.encodeBase64(tokenValue.getBytes()));
return cookie;
}
// 加密的密码
public static String EncodePassword(String password) {
String str = password + Util.key;
String MD5Password = DigestUtils.md5Hex(str.getBytes());
return MD5Password;
}
// 解密cookie
public static Map DecodeCookie(String encodeCookie) {
Map<String, String> map = null;
byte[] bytes = Base64.decodeBase64(encodeCookie);
String decodeCookie = new String(bytes);
String[] split = decodeCookie.split(":");
if (split != null && split.length == 3) {
map = new HashMap<>();
map.put("username", split[0]);
map.put("expiryTime", split[1]);
map.put("signatureValue", split[2]);
}
return map;
}
// cookie是否有效
public static boolean loginByCookie(String encodeCookie) {
if (encodeCookie != null && !"".equals(encodeCookie)) {
Map map = DecodeCookie(encodeCookie);
if (map != null) {
String username = (String) map.get("username");
String expiryTime = (String) map.get("expiryTime");
String signatureValue = (String) map.get("signatureValue");
long time = Long.valueOf(expiryTime);
long currentTimeMillis = System.currentTimeMillis();
if (time > currentTimeMillis) { //解密cookie中带上expiryTime是为了防止别人修改cookie的时间
String str = username + ":" + expiryTime + ":" + MyDB.getInstance().getMap().get(username) + ":"
+ Util.key;
String signatureValueCurrent = DigestUtils.md5Hex(str.getBytes());
if (signatureValue.equals(signatureValueCurrent)) {
return true;
}
}
}
}
return false;
}
public static Cookie setCookie(String encodeCookie,Integer time){
Cookie cookie = new Cookie("autologin", encodeCookie);
cookie.setPath("/");
cookie.setDomain("chen.com");
if(time!=null){
cookie.setMaxAge(time); //这个是为了让浏览器决定带不带cookie,不安全,可以被人修改,所以在解密cookie中还带了expiryTime
}
return cookie;
}
}
Logout.java
package com.chen.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.chen.util.Util;
@WebServlet("/loginout")
public class Logout extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
HttpSession session = req.getSession();
//这里存在一个问题
//同一浏览器访问同一台服务器上的不同站点用的是不同的session
//这里只移除了某一个网站的session属性
//其他的网站的session属性还保留着,只要浏览器没有关闭,照样可以访问资源
session.removeAttribute("username");
Cookie cookie = Util.setCookie("", 0);
resp.addCookie(cookie);
resp.sendRedirect(req.getContextPath()+"/index.jsp");
}
}
部署参考:同一台Tomcat的多域名绑定