shiro我就不多介绍了,我的方案是重写 shiro的sessionDAO,把session存储到redis上,直接上代码
一、spring中配置
首先是securityManager,由于是web项目,使用的是shiro自带的DefaultWebSecurityManager,这里没有必要注入cacheManager,通过查看源码可知,在这里配的cacheManager最终会被set到sessionDAO中,由于我们要自己写sessionDAO,所以没必要,很多人没有看源码,以为在这儿配了就能用,其实不然,要使得cacheManager生效,你接下来配置的sessionManager和sessionDAO都必须实现CacheManagerAware接口才行,sessionMode这个属性,已经被shiro废弃,后续shiro版本可能就没了,这里的sessionMode属性影响是很大的,配置不同会影响sessionManager是否被重置,我们要配自己的sessionDAO,所有绝对是不用配该属性的
接下来是sessionManager了
sessionManager使用shiro的DefaultWebSessionManager,设置了session的超时时间,配置自己写的session,重点要说的时这个sessionIdCookie.name,shiro默认是通过cookie保存的sessionId,cookie的key默认为JSESSIONID,例如:JSESSIONID=5575866c-96e5-4c34-9b3c-ebe2c07fd423,而tomcat或者其他的一些用到的框架可能会重写这个键值对,因为JSESSIONID这个名字实在是太通用了,一旦被复写,shiro自然就找不到sessionid对应的session了,这会导致一连串的问题,比如用着用着跳到登录页面,获取session中存好的参数获取不到等,为了避免这个问题,配置了cookie的name
sessionDAO配置及redisManager配置
redisManager主要用来访问redis的,当然也可以用spring提供的redis的template,我反正是没用成功,有兴趣的朋友试下
其他shiro配置
/login = anon
/captcha.png = anon
/toLogin = anon
/logout = logout
/static/** = anon
/weixin/** = anon
/cxf/** = anon
/api/oauth2/**=anon
/api/** = anon ,tkf
/** = authc
二、java代码
public class RedisSessionDAO extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
private RedisManager redisManager;
/**
* The Redis key prefix for the sessions
*/
private String keyPrefix = "shiro_redis_session:";
@Override
public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}
/**
* save session
* @param session
* @throws UnknownSessionException
*/
private void saveSession(Session session) throws UnknownSessionException{
if(session == null || session.getId() == null){
logger.error("session or session id is null");
return;
}
byte[] key = getByteKey(session.getId());
byte[] value = SerializeUtils.serialize(session);
session.setTimeout(redisManager.getExpire()*1000);
this.redisManager.set(key, value, redisManager.getExpire());
}
@Override
public void delete(Session session) {
if(session == null || session.getId() == null){
logger.error("session or session id is null");
return;
}
redisManager.del(this.getByteKey(session.getId()));
}
@Override
public Collection getActiveSessions() {
Set sessions = new HashSet();
Set keys = redisManager.keys(this.keyPrefix + "*");
if(keys != null && keys.size()>0){
for(byte[] key:keys){
Session s = (Session)SerializeUtils.deserialize(redisManager.get(key));
sessions.add(s);
}
}
return sessions;
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if(sessionId == null){
logger.error("session id is null");
return null;
}
Session s = (Session)SerializeUtils.deserialize(redisManager.get(this.getByteKey(sessionId)));
return s;
}
/**
* 获得byte[]型的key
* @param key
* @return
*/
private byte[] getByteKey(Serializable sessionId){
String preKey = this.keyPrefix + sessionId;
return preKey.getBytes();
}
public RedisManager getRedisManager() {
return redisManager;
}
public void setRedisManager(RedisManager redisManager) {
this.redisManager = redisManager;
/**
* 初始化redisManager
*/
this.redisManager.init();
}
/**
* Returns the Redis session keys
* prefix.
* @return The prefix
*/
public String getKeyPrefix() {
return keyPrefix;
}
/**
* Sets the Redis sessions key
* prefix.
* @param keyPrefix The prefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}
在保存session时,每次都重新设置了session的过期时间,按理说应该不需要,为了保险吧
public class RedisManager {
private String host = "127.0.0.1";
private int port = 6379;
// 0 - never expire
private int expire = 0;
//timeout for jedis try to connect to redis server, not expire time! In milliseconds
private int timeout = 0;
private String password = "";
private static JedisPool jedisPool = null;
public RedisManager(){
}
/**
* 初始化方法
*/
public void init(){
if(jedisPool == null){
if(password != null && !"".equals(password)){
jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password);
}else if(timeout != 0){
jedisPool = new JedisPool(new JedisPoolConfig(), host, port,timeout);
}else{
jedisPool = new JedisPool(new JedisPoolConfig(), host, port);
}
}
}
/**
* get value from redis
* @param key
* @return
*/
public byte[] get(byte[] key){
byte[] value = null;
Jedis jedis = jedisPool.getResource();
try{
value = jedis.get(key);
}finally{
//jedisPool.returnResource(jedis);该方法会导致高并发访问时,报[B cannot be cast to java.lang.Long]异常
jedis.close();
}
return value;
}
/**
* set
* @param key
* @param value
* @return
*/
public byte[] set(byte[] key,byte[] value){
Jedis jedis = jedisPool.getResource();
try{
jedis.set(key,value);
if(this.expire != 0){
jedis.expire(key, this.expire);
}
}finally{
jedis.close();
}
return value;
}
/**
* set
* @param key
* @param value
* @param expire
* @return
*/
public byte[] set(byte[] key,byte[] value,int expire){
Jedis jedis = jedisPool.getResource();
try{
jedis.set(key,value);
if(expire != 0){
jedis.expire(key, expire);
}
}finally{
jedis.close();
}
return value;
}
/**
* del
* @param key
*/
public void del(byte[] key){
Jedis jedis = jedisPool.getResource();
try{
jedis.del(key);
}finally{
jedis.close();
}
}
/**
* flush
*/
public void flushDB(){
Jedis jedis = jedisPool.getResource();
try{
jedis.flushDB();
}finally{
jedis.close();
}
}
/**
* size
*/
public Long dbSize(){
Long dbSize = 0L;
Jedis jedis = jedisPool.getResource();
try{
dbSize = jedis.dbSize();
}finally{
jedis.close();
}
return dbSize;
}
/**
* keys
* @param regex
* @return
*/
public Set keys(String pattern){
Set keys = null;
Jedis jedis = jedisPool.getResource();
try{
keys = jedis.keys(pattern.getBytes());
}finally{
jedis.close();
}
return keys;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getExpire() {
return expire;
}
public void setExpire(int expire) {
this.expire = expire;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
序列化用的util类
public class SerializeUtils {
private static Logger logger = LoggerFactory.getLogger(SerializeUtils.class);
/**
* 反序列化
* @param bytes
* @return
*/
public static Object deserialize(byte[] bytes) {
Object result = null;
if (isEmpty(bytes)) {
return null;
}
try {
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteStream);
try {
result = objectInputStream.readObject();
}
catch (ClassNotFoundException ex) {
throw new Exception("Failed to deserialize object type", ex);
}
}
catch (Throwable ex) {
throw new Exception("Failed to deserialize", ex);
}
} catch (Exception e) {
logger.error("Failed to deserialize",e);
}
return result;
}
public static boolean isEmpty(byte[] data) {
return (data == null || data.length == 0);
}
/**
* 序列化
* @param object
* @return
*/
public static byte[] serialize(Object object) {
byte[] result = null;
if (object == null) {
return new byte[0];
}
try {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(128);
try {
if (!(object instanceof Serializable)) {
throw new IllegalArgumentException(SerializeUtils.class.getSimpleName() + " requires a Serializable payload " +
"but received an object of type [" + object.getClass().getName() + "]");
}
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream);
objectOutputStream.writeObject(object);
objectOutputStream.flush();
result = byteStream.toByteArray();
}
catch (Throwable ex) {
throw new Exception("Failed to serialize", ex);
}
} catch (Exception ex) {
logger.error("Failed to serialize",ex);
}
return result;
}
}