實際項目中用shiro做權限與登錄驗證,並做單點登錄,單點登錄克服有瀏覽器cookie的sessionID的統一與redis緩存的sessionID統一 也就是所有的sessionID要一致。
怕忘記並記錄下,實際項目redis采用集群。
一、shiro緩存為redis毋庸置疑
1、RedisConfig

package com.sso.common.redis; import com.sso.common.redis.shiro.FastJsonRedisSerializer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Component; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPoolConfig; import java.util.HashSet; import java.util.Set; @Configuration @Component public class RedisConfig { @Value("${spring.redis.pool.max-idle}") private Integer maxIdle; // @Value("${spring.redis.}") // private Integer maxTotal; @Value("${spring.redis.pool.max-wait}") private Integer maxWaitMillis; // @Value("${redis.minEvictableIdleTimeMillis}") // private Integer minEvictableIdleTimeMillis; // @Value("${redis.numTestsPerEvictionRun}") // private Integer numTestsPerEvictionRun; // @Value("${redis.timeBetweenEvictionRunsMillis}") // private long timeBetweenEvictionRunsMillis; // @Value("${redis.testOnBorrow}") // private boolean testOnBorrow; // @Value("${redis.testWhileIdle}") // private boolean testWhileIdle; @Value("${spring.redis.cluster.nodes}") private String clusterNodes; @Value("${spring.redis.cluster.max-redirects}") private Integer mmaxRedirectsac; /** * JedisPoolConfig 連接池 * * @return */ @Bean public JedisPoolConfig jedisPoolConfig() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // 最大空閑數 jedisPoolConfig.setMaxIdle(maxIdle); // 連接池的最大數據庫連接數 //jedisPoolConfig.setMaxTotal(maxTotal); // 最大建立連接等待時間 jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); // 逐出連接的最小空閑時間 默認1800000毫秒(30分鍾) //jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 每次逐出檢查時 逐出的最大數目 如果為負數就是 : 1/abs(n), 默認3 //jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun); // 逐出掃描的時間間隔(毫秒) 如果為負數,則不運行逐出線程, 默認-1 //jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 是否在從池中取出連接前進行檢驗,如果檢驗失敗,則從池中去除連接並嘗試取出另一個 //jedisPoolConfig.setTestOnBorrow(testOnBorrow); // 在空閑時檢查有效性, 默認false //jedisPoolConfig.setTestWhileIdle(testWhileIdle); return jedisPoolConfig; } /** * Redis集群的配置 * * @return RedisClusterConfiguration * @throws * @autor lpl * @date 2017年12月22日 */ @Bean public RedisClusterConfiguration redisClusterConfiguration() { RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); //Set<RedisNode> clusterNodes String[] serverArray = clusterNodes.split(","); Set<RedisNode> nodes = new HashSet<RedisNode>(); for (String ipPort : serverArray) { String[] ipAndPort = ipPort.split(":"); nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1]))); } redisClusterConfiguration.setClusterNodes(nodes); redisClusterConfiguration.setMaxRedirects(mmaxRedirectsac); return redisClusterConfiguration; } /** * 配置工廠 * * @param @param jedisPoolConfig * @param @return * @return JedisConnectionFactory * @throws * @Title: JedisConnectionFactory * @autor lpl * @date 2017年12月22日 */ @Bean public JedisConnectionFactory JedisConnectionFactory(JedisPoolConfig jedisPoolConfig, RedisClusterConfiguration redisClusterConfiguration) { JedisConnectionFactory JedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig); return JedisConnectionFactory; } @Bean public JedisCluster jedisCluster(JedisConnectionFactory JedisConnectionFactory) { JedisCluster jedisCluster = (JedisCluster)JedisConnectionFactory.getConnection().getNativeConnection(); return jedisCluster; } @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); // 使用fastjson序列化 FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer); // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } }
2、RedisUtil

package com.sso.common.redis; import com.sso.common.redis.shiro.FastJsonRedisSerializer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Component; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPoolConfig; import java.util.HashSet; import java.util.Set; @Configuration @Component public class RedisConfig { @Value("${spring.redis.pool.max-idle}") private Integer maxIdle; // @Value("${spring.redis.}") // private Integer maxTotal; @Value("${spring.redis.pool.max-wait}") private Integer maxWaitMillis; // @Value("${redis.minEvictableIdleTimeMillis}") // private Integer minEvictableIdleTimeMillis; // @Value("${redis.numTestsPerEvictionRun}") // private Integer numTestsPerEvictionRun; // @Value("${redis.timeBetweenEvictionRunsMillis}") // private long timeBetweenEvictionRunsMillis; // @Value("${redis.testOnBorrow}") // private boolean testOnBorrow; // @Value("${redis.testWhileIdle}") // private boolean testWhileIdle; @Value("${spring.redis.cluster.nodes}") private String clusterNodes; @Value("${spring.redis.cluster.max-redirects}") private Integer mmaxRedirectsac; /** * JedisPoolConfig 連接池 * * @return */ @Bean public JedisPoolConfig jedisPoolConfig() { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // 最大空閑數 jedisPoolConfig.setMaxIdle(maxIdle); // 連接池的最大數據庫連接數 //jedisPoolConfig.setMaxTotal(maxTotal); // 最大建立連接等待時間 jedisPoolConfig.setMaxWaitMillis(maxWaitMillis); // 逐出連接的最小空閑時間 默認1800000毫秒(30分鍾) //jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 每次逐出檢查時 逐出的最大數目 如果為負數就是 : 1/abs(n), 默認3 //jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun); // 逐出掃描的時間間隔(毫秒) 如果為負數,則不運行逐出線程, 默認-1 //jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 是否在從池中取出連接前進行檢驗,如果檢驗失敗,則從池中去除連接並嘗試取出另一個 //jedisPoolConfig.setTestOnBorrow(testOnBorrow); // 在空閑時檢查有效性, 默認false //jedisPoolConfig.setTestWhileIdle(testWhileIdle); return jedisPoolConfig; } /** * Redis集群的配置 * * @return RedisClusterConfiguration * @throws * @autor lpl * @date 2017年12月22日 */ @Bean public RedisClusterConfiguration redisClusterConfiguration() { RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); //Set<RedisNode> clusterNodes String[] serverArray = clusterNodes.split(","); Set<RedisNode> nodes = new HashSet<RedisNode>(); for (String ipPort : serverArray) { String[] ipAndPort = ipPort.split(":"); nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1]))); } redisClusterConfiguration.setClusterNodes(nodes); redisClusterConfiguration.setMaxRedirects(mmaxRedirectsac); return redisClusterConfiguration; } /** * 配置工廠 * * @param @param jedisPoolConfig * @param @return * @return JedisConnectionFactory * @throws * @Title: JedisConnectionFactory * @autor lpl * @date 2017年12月22日 */ @Bean public JedisConnectionFactory JedisConnectionFactory(JedisPoolConfig jedisPoolConfig, RedisClusterConfiguration redisClusterConfiguration) { JedisConnectionFactory JedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig); return JedisConnectionFactory; } @Bean public JedisCluster jedisCluster(JedisConnectionFactory JedisConnectionFactory) { JedisCluster jedisCluster = (JedisCluster)JedisConnectionFactory.getConnection().getNativeConnection(); return jedisCluster; } @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); // 使用fastjson序列化 FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer); // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } }
3、FastJsonRedisSerializer

package com.sso.common.redis.shiro; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import java.nio.charset.Charset; /** * @author yuduojia * @date 2018/9/14 14:42 */ public class FastJsonRedisSerializer<T> implements RedisSerializer<T> { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class<T> clazz; public FastJsonRedisSerializer(Class<T> clazz) { super(); this.clazz = clazz; } @Override public byte[] serialize(T t) throws SerializationException { if (null == t) { return new byte[0]; } return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); } @Override public T deserialize(byte[] bytes) throws SerializationException { if (null == bytes || bytes.length <= 0) { return null; } String str = new String(bytes, DEFAULT_CHARSET); return (T) JSON.parseObject(str, clazz); } }
4、MyObjectInputStream

package com.sso.common.redis.shiro; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; /** * @author yuduojia * @date 2018/10/6 10:20 */ public class MyObjectInputStream extends ObjectInputStream { protected MyObjectInputStream() throws IOException, SecurityException { super(); } public MyObjectInputStream(InputStream arg0) throws IOException { super(arg0); } protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name = desc.getName(); try { if (name.endsWith("UserDO")) name = "com.sso.system.domain.UserDO"; return Class.forName(name); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } return super.resolveClass(desc); } }
5、RedisCache

package com.se.common.redis.shiro; /** * @author ydj * @version V1.0 */ import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.apache.shiro.util.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; public class RedisCache<K, V> implements Cache<K, V> { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * The wrapped Jedis instance. */ @Autowired private RedisManager cache; /** * The Redis key prefix for the sessions */ private String keyPrefix = "sso_redis_session:"; /** * 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; } /** * 通過一個JedisManager實例構造RedisCache */ public RedisCache(RedisManager cache){ if (cache == null) { throw new IllegalArgumentException("Cache argument cannot be null."); } this.cache = cache; } /** * Constructs a cache instance with the specified * Redis manager and using a custom key prefix. * @param cache The cache manager instance * @param prefix The Redis key prefix */ public RedisCache(RedisManager cache, String prefix){ this( cache ); // set the prefix this.keyPrefix = prefix; } /** * 獲得byte[]型的key * @param key * @return */ private byte[] getByteKey(K key){ if(key instanceof String){ String preKey = this.keyPrefix + key; return preKey.getBytes(); }else{ return SerializeUtils.serialize(key); } } @Override public V get(K key) throws CacheException { logger.debug("根據key從Redis中獲取對象 key [" + key + "]"); try { if (key == null) { return null; }else{ byte[] rawValue = cache.get(getByteKey(key)); @SuppressWarnings("unchecked") V value = (V)SerializeUtils.deserialize(rawValue); return value; } } catch (Throwable t) { throw new CacheException(t); } } @Override public V put(K key, V value) throws CacheException { logger.debug("根據key從存儲 key [" + key + "]"); try { cache.set(getByteKey(key), SerializeUtils.serialize(value)); return value; } catch (Throwable t) { throw new CacheException(t); } } @Override public V remove(K key) throws CacheException { logger.debug("從redis中刪除 key [" + key + "]"); try { V previous = get(key); cache.del(getByteKey(key)); return previous; } catch (Throwable t) { throw new CacheException(t); } } @Override public void clear() throws CacheException { logger.debug("從redis中刪除所有元素"); try { cache.flushDB(); } catch (Throwable t) { throw new CacheException(t); } } @Override public int size() { try { Long longSize = new Long(cache.dbSize()); return longSize.intValue(); } catch (Throwable t) { throw new CacheException(t); } } @SuppressWarnings("unchecked") @Override public Set<K> keys() { try { Set<byte[]> keys = cache.keys(this.keyPrefix + "*"); if (CollectionUtils.isEmpty(keys)) { return Collections.emptySet(); }else{ Set<K> newKeys = new HashSet<K>(); for(byte[] key:keys){ newKeys.add((K)key); } return newKeys; } } catch (Throwable t) { throw new CacheException(t); } } @Override public Collection<V> values() { try { Set<byte[]> keys = cache.keys(this.keyPrefix + "*"); if (!CollectionUtils.isEmpty(keys)) { List<V> values = new ArrayList<V>(keys.size()); for (byte[] key : keys) { @SuppressWarnings("unchecked") V value = get((K)key); if (value != null) { values.add(value); } } return Collections.unmodifiableList(values); } else { return Collections.emptyList(); } } catch (Throwable t) { throw new CacheException(t); } } }
6、RedisCacheManager

package com.se.common.redis.shiro; /** * @author ydj * @version V1.0 */ import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.apache.shiro.cache.CacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @Service public class RedisCacheManager implements CacheManager { private static final Logger logger = LoggerFactory .getLogger(RedisCacheManager.class); // fast lookup by name map private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>(); @Autowired private RedisManager redisManager; /** * The Redis key prefix for caches */ private String keyPrefix = "shiro_redis_cache:"; /** * 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; } @Override public <K, V> Cache<K, V> getCache(String name) throws CacheException { logger.debug("獲取名稱為: " + name + " 的RedisCache實例"); Cache c = caches.get(name); if (c == null) { // initialize the Redis manager instance // create a new cache instance c = new RedisCache<K, V>(redisManager, keyPrefix); // add it to the cache collection caches.put(name, c); } return c; } // public RedisManager getRedisManager() { // return redisManager; // } // // public void setRedisManager(RedisManager redisManager) { // this.redisManager = redisManager; // } }
7、RedisManager

package com.se.common.redis.shiro; /** * @author ydj * @version V1.0 */ import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; import javax.annotation.Resource; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * */ @Component public class RedisManager { @Resource(name="P01JedisCluster") private JedisCluster jedis; private int expire = 0; 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; try { value = jedis.get(key); } finally { } return value; } /** * set * * @param key * @param value * @return */ public byte[] set(byte[] key, byte[] value) { try { jedis.set(key, value); if (this.expire != 0) { jedis.expire(key, this.expire); } } catch (Exception e){ e.printStackTrace(); } return value; } /** * set * * @param key * @param value * @param expire * @return */ public byte[] set(byte[] key, byte[] value, int expire) { try { jedis.set(key, value); if (expire != 0) { jedis.expire(key, expire); } } finally { if (jedis != null) { } } return value; } /** * del * * @param key */ public void del(byte[] key) { try { jedis.del(key); } finally { if (jedis != null) { } } } /** * flush */ public void flushDB() { try { jedis.flushDB(); } finally { if (jedis != null) { } } } /** * size */ public Long dbSize() { Long dbSize = 0L; try { dbSize = jedis.dbSize(); } finally { if (jedis != null) { } } return dbSize; } /** * keys * * @param regex * @return */ // public Set<byte[]> keys(String pattern) { // Set<byte[]> keys = null; // try { // Jedis jedis1 = new Jedis(); // jedis1.keys(pattern.getBytes()); // keys = jedis. // } finally { // if (jedis != null) { // } // } // return keys; // } public Set<byte[]> keys(String pattern){ Set<byte[]> keyss = new HashSet<byte[]>(); Map<String, JedisPool> clusterNodes = jedis.getClusterNodes(); for(String k : clusterNodes.keySet()){ JedisPool jp = clusterNodes.get(k); Jedis connection = jp.getResource(); try { Set<String> keys1 = connection.keys(pattern); Set<byte[]> key2 = new HashSet<byte[]>(); for(String k1 : keys1){ key2.add(k1.getBytes()); } //keys.addAll(connection.keys(pattern)); keyss.addAll(key2); } catch(Exception e){ } finally{ connection.close();//用完一定要close這個鏈接!!! } } return keyss; } // 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; } }
8、RedisSessionDAO

package com.se.common.redis.shiro; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * @author ydj * @version V1.0 */ public class RedisSessionDAO extends AbstractSessionDAO { private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class); /** * shiro-redis的session對象前綴 */ @Autowired private RedisManager redisManager; /** * The Redis key prefix for the sessions */ private String keyPrefix = "sso_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; } redisManager.setExpire(1800); 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<Session> getActiveSessions() { Set<Session> sessions = new HashSet<Session>(); Set<byte[]> 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 */ } /** * 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; } }
9、SerializeUtils

package com.se.common.redis.shiro; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author ydj * @version V1.0 */ 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 MyObjectInputStream(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; } }
10、客戶端StatelessSessionManager SessionManager是在應用程序中為所有Subject提供Session的管理,包括創建,刪除,失效及驗證等。同其的核心組件一樣,SessionManager 也是一個由SecurityManager 維護的頂級組件。 在Shiro中默認提供了一個SessionManager的實現DefaultWebSessionManager

package com.se.common.redis.shiro; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.session.ExpiredSessionException; import org.apache.shiro.session.InvalidSessionException; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.DelegatingSession; import org.apache.shiro.session.mgt.SessionContext; import org.apache.shiro.session.mgt.SessionKey; import org.apache.shiro.web.servlet.Cookie; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.servlet.ShiroHttpSession; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.session.mgt.WebSessionKey; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.Serializable; /** * @author yuduojia * @date 2018/9/27 10:54 */ @Component public class StatelessSessionManager extends DefaultWebSessionManager { public final static String TOKEN_NAME = "TOKEN"; public final static String HEADER_TOKEN_NAME = "token"; public final static Logger LOG = LoggerFactory.getLogger(StatelessSessionManager.class); private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response){ String id = WebUtils.toHttp(request).getParameter(HEADER_TOKEN_NAME); if(StringUtils.isEmpty(id)){ return super.getSessionId(request, response); }else{ if (WebUtils.isTrue(request, "_cookie")){ HttpServletRequest rq = (HttpServletRequest)request; HttpServletResponse rs = (HttpServletResponse)response; Cookie template = getSessionIdCookie(); Cookie cookie = new SimpleCookie(template); cookie.setValue(id); cookie.saveTo(rq, rs); } request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID,id); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID,Boolean.TRUE); return id; } } private static final Logger log = LoggerFactory.getLogger(DefaultWebSessionManager.class); private Cookie sessionIdCookie; private boolean sessionIdCookieEnabled; public StatelessSessionManager() { Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME); cookie.setHttpOnly(true); // more secure, protects against XSS attacks this.sessionIdCookie = cookie; this.sessionIdCookieEnabled = true; } public Cookie getSessionIdCookie() { return sessionIdCookie; } public void setSessionIdCookie(Cookie sessionIdCookie) { this.sessionIdCookie = sessionIdCookie; } public boolean isSessionIdCookieEnabled() { return sessionIdCookieEnabled; } public void setSessionIdCookieEnabled(boolean sessionIdCookieEnabled) { this.sessionIdCookieEnabled = sessionIdCookieEnabled; } private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) { if (currentId == null) { String msg = "sessionId cannot be null when persisting for subsequent requests."; throw new IllegalArgumentException(msg); } Cookie template = getSessionIdCookie(); Cookie cookie = new SimpleCookie(template); String idString = currentId.toString(); cookie.setValue(idString); cookie.saveTo(request, response); log.trace("Set session ID cookie for session with id {}", idString); } private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) { getSessionIdCookie().removeFrom(request, response); } private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) { if (!isSessionIdCookieEnabled()) { log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie."); return null; } if (!(request instanceof HttpServletRequest)) { log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie. Returning null."); return null; } HttpServletRequest httpRequest = (HttpServletRequest) request; return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response)); } private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) { String id = getSessionIdCookieValue(request, response); if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE); } else { // not in a cookie, or cookie is disabled - try the request URI as a // fallback (i.e. due to URL rewriting): // try the URI path segment parameters first: id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME); if (id == null) { // not a URI path segment parameter, try the query parameters: String name = getSessionIdName(); id = request.getParameter(name); if (id == null) { // try lowercase: id = request.getParameter(name.toLowerCase()); } } if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); } } if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); // automatically mark it valid here. If it is invalid, the // onUnknownSession method below will be invoked and we'll remove // the attribute at that time. request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); } return id; } // SHIRO-351 // also see // http://cdivilly.wordpress.com/2011/04/22/java-servlets-uri-parameters/ // since 1.2.2 private String getUriPathSegmentParamValue(ServletRequest servletRequest, String paramName) { if (!(servletRequest instanceof HttpServletRequest)) { return null; } HttpServletRequest request = (HttpServletRequest) servletRequest; String uri = request.getRequestURI(); if (uri == null) { return null; } int queryStartIndex = uri.indexOf('?'); if (queryStartIndex >= 0) { // get rid of the query string uri = uri.substring(0, queryStartIndex); } int index = uri.indexOf(';'); // now check for path segment parameters: if (index < 0) { // no path segment params - return: return null; } // there are path segment params, let's get the last one that may exist: final String TOKEN = paramName + "="; uri = uri.substring(index + 1); // uri now contains only the path // segment params // we only care about the last JSESSIONID param: index = uri.lastIndexOf(TOKEN); if (index < 0) { // no segment param: return null; } uri = uri.substring(index + TOKEN.length()); index = uri.indexOf(';'); // strip off any remaining segment params: if (index >= 0) { uri = uri.substring(0, index); } return uri; // what remains is the value } //------------------------------------------------------------------------------------------------------------------ /** * Modify By Goma */ @Override protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException { Serializable sessionId = getSessionId(sessionKey); if (sessionId == null) { log.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " + "session could not be found.", sessionKey); return null; } // ***************Add By Goma**************** ServletRequest request = null; if (sessionKey instanceof WebSessionKey) { request = ((WebSessionKey) sessionKey).getServletRequest(); } if (request != null) { Object s = request.getAttribute(sessionId.toString()); if (s != null) { return (Session) s; } } // ***************Add By Goma**************** Session s = retrieveSessionFromDataSource(sessionId); if (s == null) { // session ID was provided, meaning one is expected to be found, but // we couldn't find one: String msg = "Could not find session with ID [" + sessionId + "]"; throw new UnknownSessionException(msg); } // ***************Add By Goma**************** if (request != null) { request.setAttribute(sessionId.toString(),s); } // ***************Add By Goma**************** return s; } //------------------------------------------------------------------------------------------------------------------ // since 1.2.1 private String getSessionIdName() { String name = this.sessionIdCookie != null ? this.sessionIdCookie.getName() : null; if (name == null) { name = ShiroHttpSession.DEFAULT_SESSION_ID_NAME; } return name; } protected Session createExposedSession(Session session, SessionContext context) { if (!WebUtils.isWeb(context)) { return super.createExposedSession(session, context); } ServletRequest request = WebUtils.getRequest(context); ServletResponse response = WebUtils.getResponse(context); SessionKey key = new WebSessionKey(session.getId(), request, response); return new DelegatingSession(this, key); } protected Session createExposedSession(Session session, SessionKey key) { if (!WebUtils.isWeb(key)) { return super.createExposedSession(session, key); } ServletRequest request = WebUtils.getRequest(key); ServletResponse response = WebUtils.getResponse(key); SessionKey sessionKey = new WebSessionKey(session.getId(), request, response); return new DelegatingSession(this, sessionKey); } /** * Stores the Session's ID, usually as a Cookie, to associate with future * requests. * * @param session * the session that was just {@link #createSession created}. */ @Override protected void onStart(Session session, SessionContext context) { super.onStart(session, context); if (!WebUtils.isHttp(context)) { log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response " + "pair. No session ID cookie will be set."); return; } HttpServletRequest request = WebUtils.getHttpRequest(context); HttpServletResponse response = WebUtils.getHttpResponse(context); if (isSessionIdCookieEnabled()) { Serializable sessionId = session.getId(); storeSessionId(sessionId, request, response); } else { log.debug("Session ID cookie is disabled. No cookie has been set for new session with id {}", session.getId()); } request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE); } @Override public Serializable getSessionId(SessionKey key) { Serializable id = super.getSessionId(key); if (id == null && WebUtils.isWeb(key)) { ServletRequest request = WebUtils.getRequest(key); ServletResponse response = WebUtils.getResponse(key); id = getSessionId(request, response); } return id; } /*protected Serializable getSessionId(ServletRequest request, ServletResponse response) { return getReferencedSessionId(request, response); }*/ @Override protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) { super.onExpiration(s, ese, key); onInvalidation(key); } @Override protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) { super.onInvalidation(session, ise, key); onInvalidation(key); } private void onInvalidation(SessionKey key) { ServletRequest request = WebUtils.getRequest(key); if (request != null) { request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID); } if (WebUtils.isHttp(key)) { log.debug("Referenced session was invalid. Removing session ID cookie."); removeSessionIdCookie(WebUtils.getHttpRequest(key), WebUtils.getHttpResponse(key)); } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " + "pair. Session ID cookie will not be removed due to invalidated session."); } } @Override protected void onStop(Session session, SessionKey key) { super.onStop(session, key); if (WebUtils.isHttp(key)) { HttpServletRequest request = WebUtils.getHttpRequest(key); HttpServletResponse response = WebUtils.getHttpResponse(key); log.debug("Session has been stopped (subject logout or explicit stop). Removing session ID cookie."); removeSessionIdCookie(request, response); } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " + "pair. Session ID cookie will not be removed due to stopped session."); } } /** * This is a native session manager implementation, so this method returns * {@code false} always. * * @return {@code false} always * @since 1.2 */ public boolean isServletContainerSessions() { return false; } }
DefaultWebSessionManager類 介紹 不是項目用到的
DefaultWebSessionManager類 1.數據屬性 private Cookie sessionIdCookie;//sessionId cookie private boolean sessionIdCookieEnabled;//session是否可以被保存到cookie中 2.構造方法 public DefaultWebSessionManager() { Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME); cookie.setHttpOnly(true); //more secure, protects against XSS attacks this.sessionIdCookie = cookie; this.sessionIdCookieEnabled = true; } 3.獲取sessionIdCookie public Cookie getSessionIdCookie() { return sessionIdCookie; } 4.設置sessionIdCookie public void setSessionIdCookie(Cookie sessionIdCookie) { this.sessionIdCookie = sessionIdCookie; } 5.是否將sessionId存儲到cookie中 public boolean isSessionIdCookieEnabled() { return sessionIdCookieEnabled; } 6.設置是否將sessionId存儲到cookie中 public void setSessionIdCookieEnabled(boolean sessionIdCookieEnabled) { this.sessionIdCookieEnabled = sessionIdCookieEnabled; } 7.存儲sessionId到cookie中 private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) { if (currentId == null) { String msg = "sessionId cannot be null when persisting for subsequent requests."; throw new IllegalArgumentException(msg); } Cookie template = getSessionIdCookie(); Cookie cookie = new SimpleCookie(template); String idString = currentId.toString(); cookie.setValue(idString); cookie.saveTo(request, response); log.trace("Set session ID cookie for session with id {}", idString); } 8.從cookie中移除session信息 private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) { getSessionIdCookie().removeFrom(request, response); } 9.從cookie中讀取sessionId信息 private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) { if (!isSessionIdCookieEnabled()) { log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie."); return null; } if (!(request instanceof HttpServletRequest)) { log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie. Returning null."); return null; } HttpServletRequest httpRequest = (HttpServletRequest) request; return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response)); } 10.檢測sessionId關聯源(如果可以從cookie中獲取sessionId,則在request中設置sessionId的關聯源為cookie;如果不可以讀取,則從request訪問路徑中獲取,如果不存在,則從request的parameter中獲取,如果從request的訪問路徑中或者parameter中獲取到的不為空,則設置關聯源為url;並在將sessionId和合法信息存儲到request中) private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) { String id = getSessionIdCookieValue(request, response); if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE); } else { //not in a cookie, or cookie is disabled - try the request URI as a fallback (i.e. due to URL rewriting): //try the URI path segment parameters first: id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME); if (id == null) { //not a URI path segment parameter, try the query parameters: String name = getSessionIdName(); id = request.getParameter(name); if (id == null) { //try lowercase: id = request.getParameter(name.toLowerCase()); } } if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); } } if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); //automatically mark it valid here. If it is invalid, the //onUnknownSession method below will be invoked and we'll remove the attribute at that time. request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); } return id; } 11.從request路徑中獲取sessionId信息 private String getUriPathSegmentParamValue(ServletRequest servletRequest, String paramName) { if (!(servletRequest instanceof HttpServletRequest)) { return null; } HttpServletRequest request = (HttpServletRequest)servletRequest; String uri = request.getRequestURI(); if (uri == null) { return null; } int queryStartIndex = uri.indexOf('?'); if (queryStartIndex >= 0) { //get rid of the query string uri = uri.substring(0, queryStartIndex); } int index = uri.indexOf(';'); //now check for path segment parameters: if (index < 0) { //no path segment params - return: return null; } //there are path segment params, let's get the last one that may exist: final String TOKEN = paramName + "="; uri = uri.substring(index+1); //uri now contains only the path segment params //we only care about the last JSESSIONID param: index = uri.lastIndexOf(TOKEN); if (index < 0) { //no segment param: return null; } uri = uri.substring(index + TOKEN.length()); index = uri.indexOf(';'); //strip off any remaining segment params: if(index >= 0) { uri = uri.substring(0, index); } return uri; //what remains is the value } 12.獲取sessionId名字 private String getSessionIdName() { String name = this.sessionIdCookie != null ? this.sessionIdCookie.getName() : null; if (name == null) { name = ShiroHttpSession.DEFAULT_SESSION_ID_NAME; } return name; } 13.創建session信息(它覆蓋了AbstractNativeSessionManager接口的方法) protected Session createExposedSession(Session session, SessionContext context) { if (!WebUtils.isWeb(context)) { return super.createExposedSession(session, context); } ServletRequest request = WebUtils.getRequest(context); ServletResponse response = WebUtils.getResponse(context); SessionKey key = new WebSessionKey(session.getId(), request, response); return new DelegatingSession(this, key); } 14.創建session信息(它覆蓋了AbstractNativeSessionManager抽象類的方法) protected Session createExposedSession(Session session, SessionKey key) { if (!WebUtils.isWeb(key)) { return super.createExposedSession(session, key); } ServletRequest request = WebUtils.getRequest(key); ServletResponse response = WebUtils.getResponse(key); SessionKey sessionKey = new WebSessionKey(session.getId(), request, response); return new DelegatingSession(this, sessionKey); } 15.啟動session(將session存儲到cookie中,並將cookie中移除session源,設置session是新的,它覆蓋了AbstractNativeSessionManager抽象類的方法) protected void onStart(Session session, SessionContext context) { super.onStart(session, context); if (!WebUtils.isHttp(context)) { log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response " + "pair. No session ID cookie will be set."); return; } HttpServletRequest request = WebUtils.getHttpRequest(context); HttpServletResponse response = WebUtils.getHttpResponse(context); if (isSessionIdCookieEnabled()) { Serializable sessionId = session.getId(); storeSessionId(sessionId, request, response); } else { log.debug("Session ID cookie is disabled. No cookie has been set for new session with id {}", session.getId()); } request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE); } 16.獲取sessionId(先從sessionKey中獲取,如果沒有,則從cookie中獲取,如果在沒有,則從request請求路徑或者parameter中獲取,它覆蓋了DefaultSessionManager類的方法) public Serializable getSessionId(SessionKey key) { Serializable id = super.getSessionId(key); if (id == null && WebUtils.isWeb(key)) { ServletRequest request = WebUtils.getRequest(key); ServletResponse response = WebUtils.getResponse(key); id = getSessionId(request, response); } return id; } 17.從cookie或者request中獲取sessionId信息 protected Serializable getSessionId(ServletRequest request, ServletResponse response) { return getReferencedSessionId(request, response); } 18.session過期(它覆蓋了AbstractValidatingSessionManager抽象類的方法) protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) { super.onExpiration(s, ese, key); onInvalidation(key); } 19.session失效(它覆蓋了AbstractValidatingSessionManager抽象類的方法) protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) { super.onInvalidation(session, ise, key); onInvalidation(key); } 20.移除request中屬性(sessionId是合法的和從cookie中移除sessionId) private void onInvalidation(SessionKey key) { ServletRequest request = WebUtils.getRequest(key); if (request != null) { request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID); } if (WebUtils.isHttp(key)) { log.debug("Referenced session was invalid. Removing session ID cookie."); removeSessionIdCookie(WebUtils.getHttpRequest(key), WebUtils.getHttpResponse(key)); } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " + "pair. Session ID cookie will not be removed due to invalidated session."); } } 21.暫停session(設置session的暫停時間,並從cookie中移除session,它實現了AbstractNativeSessionManager抽象類的方法) protected void onStop(Session session, SessionKey key) { super.onStop(session, key); if (WebUtils.isHttp(key)) { HttpServletRequest request = WebUtils.getHttpRequest(key); HttpServletResponse response = WebUtils.getHttpResponse(key); log.debug("Session has been stopped (subject logout or explicit stop). Removing session ID cookie."); removeSessionIdCookie(request, response); } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " + "pair. Session ID cookie will not be removed due to stopped session."); } } 22.如果session被servlet container管理則返回true;如果被shiro管理,則返回false public boolean isServletContainerSessions() { return false; }
11、客戶端ShiroConfig 設置DefaultWebSessionManager

package com.se.system.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import com.se.common.config.Constant; import com.se.common.redis.shiro.RedisCacheManager; import com.se.common.redis.shiro.RedisManager; import com.se.common.redis.shiro.RedisSessionDAO; import com.se.common.redis.shiro.StatelessSessionManager; import com.se.system.shiro.UserRealm; //import org.apache.shiro.cache.CacheManager; import net.sf.ehcache.CacheManager; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.SessionListener; import org.apache.shiro.session.mgt.eis.MemorySessionDAO; import org.apache.shiro.session.mgt.eis.SessionDAO; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; /** * @author ydj */ @Configuration public class ShiroConfig { // @Value("${spring.redis.host}") // private String host; // @Value("${spring.redis.password}") // private String password; // @Value("${spring.redis.port}") // private int port; // @Value("${spring.redis.timeout}") // private int timeout; @Value("${spring.cache.type}") private String cacheType; @Value("${server.session-timeout}") private int tomcatTimeout; @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Autowired private RedisManager redisManager; @Autowired private RedisCacheManager cacheManager; /** * ShiroDialect,為了在thymeleaf里使用shiro的標簽的bean * * @return */ @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } @Bean ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/index"); shiroFilterFactoryBean.setUnauthorizedUrl("/403"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //對位提供服務接口忽略 filterChainDefinitionMap.put("/ssoGetUserIdList/**", "anon"); filterChainDefinitionMap.put("/sendEmail/**", "anon"); filterChainDefinitionMap.put("/YPObtainSMSState/YPobtainSMSState", "anon"); filterChainDefinitionMap.put("/HKsendSMS/**", "anon"); filterChainDefinitionMap.put("/sendSMS/**", "anon"); filterChainDefinitionMap.put("/batchSendSMS/**", "anon"); filterChainDefinitionMap.put("/yimeicmq/**", "anon"); filterChainDefinitionMap.put("/cslcmq/**", "anon"); filterChainDefinitionMap.put("/yunpiancmq/**", "anon"); filterChainDefinitionMap.put("/cmqtest/**", "anon"); filterChainDefinitionMap.put("/asyncSendEmail/**", "anon"); filterChainDefinitionMap.put("/asyncSendSMS/**", "anon"); filterChainDefinitionMap.put("/oneSMSCmq/**", "anon"); filterChainDefinitionMap.put("/emailCmq/**", "anon"); filterChainDefinitionMap.put("/batchBackReceiver/**", "anon"); filterChainDefinitionMap.put("/Timescheduling/**", "anon"); //億美時間調度獲取短信狀態報告 filterChainDefinitionMap.put("/statueReport/**", "anon"); //香港獲取推送狀態報告的接口 filterChainDefinitionMap.put("/sys/userpush/**", "anon"); //角色操作接口 filterChainDefinitionMap.put("/sys/rolepush/**", "anon"); //角色操作接口 filterChainDefinitionMap.put("/login","anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/fonts/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); filterChainDefinitionMap.put("/docs/**", "anon"); filterChainDefinitionMap.put("/druid/**", "anon"); filterChainDefinitionMap.put("/upload/**", "anon"); filterChainDefinitionMap.put("/files/**", "anon"); filterChainDefinitionMap.put("/templates/*", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/blog", "anon"); filterChainDefinitionMap.put("/blog/open/**", "anon"); filterChainDefinitionMap.put("/**", "user"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //設置realm. securityManager.setRealm(userRealm()); // 自定義緩存實現 使用redis if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) { securityManager.setCacheManager(cacheManager); } else { securityManager.setCacheManager(ehCacheManager()); } // securityManager.setCacheManager(cacheManager); securityManager.setSessionManager(sessionManager()); return securityManager; } @Bean UserRealm userRealm() { UserRealm userRealm = new UserRealm(); return userRealm; } /** * 開啟shiro aop注解支持. * 使用代理方式;所以需要開啟代碼支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 配置shiro redisManager * * @return */ // @Bean // public RedisManager redisManager() { // RedisManager redisManager = new RedisManager(); // redisManager.setHost(host); // redisManager.setPort(port); // redisManager.setExpire(1800);// 配置緩存過期時間 // //redisManager.setTimeout(1800); // redisManager.setPassword(password); // return redisManager; // } /** * cacheManager 緩存 redis實現 * 使用的是shiro-redis開源插件 * * @return */ // public RedisCacheManager cacheManager() { // RedisCacheManager redisCacheManager = new RedisCacheManager(); // redisCacheManager.setRedisManager(redisManager()); // return redisCacheManager; // } /** * RedisSessionDAO shiro sessionDao層的實現 通過redis * 使用的是shiro-redis開源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager); return redisSessionDAO; } @Bean public SessionDAO sessionDAO() { if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) { return redisSessionDAO(); } else { return new MemorySessionDAO(); } // return redisSessionDAO(); } /** * shiro session的管理 */ @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new StatelessSessionManager(); sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000); sessionManager.setSessionDAO(sessionDAO()); Collection<SessionListener> listeners = new ArrayList<SessionListener>(); listeners.add(new BDSessionListener()); sessionManager.setSessionListeners(listeners); return sessionManager; } @Bean public EhCacheManager ehCacheManager() { EhCacheManager em = new EhCacheManager(); em.setCacheManager(CacheManager.create()); return em; } }
12、服務端ShiroCofig 設置雙Realm

package com.sso.system.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import com.sso.common.config.Constant; import com.sso.common.redis.shiro.RedisCacheManager; import com.sso.common.redis.shiro.RedisManager; import com.sso.common.redis.shiro.RedisSessionDAO; import com.sso.system.shiro.ChildrenRealm; import com.sso.system.shiro.UserModularRealmAuthenticator; import com.sso.system.shiro.UserRealm; //import org.apache.shiro.cache.CacheManager; import net.sf.ehcache.CacheManager; import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy; import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; import org.apache.shiro.session.SessionListener; import org.apache.shiro.session.mgt.eis.MemorySessionDAO; import org.apache.shiro.session.mgt.eis.SessionDAO; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; /** * @author ydj */ @Configuration public class ShiroConfig { // @Value("${spring.redis.host}") // private String host; // @Value("${spring.redis.password}") // private String password; // @Value("${spring.redis.port}") // private int port; // @Value("${spring.redis.timeout}") // private int timeout; // @Value("${spring.cache.type}") // private String cacheType; @Value("${server.session-timeout}") private int tomcatTimeout; @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Autowired private RedisManager redisManager; @Autowired private RedisCacheManager cacheManager; /** * ShiroDialect,為了在thymeleaf里使用shiro的標簽的bean * * @return */ @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } @Bean ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setLoginUrl("/childrenLogin");// shiroFilterFactoryBean.setSuccessUrl("/index"); shiroFilterFactoryBean.setSuccessUrl("/childIndex");// shiroFilterFactoryBean.setUnauthorizedUrl("/403"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //對位提供服務接口忽略 filterChainDefinitionMap.put("/login","anon"); filterChainDefinitionMap.put("/childrenLogin","anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/fonts/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); filterChainDefinitionMap.put("/docs/**", "anon"); filterChainDefinitionMap.put("/druid/**", "anon"); filterChainDefinitionMap.put("/upload/**", "anon"); filterChainDefinitionMap.put("/files/**", "anon"); filterChainDefinitionMap.put("/templates/*", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/blog", "anon"); filterChainDefinitionMap.put("/blog/open/**", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //設置realm. securityManager.setAuthenticator(modularRealmAuthenticator()); List<Realm> realms = new ArrayList<>(); //添加多個Realm realms.add(userRealm()); realms.add(childrenRealm()); securityManager.setRealms(realms); // 自定義緩存實現 使用redis /*if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) { securityManager.setCacheManager(cacheManager); } else { securityManager.setCacheManager(ehCacheManager()); }*/ securityManager.setCacheManager(cacheManager); securityManager.setSessionManager(sessionManager()); return securityManager; } @Bean public ChildrenRealm childrenRealm() { ChildrenRealm childrenRealm = new ChildrenRealm(); return childrenRealm; } @Bean public ModularRealmAuthenticator modularRealmAuthenticator(){ //自己重寫的ModularRealmAuthenticator UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator(); modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); return modularRealmAuthenticator; } @Bean UserRealm userRealm() { UserRealm userRealm = new UserRealm(); return userRealm; } /** * 開啟shiro aop注解支持. * 使用代理方式;所以需要開啟代碼支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 配置shiro redisManager * * @return */ // @Bean // public RedisManager redisManager() { // RedisManager redisManager = new RedisManager(); // redisManager.setHost(host); // redisManager.setPort(port); // redisManager.setExpire(1800);// 配置緩存過期時間 // //redisManager.setTimeout(1800); // redisManager.setPassword(password); // return redisManager; // } /** * cacheManager 緩存 redis實現 * 使用的是shiro-redis開源插件 * * @return */ // public RedisCacheManager cacheManager() { // RedisCacheManager redisCacheManager = new RedisCacheManager(); // redisCacheManager.setRedisManager(redisManager()); // return redisCacheManager; // } /** * RedisSessionDAO shiro sessionDao層的實現 通過redis * 使用的是shiro-redis開源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager); return redisSessionDAO; } @Bean public SessionDAO sessionDAO() { /*if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) { return redisSessionDAO(); } else { return new MemorySessionDAO(); }*/ return redisSessionDAO(); } /** * shiro session的管理 */ @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000); sessionManager.setSessionDAO(sessionDAO()); Collection<SessionListener> listeners = new ArrayList<SessionListener>(); listeners.add(new BDSessionListener()); sessionManager.setSessionListeners(listeners); //sessionManager.setSessionIdCookie(sessionIdCookie()); return sessionManager; } @Bean public EhCacheManager ehCacheManager() { EhCacheManager em = new EhCacheManager(); em.setCacheManager(CacheManager.create()); return em; } /* @Bean public SimpleCookie sessionIdCookie() { SimpleCookie cookie = new SimpleCookie(); cookie.setName("SSOSESSIONID"); cookie.setHttpOnly(true); cookie.setMaxAge(18000); return cookie; }*/ }
二、登錄,驗證,並且雙身份用戶Realm (服務端) ChildrenRealm是普通用戶登錄,UserRealm系統管理員用戶登錄 所以這個設計是有兩個登錄頁面
1、LoginController

package com.sso.system.controller; import com.sso.common.annotation.Log; import com.sso.common.controller.BaseController; import com.sso.common.domain.FileDO; import com.sso.common.domain.Tree; import com.sso.common.service.FileService; import com.sso.common.utils.MD5Utils; import com.sso.common.utils.R; import com.sso.common.utils.ShiroUtils; import com.sso.system.domain.MenuDO; import com.sso.system.service.MenuService; import com.sso.system.shiro.UserToken; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller public class LoginController extends BaseController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired MenuService menuService; @Autowired FileService fileService; @GetMapping({ "/", "" }) String welcome(Model model) { return "redirect:/login"; } @Log("請求訪問主頁") @GetMapping({ "/index" }) String index(Model model) { List<Tree<MenuDO>> menus = menuService.listMenuTree(getUserId()); model.addAttribute("menus", menus); model.addAttribute("name", getUser().getName()); FileDO fileDO = fileService.get(getUser().getPicId()); if(fileDO!=null&&fileDO.getUrl()!=null){ if(fileService.isExist(fileDO.getUrl())){ model.addAttribute("picUrl",fileDO.getUrl()); }else { model.addAttribute("picUrl","/img/photo_s.jpg"); } }else { model.addAttribute("picUrl","/img/photo_s.jpg"); } model.addAttribute("username", getUser().getUsername()); return "index_v1"; } @GetMapping("/login") String login() { return "login"; } @Log("登錄") @PostMapping("/login") @ResponseBody R ajaxLogin(String username, String password) { password = MD5Utils.encrypt(username, password); UserToken token = new UserToken(username, password,"User"); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); return R.ok(); }catch (UnknownAccountException e) { return R.error("賬戶不存在"); }catch (IncorrectCredentialsException e) { return R.error("密碼錯誤"); } } @GetMapping("/logout") String logout() { ShiroUtils.logout(); return "redirect:/login"; } @GetMapping("/main") String main() { return "main"; } }
2、ChildrenRealm 這個是客戶登錄 Realm提供了安全的訪問應用的相關實體類,比如用戶、角色、權限,對其中的訪問應用相應的認證或者授權操作。其提供的主要的方法為AuthenticationInfo#getAuthenticationInfo
,涉及的內容是關於信息的認證,這主要由AuthencatingRealm
類實現

package com.sso.system.controller; import com.sso.common.annotation.Log; import com.sso.common.controller.BaseController; import com.sso.common.domain.FileDO; import com.sso.common.domain.Tree; import com.sso.common.service.FileService; import com.sso.common.utils.MD5Utils; import com.sso.common.utils.R; import com.sso.common.utils.ShiroUtils; import com.sso.system.domain.MenuDO; import com.sso.system.service.MenuService; import com.sso.system.shiro.UserToken; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller public class LoginController extends BaseController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired MenuService menuService; @Autowired FileService fileService; @GetMapping({ "/", "" }) String welcome(Model model) { return "redirect:/login"; } @Log("請求訪問主頁") @GetMapping({ "/index" }) String index(Model model) { List<Tree<MenuDO>> menus = menuService.listMenuTree(getUserId()); model.addAttribute("menus", menus); model.addAttribute("name", getUser().getName()); FileDO fileDO = fileService.get(getUser().getPicId()); if(fileDO!=null&&fileDO.getUrl()!=null){ if(fileService.isExist(fileDO.getUrl())){ model.addAttribute("picUrl",fileDO.getUrl()); }else { model.addAttribute("picUrl","/img/photo_s.jpg"); } }else { model.addAttribute("picUrl","/img/photo_s.jpg"); } model.addAttribute("username", getUser().getUsername()); return "index_v1"; } @GetMapping("/login") String login() { return "login"; } @Log("登錄") @PostMapping("/login") @ResponseBody R ajaxLogin(String username, String password) { password = MD5Utils.encrypt(username, password); UserToken token = new UserToken(username, password,"User"); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); return R.ok(); }catch (UnknownAccountException e) { return R.error("賬戶不存在"); }catch (IncorrectCredentialsException e) { return R.error("密碼錯誤"); } } @GetMapping("/logout") String logout() { ShiroUtils.logout(); return "redirect:/login"; } @GetMapping("/main") String main() { return "main"; } }
3、UserRealm 這個是系統管理員登錄的

package com.sso.system.shiro; import com.sso.common.config.ApplicationContextRegister; import com.sso.common.utils.ShiroUtils; import com.sso.system.dao.UserDao; import com.sso.system.domain.UserDO; import com.sso.system.service.MenuService; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; public class UserRealm extends AuthorizingRealm { /* @Autowired UserDao userMapper; @Autowired MenuService menuService;*/ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { Long userId = ShiroUtils.getUserId(); MenuService menuService = ApplicationContextRegister.getBean(MenuService.class); Set<String> perms = menuService.listPerms(userId); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(perms); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); Map<String, Object> map = new HashMap<>(16); map.put("username", username); String password = new String((char[]) token.getCredentials()); UserDao userMapper = ApplicationContextRegister.getBean(UserDao.class); // 查詢用戶信息 List<UserDO> userDOS = userMapper.list(map); UserDO user = null; if(userDOS.size()>0){ user=userDOS.get(0); } // 賬號不存在 if (user == null) { throw new UnknownAccountException("賬號或密碼不正確"); } // 密碼錯誤 if (!password.equals(user.getPassword())) { throw new IncorrectCredentialsException("賬號或密碼不正確"); } // 賬號鎖定 if (user.getStatus() == 0) { throw new LockedAccountException("賬號已被鎖定,請聯系管理員"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName()); return info; } }
4、UserModularRealmAuthenticator

package com.sso.system.shiro; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.realm.Realm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; /** * @author yuduojia * @date 2018/10/9 19:51 */ public class UserModularRealmAuthenticator extends ModularRealmAuthenticator { private static final Logger logger = LoggerFactory.getLogger(UserModularRealmAuthenticator.class); @Override protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { logger.info("UserModularRealmAuthenticator:method doAuthenticate() execute "); // 判斷getRealms()是否返回為空 assertRealmsConfigured(); // 強制轉換回自定義的CustomizedToken UserToken userToken = (UserToken) authenticationToken; // 登錄類型 String loginType = userToken.getLoginType(); // 所有Realm Collection<Realm> realms = getRealms(); // 登錄類型對應的所有Realm Collection<Realm> typeRealms = new ArrayList<>(); for (Realm realm : realms) { if (realm.getName().contains(loginType)) typeRealms.add(realm); } // 判斷是單Realm還是多Realm if (typeRealms.size() == 1){ logger.info("doSingleRealmAuthentication() execute "); return doSingleRealmAuthentication(typeRealms.iterator().next(), userToken); } else{ logger.info("doMultiRealmAuthentication() execute "); return doMultiRealmAuthentication(typeRealms, userToken); } } }
5、LoginType

package com.sso.system.shiro; /** * @author * @version 1.0 * @date 2018/10/9 */ public enum LoginType { CHILDREN("Children"), ADMIN("User"); private String type; private LoginType(String type) { this.type = type; } @Override public String toString() { return this.type.toString(); } }
6、UserToken

package com.sso.system.shiro; import org.apache.shiro.authc.UsernamePasswordToken; /** * @author yuduojia * @date 2018/10/9 19:49 */ public class UserToken extends UsernamePasswordToken { //登錄類型,判斷是普通用戶還是系統管理員用戶登錄 private String loginType; public UserToken(final String username, final String password,String loginType) { super(username,password); this.loginType = loginType; } public String getLoginType() { return loginType; } public void setLoginType(String loginType) { this.loginType = loginType; } }
7、ChildLoginController

package com.sso.children.controller; import com.google.gson.Gson; import com.sso.children.domain.ChildDeptDO; import com.sso.children.domain.ChildMenuDO; import com.sso.children.domain.OfficeDO; import com.sso.children.service.ChildDeptService; import com.sso.children.service.ChildMenuService; import com.sso.children.service.ChildUserService; import com.sso.children.service.UserOfficeService; import com.sso.children.util.OfficeToZtree; import com.sso.children.vo.ChildDeptVO; import com.sso.children.vo.ZtreeVO; import com.sso.common.annotation.Log; import com.sso.common.controller.BaseController; import com.sso.common.domain.Tree; import com.sso.common.service.FileService; import com.sso.common.utils.MD5Utils; import com.sso.common.utils.R; import com.sso.common.utils.ShiroUtils; import com.sso.system.shiro.UserToken; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import sun.misc.BASE64Encoder; import javax.servlet.http.HttpServletRequest; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 登錄 */ @Controller public class ChildLoginController extends BaseController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private UserOfficeService userOfficeService; @Autowired ChildMenuService menuService; @Autowired FileService fileService; @Autowired ChildUserService userService; @Autowired ChildDeptService deptService; @Log("請求訪問主頁") @GetMapping({ "/childIndex" }) String childrenIndex(Model model) { List<Tree<ChildMenuDO>> menus = menuService.listMenuTree(getUserId()); model.addAttribute("menus", menus); model.addAttribute("name", getUser().getName()); model.addAttribute("username", getUser().getUsername()); return "childIndex_v1"; } @Log("請求訪問渠道選擇頁") @GetMapping({ "/childIndex_choice" }) String childrenIndex_choice(Model model) { Long userId = super.getUserId(); //選出此用戶下的所有的部門表 List<ChildDeptDO> deptDOS = deptService.ListNameUrl(userId); List<ChildDeptVO> depts = new ArrayList<>(); String username = super.getUsername(); String name =super.getUser().getName(); String uu = username + "," + userId; byte[] textByte = new byte[0]; final BASE64Encoder encoder = new BASE64Encoder(); try { textByte = uu.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //編碼 final String encodedText = encoder.encode(textByte); //shiro的token Subject subject = SecurityUtils.getSubject(); Serializable id = subject.getSession().getId(); model.addAttribute("token",(String)id); if(null!=deptDOS && deptDOS.size()>0){ for (int i = 0; i < deptDOS.size(); i++) { ChildDeptDO childDept = deptDOS.get(i); List<OfficeDO> officeDO = userOfficeService.getOfficeByUserDept( new HashMap(){{ put("deptId",childDept.getDeptId()); put("userId",userId); }}); ChildDeptVO childDeptDO = new ChildDeptVO(); childDeptDO.setDeptId(childDept.getDeptId()); childDeptDO.setImgUrl(deptDOS.get(i).getImgUrl()); childDeptDO.setOfficeDOS(officeDO); childDeptDO.setLoginUrl(deptDOS.get(i).getLoginUrl()+"?token="+(String)id+"&_cookie=true"+"&token_uu="+encodedText); childDeptDO.setName(deptDOS.get(i).getName()); depts.add(childDeptDO); } } Gson gson = new Gson(); List<ChildDeptVO> childDeptVOS = new ArrayList<>(); List<ChildDeptVO> childJGDeptVOS = new ArrayList<>(); for(int i = 0 ;i<depts.size();i++){ ChildDeptVO childDeptVO = depts.get(i); if(childDeptVO.getOfficeDOS().size()>0){ String youJgDept = gson.toJson(childDeptVO); ChildDeptVO childDeptvo =gson.fromJson(youJgDept,ChildDeptVO.class); List<OfficeDO> offices = childDeptvo.getOfficeDOS(); OfficeToZtree officeToZtree = new OfficeToZtree(childDeptVO.getLoginUrl()); List<ZtreeVO> ztress = officeToZtree.change(offices); childDeptvo.setZtreeVOS(ztress); childJGDeptVOS.add(childDeptvo); }else{ String noJgDept = gson.toJson(childDeptVO); childDeptVOS.add(gson.fromJson(noJgDept,ChildDeptVO.class)); } } model.addAttribute("jGdepts",childJGDeptVOS); model.addAttribute("depts",childDeptVOS); model.addAttribute("name",name); return "childIndex_choice"; } @GetMapping("/childLogout") String logout() { ShiroUtils.logout(); return "redirect:/childrenLogin"; } @GetMapping("/childMain") String childrenMain() { return "childMain"; } @GetMapping("/childrenLogin") String childrenLogin() { return "childrenLogin"; } @PostMapping("/childrenLogin") @ResponseBody public R childrenLogin(HttpServletRequest request){ String username = request.getParameter("username"); String password = request.getParameter("password"); password = MD5Utils.encrypt(username, password); UserToken token = new UserToken(username, password,"Children"); Subject currentUser = SecurityUtils.getSubject(); try { currentUser.login(token); } catch (UnknownAccountException e) { return R.error("賬戶不存在"); }catch (IncorrectCredentialsException e) { return R.error("密碼錯誤"); } return R.childIndex("/mcpsso/childIndex_choice"); } }