Redis+Cookie+Jackson+Filter实现单点登录
单点登录实现
封装Cookie
//COOKIE_NAME cookie 的名字
//COOKIE_DOMAIN 要设置的域
//写cookie
public static void writeLoginToken(HttpServletResponse response, String token) {
Cookie ck = new Cookie(COOKIE_NAME, token);
ck.setDomain(COOKIE_DOMAIN);
ck.setPath("/");
ck.setHttpOnly(true);
ck.setMaxAge(60 * 60 * 24 * 365);//如果是-1 代表永久,若果不设置则不写入硬盘,只写入内存,在当前页面有效
log.info("write cookieName:{},cookieValue:{}", ck.getName(), ck.getValue());
response.addCookie(ck);
}
//读cookie
public static String readLoginCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie ck : cookies) {
log.info("read cookieName{},cookievalue{}", ck.getName(), ck.getValue());
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
log.info("return cookieName{},cookievalue{}", ck.getName(), ck.getValue());
return ck.getValue();
}
}
}
return null;
}
//删除cookie
public static void delLoginToken(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie ck : cookies) {
log.info("read cookieName{},cookievalue{}", ck.getName(), ck.getValue());
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
ck.setDomain(COOKIE_DOMAIN);
ck.setPath("/");
ck.setMaxAge(0);
log.info("del cookieName{},cookievalue{}", ck.getName(), ck.getValue());
response.addCookie(ck);
return;
}
}
}
}
domain 和cookie
| X:domain=".xx.com" | cookie |
|---|---|
| a:A.xx.com | cookie:domain=A.xx.com;path="/" |
| b:B.xx.com | cookie:domain=B.xx.com;path="/" |
| c:A.xx.com/test/cc | cookie:domain=A.xx.com;path="/test/cc" |
| d:A.xx.com/test/dd | cookie:domain=A.xx.com;path="/test/dd" |
| e:A.xx.com/test | cookie:domain=A.xx.com;path="/test" |
注意:a、b不能互相拿到对方的cookie,c、d 可以共享a、e的cookie
Redis包装
//exTime seconds
public static String setEx(String key, String value, int exTime) {
Jedis jedis = null;
String result = null;
try {
jedis = RedisPool.getJedis();
result = jedis.setex(key, exTime, value);
} catch (Exception e) {
log.error("setex key:{} value {} error", key, value, e);
RedisPool.returnBrokenResource(jedis);
return result;
}
RedisPool.returnBrokenResource(jedis);
return result;
}
以登录为例实现
@RequestMapping(value = "login.do", method = RequestMethod.POST)
@ResponseBody
public ServerResponse<User> login(String username, String password, HttpSession session, HttpServletResponse httpServletResponse) {
ServerResponse<User> response = iUserService.login(username, password);
if (response.isSuccess()) {
CookieUtil.writeLoginToken(httpServletResponse,session.getId());
RedisPoolUtil.setEx(session.getId(), JsonUtil.obj2str(response.getData()), Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
}
return response;
}
流程:
- 获取session种到cookie中同时存user的json串到redis中
- 对于需要验证登录的操作,首先获取cookie然后拿到token,以此为键获取redis中的数据
session有效期的问题
登录后所有使用用户登录验证的操作都应重新设置session有效期,但是上述功能不能实现session的重置
SessionExpireFilter 重置session有效期
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String loginToken = CookieUtil.readLoginCookie(request);
if (StringUtils.isNotEmpty(loginToken)){
String userJsonStr = RedisPoolUtil.get(loginToken);
User user = JsonUtil.str2obj(userJsonStr,User.class);
if (user != null) {
RedisPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
}
}
filterChain.doFilter(servletRequest,servletResponse);
}
如果有user则重新设置redis的过期时间
总结
- 使用cookie的damain在一级域名下种下cookie
- 使用redis的ex特性存储user信息,键为cookie的value值,值为user对象的json值
- 通过filter来重置session的有效期
- 通过设置cookie的setMaxAge实现cookie存储在本地磁盘
