- 会话
Shiro可以扩展Session至第三方缓存中,可以实现分布式下的缓存共享,如redis的话实现RedisSessionDao接口即可,本质为将session序列化后存储到redis中。
@Component
public class RedisSessionDao extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(AbstractSessionDAO.class);
@Autowired
private JedisPool jedisPool;
private static final String SHIRO_SESSION_ID_PREFIX="SHIRO_SESSION_ID:";
/**
*
* @param session
*/
private void saveDession(Session session){
if(session==null){
return;
}
Jedis jedis=null;
try{
jedis = jedisPool.getResource();
byte[] key=(SHIRO_SESSION_ID_PREFIX+session.getId().toString()).getBytes();
jedis.set(key, SerializationUtils.serialize(session));
jedis.expire(key,600);
}finally {
if(jedis!=null){
jedis.close();
}
}
}
@Override
protected Serializable doCreate(Session session) {
logger.info("----------------RedisSessionDao->doCreate---------------------------");
Serializable sessionId = generateSessionId(session);
logger.info("创建session:sessionId;{}",sessionId);
assignSessionId(session,sessionId);
saveDession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
logger.info("----------------RedisSessionDao->doReadSession--------------------------");
if(sessionId==null){
return null;
}
logger.info("读取session信息,sessionId为{}",sessionId);
byte[] key = (SHIRO_SESSION_ID_PREFIX + sessionId.toString()).getBytes();
Jedis jedis=null;
try{
jedis= jedisPool.getResource();
byte[] value=jedis.get(key);
return (Session) SerializationUtils.deserialize(value);
}finally {
if(jedis!=null){
jedis.close();
}
}
}
@Override
public void update(Session session) throws UnknownSessionException {
logger.info("----------------RedisSessionDao->update--------------------------");
logger.info("更新session信息,");
saveDession(session);
}
@Override
public void delete(Session session) {
logger.info("----------------RedisSessionDao->delete--------------------------");
if(session==null||session.getId()==null){
return;
}
logger.info("删除session信息,sessionId为:{}",session.getId().toString());
byte[] key=(SHIRO_SESSION_ID_PREFIX+session.getId().toString()).getBytes();
Jedis jedis=null;
try{
jedis= jedisPool.getResource();
jedis.del(key);
}finally {
if(jedis!=null){
jedis.close();
}
}
}
@Override
public Collection<Session> getActiveSessions() {
Jedis jedis=null;
try{
jedis= jedisPool.getResource();
Set<byte[]> keys = jedis.keys((SHIRO_SESSION_ID_PREFIX + "*").getBytes());
Set<Session> sessions=new HashSet<>();
if(CollectionUtils.isEmpty(keys)){
return sessions;
}
for (byte[] key:keys){
Session session=(Session)SerializationUtils.deserialize(jedis.get(key));
sessions.add(session);
}
return sessions;
}finally {
if(jedis!=null){
jedis.close();
}
}
}
}
shiro的配置文件中SecurityManager注入sessionManager
<!--创建SecurityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customerRealm"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!--会话管理-->
<bean id="sessionManager" class="com.test.shiro.realm.CustomSessionManager">
<property name="sessionDAO" ref="redisSessionDao"/>
</bean>
<bean id="redisSessionDao" class="com.test.shiro.redis.RedisSessionDao"/>
踩到一个坑,在idea中开发,Tomcat启动后,我在idea中不设置tomcat启动成功自动在浏览器中访问项目,控制台不停的执行doReadSession和update方法,网上各种说法都有,有说时shiro设计的问题,还有说是shiro集成quartz。在定时清除过期的session,未果,开始chrome启动说”windows找不到文件 chrome 请确定文件名是否正确“,没在意,解决后不停的调用doReadSession和update方法的问题也不出现了,有点迷,问题原因未知。。。
就没报名
- 角色信息缓存化
shiro支持角色信息与权限信息添加到缓存中,如果缓存不存在,执行AuthorizingRealm的doGetAuthorizationInfo方法,从数据库中获取角色与权限信息,实现CacheManager接口
public class RedisCacheManager implements CacheManager {
@Autowired
private RedisCache redisCache;
@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
return redisCache;
}
}
注入的RedisCache,实现Cache<K,V>接口
@Component
public class RedisCache<K,V> implements Cache<K,V> {
private static Logger logger = LoggerFactory.getLogger(RedisCache.class);
private static final String CACHE_PREFIX="shiro-cache:";
private static final int CACHE_EXPIRE_TIME=10*60;
@Autowired
private JedisPool jedisPool;
private byte[] getKey(K k){
if(k instanceof String){
return (CACHE_PREFIX+k).getBytes();
}
return SerializationUtils.serialize(k);
}
@Override
public V get(K k) throws CacheException {
logger.info("从redis中获取权限数据,k为{}",k);
Jedis jedis=null;
try {
jedis=jedisPool.getResource();
byte[] value = jedis.get(getKey(k));
if(value!=null){
return (V)SerializationUtils.deserialize(value);
}
return null;
}finally {
if(jedis!=null){
jedis.close();
}
}
}
@Override
public V put(K k, V v) throws CacheException {
logger.info("将角色与权限信息添加到缓存中k为{}",k);
Jedis jedis=null;
try {
byte[] key=getKey(k);
byte[] value=SerializationUtils.serialize(v);
jedis=jedisPool.getResource();
jedis.set(key,value);
jedis.expire(key,CACHE_EXPIRE_TIME);
}finally {
if(jedis!=null){
jedis.close();
}
}
return v;
}
@Override
public V remove(K k) throws CacheException {
Jedis jedis=null;
try {
byte[] key=getKey(k);
jedis=jedisPool.getResource();
byte[] value = jedis.get(key);
jedis.del(key);
if(value!=null){
return (V) SerializationUtils.deserialize(value);
}
}finally {
if(jedis!=null){
jedis.close();
}
}
return null;
}
@Override
public void clear() throws CacheException {
//不需要重写,
}
@Override
public int size() {
return 0;
}
@Override
public Set<K> keys() {
return null;
}
@Override
public Collection<V> values() {
return null;
}
}
同样Shiro配置DefaultWebSecurityManager注入cacheManager
<!--创建SecurityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customerRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
<property name="rememberMeManager" ref="cookieRememberMeManager"/>
</bean>
<bean id="customerRealm" class="com.test.shiro.realm.CustomerRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<!--一次加密-->
<property name="hashIterations" value="1"/>
</bean>
<!--会话管理-->
<bean id="sessionManager" class="com.test.shiro.realm.CustomSessionManager">
<property name="sessionDAO" ref="redisSessionDao"/>
</bean>
<bean id="redisSessionDao" class="com.test.shiro.redis.RedisSessionDao"/>
<!--角色权限缓存管理-->
<bean id="cacheManager" class="com.test.shiro.cache.RedisCacheManager"/>
Cookie过期时间设置
<!--记住我功能-->
<bean id="cookieRememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="simpleCookie"/>
</bean>
<bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<!--Cookie的过期时间-->
<property name="maxAge" value="600"/>
</bean>
同样Shiro的配置文件中DefaultWebSecurityManager注入rememberMeManager
<!--创建SecurityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customerRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
<property name="rememberMeManager" ref="cookieRememberMeManager"/>
</bean>
登陆的位置UsernamePasswordToken设置Remember为true
public Object testLogin(User user, HttpServletRequest request, HttpServletResponse response){
logger.info("-----------登陆校验------------------");
Subject subject= SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(),user.getPassword());
//密码校验
try{
token.setRememberMe(true);
subject.login(token);
logger.info("是否已认证:"+subject.isAuthenticated());
}catch (Exception e){
logger.error("校验异常{}",e);
return "登陆失败";
}
return "登陆成功";
}