实际项目中用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"); } }