前言
通过Session实现登陆保持的原理很简单,保存在服务端中的Session可以保存用户名和id等一些信息,在用户登陆成功后,将这些信息保存在Session中即可,服务端会自动在创建session的时候将该session的id通过响应头的方式返回给客户端。客户端在下次请求的时候就会自动带上该sessionId(cookie名未JSSSIONID),服务端就会通过该id找到保存在服务端的seesion,取得该用户的id来识别用户并判断该用户是否登陆成功。
效果
服务端代码
import com.test.common.PJCommon;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/sLoginDemo")
public class SloginController {
/***
* 测试登录
* 这里假设用户(userName=admin,password=1111)
*/
@RequestMapping(value = "/login.do", method = RequestMethod.POST)
@ResponseBody
public Map login(HttpServletRequest request, HttpServletResponse response) {
Map<String,Object> retMap = new HashMap<String,Object>();
Map<String,String> paramMap = PJCommon.getRequestParamMapAndSessionInfo(request);
try {
String account = paramMap.get("account").toString(); // 获取account参数
String password = paramMap.get("password").toString(); // 获取password参数
/*
* 获取或者创建Session
* ajax请求会自动带上名为"JSESSIONID"的cookie(如果客户端有这个cookie的话),该值就是服务端创建session后,返回客户端的seesionId
* getSession会根据这个id查询服务端是否有这个session,如果有的话就会获取该session,如果没有的话就会新建一个新的session
* Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。
* 用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。
* */
HttpSession session = request.getSession(); // 获取Session对象
Object personObj = session.getAttribute("person");
// 判断personObj是否存在,如果存在则不是新建的session,直接返回账户信息即可
if (personObj != null) {
retMap.put("state", 0);
retMap.put("person", personObj);
retMap.put("message", "session登陆成功");
return retMap;
}
// 如果不存在,则是新建的session,需要重新验证账号密码
if (!"admin".equals(account) || !"1111".equals(password)) {
session.invalidate(); // 作废session
// 账号密码校验不通过
retMap.put("state", -1);
retMap.put("message", "账号/密码错误");
return retMap;
}
// 验证通过
Map personInfo = new HashMap();
personInfo.put("name", "huzhenv5");
personInfo.put("age", "18");
session.setAttribute("person", personInfo); // 设置Session中的属性
// 设置Session维持时间,单位是秒(0或者负数表示session永远不超时)
// spring boot 可以在配置文件中配置默认的session超时时间
// 通过以下代码设置session超时时间,优先级更高
session.setMaxInactiveInterval(10);
retMap.put("state", 0);
retMap.put("person", personInfo);
retMap.put("message", "账号/密码登陆成功");
} catch (Exception e) {
e.printStackTrace();
retMap.put("state", -2);
retMap.put("message", "登陆失败");
}
return retMap;
}
/***
* 注销登录
*/
@RequestMapping(value = "/logout.do", method = RequestMethod.POST)
@ResponseBody
public Map logout(HttpServletRequest request, HttpServletResponse response) {
Map<String,Object> retMap = new HashMap<String,Object>();
Map<String,String> paramMap = PJCommon.getRequestParamMapAndSessionInfo(request);
try {
HttpSession session = request.getSession(); // 获取Session对象
session.invalidate(); // 作废session
// 注销成功
retMap.put("state", 0);
retMap.put("message", "注销成功");
} catch (Exception e) {
e.printStackTrace();
retMap.put("state", -2);
retMap.put("message", "注销失败");
}
return retMap;
}
}
前端登陆页脚本
// 页面初始化方法
$(document).ready(function(){
// login(false);
});
function doLogin() {
login(true);
}
/**
* 登陆方法
* @param showAlter 失败是否报错
* */
function login(showAlter) {
var account = $('#accountInput').val();
var ps = $('#psInput').val();
// 请求
$.ajax({
type : 'POST',
data: {
account: account,
password: ps
},
dataType: 'json',
url : "../../sLoginDemo/login.do",
success: function(res) {
if (res.state == 0) {
console.log(res.message);
location.href = '../sMain/sMain.html'
} else {
showAlter && alert('失败:' + res.message);
}
},
error: function(err) {
showAlter && alert('错误:' + err.message);
}
})
}
前端注销页脚本
function logout() {
$.ajax({
type : 'POST',
dataType: 'json',
url : "../../sLoginDemo/logout.do",
success: function(res) {
if (res.state == 0) {
location.href = '../sLogin/sLogin.html'
} else {
alert('失败:' + res.message);
}
},
error: function(err) {
alert('错误:' + err.message);
}
})
}
和通过Cookie方式实现的比较
Cookie优点:
- 不占用服务端内存
- 分布式的时候不用考虑session无法共享的问题
- 由于验证信息保存在客户端,可以将登陆状态保持较长时间
Cookie缺点:
- 每次请求Cookie内容较多,增请求量
- 如果存储内容过多,容易超过浏览器对Cookie大小的限制
- 每次请求均需要进行安全验证
Session优点:
- 客户端发送的Cookie仅有一个JSESSIONID,该部分请求量小
- 用户信息保存在服务端,可以直接取用,不用每次请求都进行验证
Session缺点:
- 高并发情况,占用大量的服务端内存
- 分布式的时候session不能共享
- 不通过其它方式辅助的话,由于session占用服务端内存,登陆状态无法保持较长时间
Git
该工程通过spring boot实现,完整的工程可以访问笔者的GitHub
GitHub链接:https://github.com/huzhen-v5/spring-boot-swagger
分支名:logindemo