一、session简介
浏览器在请求服务器时,服务器都会创建一个session,session负责浏览器与服务器之间的会话。session的存在是为了维护浏览器和服务器之间交互时保留一些交互数据,例如用户行为或者用户信息等。每个会话都有自己的唯一标识,叫做sessionId。这个流程如下:- 浏览器请求服务器。
- 服务器检查浏览器是否携带sessionId,如果未携带sessionId或者携带一个不存在的sessionId,服务器生成一个session,并将sessionId返回给浏览器。
- 浏览器将sessionId存放在cookie中。
- 浏览器接下来访问该服务器时,就会带着sessionId去访问服务器,服务器都会找到处理该会话的session对象。直到session过期或者session被销毁。
session有自己的过期时间(这个时间是服务气端设置的),如果浏览器长时间未访问服务器,session会自动中断。(session的存在是因为Http协议是一种无状态协议,每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录)。
二、用户身份验证
几乎每一个网站针对不同身份的用户会有不同的呈现,例如:游客、登录用户、管理员。这种情况就需要我们在基于session的基础上再实现一套用户身份的认证。这种身份认证通常是网站在需要用户信息认证时,用户输入用账号,密码来实现的。三、不同session下登录同一账号
用在用不同浏览器访问同一个网站时,登录同一个用户账号,这种情况下就会出现“不同session下重复登录账号”。在现实中的业务中,很多时候,我们是不允许用户在多处登录同一账号的。处理这个需求的思路:
- 每当服务器端生成一个新的session时,将sessionId和session对象分别作为key和value存储起来(可用一个全局的map,也可以用redis存储)。
- 用户登录账号时,检查服务器端的全部session,检查是否有该用户标识的session,如果有,就将之前的session关闭。
- 关闭之前的session后,用户的账号信息(或者能区分用户身份的唯一标识)以参数的形式放在当前的session中。方便下次登录检测。
具体实现
实现一个session管理类。
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MySessionContext {
private static MySessionContext instance;
private HashMap<String,HttpSession> sessionMap;
//创建存储session的map集合
private MySessionContext() {
sessionMap = new HashMap<String,HttpSession>();
}
//获取处理session的实例
public static MySessionContext getInstance() {
if (instance == null) {
instance = new MySessionContext();
}
return instance;
}
//添加一个session到map
public synchronized void addSession(HttpSession session) {
if (session != null) {
sessionMap.put(session.getId(), session);
}
}
//删除一个session到map
public synchronized void delSession(HttpSession session) {
if (session != null) {
sessionMap.remove(session.getId());
}
}
//根据sessionId获取一个session对象
public synchronized HttpSession getSession(String sessionID) {
if (sessionID == null) {
return null;
}
return sessionMap.get(sessionID);
}
//检查是否有存在该用户名的session
public HttpSession checkExist(String username){
Set<Map.Entry<String,HttpSession>> entrySet = sessionMap.entrySet();
for(Map.Entry<String,HttpSession> entry : entrySet){
HttpSession session = entry.getValue();
Object usernameValue = session.getAttribute("usernameKey");
if(usernameValue != null && usernameValue.toString().equals(username)){
return entry.getValue();
}
}
return null;
}
}
实现一个session监听类:
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class SessionListener implements HttpSessionListener {
private MySessionContext myc = MySessionContext.getInstance();
//当浏览器与服务器之间的session被创建时,存储该session。
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
myc.addSession(session);
}
//当session被销毁时,删除该session。
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
myc.delSession(session);
}
}
将该session监听类注册到web.xml的监听器列表:
<listener>
<listener-class>com.msmk.cloud.listener.SessionListener</listener-class>
</listener>
在用户登录验证通过后调用下面这个方法处理session:
/**
* 专门用于处理单点登录的方法,如果用户用同一个账号登录过了,将之前的session销毁。
* @param request
*/
public void handleSession(HttpServletRequest request){
User user = userService.getCurrent();
if(user == null){
return;
}
//作为区分用户的唯一标识。
String usernameKey = "";
//如果用户名不为空
if(!StringUtils.isEmpty(user.getUsername())){
usernameKey = user.getUsername();
}
//如果用户民为null,那就是第三方登录,使用otherId
if(StringUtils.isEmpty(user.getUsername()) && (!StringUtils.isEmpty(user.getOtherUid()))){
usernameKey = user.getOtherUid();
}else{
if(!StringUtils.isEmpty(user.getOpenid())){
usernameKey = user.getOtherUid();
}
}
//获取session管理实例。
MySessionContext myc= MySessionContext.getInstance();
HttpSession httpSession = myc.checkExist(usernameKey);
//如果sessionMap中存在该用户标识的session,将前一个session关闭。
if(httpSession != null){
httpSession.invalidate();
}
//将代表用户的唯一标识,大部分是username,由于第三方登录,有时也是otherId,或者openId
HttpSession sessionCurrent = request.getSession();
sessionCurrent.setAttribute("usernameKey",usernameKey);
}