Tomcat+nginx集群后,登录的问题就暴露了出来,通过nginx负载均衡后,用户第一次访问转发到TomcatA上,用户登录之后,在此访问又转发到TomcatB上,而TomcatB上并没有用户登录的session信息,所以又一次提示用户需要登录,本次通过cookie+Redis实现单点登录解决Tomcat集群后带来的登录问题。
Tomcat+nginx集群配置:https://blog.csdn.net/qq_37585236/article/details/81590588
Redis安装;https://blog.csdn.net/qq_37585236/article/details/81460046
POM依赖:
redis.clients
jedis
2.6.0
获取properties配置文件值:
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
/**
* Created by geely
*/
@Slf4j
public class PropertiesUtil {
private static Properties prop;
static {
String fileName = "properties配置文件";
prop = new Properties();
try {
//加载Properties
prop.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName), "UTF-8"));
} catch (IOException e) {
log.error("初始化配置失败", e);
}
}
public static String getKey(String key) {
String value = prop.getProperty(key.trim());
if (value == null) {
return null;
}
return value.trim();
}
public static String getKey(String key, String defaultValue) {
String value = prop.getProperty(key.trim());
if (value == null) {
return defaultValue;
}
return value.trim();
}
}
import com.mmall.util.PropertiesUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author Luyue
* @date 2018/8/11 14:29
**/
public class JedisPool {
private static redis.clients.jedis.JedisPool pool;
//redis ip 地址
private static String ip = PropertiesUtil.getKey("redis2.ip");
//redis 端口号
private static Integer port = Integer.parseInt(PropertiesUtil.getKey("redis2.port", "6379"));
//最大连接数
private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getKey("redis.max.total", "20"));
//最大空闲连接数
private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getKey("redis.max.idle", "10"));
//最小空闲连接数
private static Integer minIdle = Integer.parseInt(PropertiesUtil.getKey("redis.min.idle", "2"));
//通过连接池拿去jedis连接时,校验并返回可用连接
private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getKey("redis.test.borrow", "true"));
//通过连接池返还jedis连接时,校验该连接
private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getKey("redis.test.return", "true"));
/**
* 初始化连接池
*/
private static void initPool() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setTestOnBorrow(testOnBorrow);
jedisPoolConfig.setTestOnReturn(testOnReturn);
//当连接池无空闲连接时,是否阻塞
jedisPoolConfig.setBlockWhenExhausted(true);
pool = new redis.clients.jedis.JedisPool(jedisPoolConfig, ip, port, 1000*2);
}
static {
initPool();
}
/**
* 获取一个连接
* @return
*/
public static Jedis getJedis() {
return pool.getResource();
}
/**
* 返还错误的连接
* @param jedis
*/
public static void returnBrokenJedis(Jedis jedis) {
pool.returnBrokenResource(jedis);
}
/**
* 返还连接
* @param jedis
*/
public static void returnJedis(Jedis jedis) {
pool.returnResource(jedis);
}
}
import com.mmall.common.JedisPool;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
/**
* @author Luyue
* @date 2018/8/11 14:29
**/
@Slf4j
public class JedisPoolUtil {
/**
* 存储键值
* @param key
* @param value
* @return
*/
public static String set(String key, String value) {
Jedis jedis = null;
String response = null;
try {
jedis = JedisPool.getJedis();
response = jedis.set(key, value);
} catch (Exception e) {
log.error("set key:{}, value:{} is error:{}", key, value, e);
JedisPool.returnBrokenJedis(jedis);
return response;
}
JedisPool.returnJedis(jedis);
return response;
}
/**
* 存储键值,并设置有效时间
* @param key
* @param value
* @param exTime
* @return
*/
public static String setEx(String key, String value, int exTime) {
Jedis jedis = null;
String response = null;
try {
jedis = JedisPool.getJedis();
response = jedis.setex(key, exTime, value);
} catch (Exception e) {
log.error("setEx key:{}, value:{} is error:{}", key, value, e);
JedisPool.returnBrokenJedis(jedis);
return response;
}
JedisPool.returnJedis(jedis);
return response;
}
/**
* 获取value
* @param key
* @return
*/
public static String get(String key) {
Jedis jedis = null;
String response = null;
try {
jedis = JedisPool.getJedis();
response = jedis.get(key);
} catch (Exception e) {
log.error("get key:{}, is error:{}", key, e);
JedisPool.returnBrokenJedis(jedis);
return response;
}
JedisPool.returnJedis(jedis);
return response;
}
/**
* 设置键的有效时间
* @param key
* @param exTime
* @return
*/
public static Long expire(String key, int exTime) {
Jedis jedis = null;
Long response = null;
try {
jedis = JedisPool.getJedis();
response = jedis.expire(key, exTime);
} catch (Exception e) {
log.error("expire key:{}, is error:{}", key, e);
JedisPool.returnBrokenJedis(jedis);
return response;
}
JedisPool.returnJedis(jedis);
return response;
}
/**
* 删除键
* @param key
* @return
*/
public static Long del(String key) {
Jedis jedis = null;
Long response = null;
try {
jedis = JedisPool.getJedis();
response = jedis.del(key);
} catch (Exception e) {
log.error("del key:{}, is error:{}", key, e);
JedisPool.returnBrokenJedis(jedis);
return response;
}
JedisPool.returnJedis(jedis);
return response;
}
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Luyue
* @date 2018/8/12 14:40
**/
@Slf4j
public class CookieUtil {
/**
*tomcat 8.5的用法,tomcat8.5以下:.luyue.com
*/
private static final String COOKIE_DOMAIN = "luyue.com";
private static final String COOKIE_NAME = "login_token";
/**
* 种入cookie
* @param token
* @param response
*/
public static void writeCookie(String token, HttpServletResponse response) {
Cookie cookie = new Cookie(COOKIE_NAME, token);
cookie.setDomain(COOKIE_DOMAIN);
cookie.setPath("/");
//如果设置-1 , 代表cookie 永久有效
cookie.setMaxAge(60 * 60 * 24 * 365);
cookie.setHttpOnly(true);
log.info("write cookie name:{} value:{}", cookie.getName(), cookie.getValue());
response.addCookie(cookie);
}
/**
* 获取cookie
* @param request
* @return
*/
public static String readCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return null;
}
for (Cookie c : cookies) {
if (StringUtils.equals(c.getName(), COOKIE_NAME)) {
log.info("read cookie name:{} value:{}", c.getName(), c.getValue());
return c.getValue();
}
}
return null;
}
/**
* 删除cookie
* @param request
* @param response
*/
public static void delCookie(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c: cookies) {
if (StringUtils.equals(c.getName(), COOKIE_NAME)) {
log.info("del cookie name:{} value:{}", c.getName(), c.getValue());
c.setDomain(COOKIE_DOMAIN);
c.setPath("/");
c.setMaxAge(0);
response.addCookie(c);
break;
}
}
}
}
}
public ServerResponse login(String username, String password, HttpSession session, HttpServletResponse servletResponse) {
ServerResponse response = iUserService.login(username, password);
//校验用户名密码
if (response.isSuccess()) {
//Redis的key值存储在cookie中,这样每次判断用户是否登录只需要从cookie中获取key值,在从Redis取得user就行
CookieUtil.writeCookie(session.getId(), servletResponse);
//将登录用户的信息存储到Redis中
JedisPoolUtil.setEx(session.getId(), JsonUtil.objToJson(response.getData()), Constants.RedisExTime.EX_TIME);
}
return response;
}
这样,每次判断用户是否登录,只需判断cookie中是否有相关token,没有则表示没有登录,有则继续判断Redis中是否可以取得value:
public ServerResponse getUserInfo(HttpServletRequest request) {
String token = CookieUtil.readCookie(request);
User user = JsonUtil.json2Object(ShardedJedisPoolUtil.get(token), User.class);
if (user == null) {
return ServerResponse.createByErrorMessage("请登录");
}
return ServerResponse.createBySuccess(user);
}