spring boot shiro redis整合

spring boot redis整合spring boot redis整合

spring boot shiro整合  spring boot shiro整合

在上篇整合shiro 的基础上 shiroConfig配置修改如下 红色部分为新增,整合中从redis中获取session时出现类型转换错误在本文结尾有说明及解决方法

需要注意的是我这里的实现 是session实现了redis缓存管理,但是其他的缓存还是存放在ehcache里,以后有时间会写把其他的缓存数据也放到redis 的文章

package com.zyc.springboot.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
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.CookieRememberMeManager;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy;

import com.zyc.springboot.shiro.MyFormAuthenticationFilter;
import com.zyc.springboot.shiro.MyRealm;
import com.zyc.springboot.shiro.MyShiroSessionListener;
import com.zyc.springboot.shiro.RedisUtil;
import com.zyc.springboot.shiro.SessionDao;

@Configuration
public class ShiroConfig {

	@Bean(name = "shiroEhcacheManager")
	public EhCacheManager getEhCacheManager() {
		EhCacheManager em = new EhCacheManager();
		em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
		return em;
	}
	
	@Bean(name = "lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor();
		return lifecycleBeanPostProcessor;
	}
	
	@Bean(name = "sessionValidationScheduler")
	public ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() {
		ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
		scheduler.setInterval(900000);
		//scheduler.setSessionManager(defaultWebSessionManager());
		return scheduler;
	}
	
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(1);// 散列的次数,比如散列两次,相当于md5(md5(""));
		return hashedCredentialsMatcher;
	}
	
	@Bean(name = "defaultWebSecurityManager")
	public DefaultWebSecurityManager defaultWebSecurityManager(MyRealm myRealm,DefaultWebSessionManager defaultWebSessionManager) {
		DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
		defaultWebSecurityManager.setRealm(myRealm);
		defaultWebSecurityManager.setCacheManager(getEhCacheManager());
		defaultWebSecurityManager.setSessionManager(defaultWebSessionManager);
		defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
		return defaultWebSecurityManager;
	}
	
	@Bean(name = "rememberMeCookie")
	public SimpleCookie rememberMeCookie() {
		// 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		// 
		simpleCookie.setMaxAge(259200);
		return simpleCookie;
	}
	
	/**
	 * cookie管理对象;
	 * 
	 * @return
	 */
	@Bean(name = "rememberMeManager")
	public CookieRememberMeManager rememberMeManager() {
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		return cookieRememberMeManager;
	}
	

	@Bean
	@DependsOn(value = "lifecycleBeanPostProcessor")
	public MyRealm myRealm() {
		MyRealm myRealm = new MyRealm();
		myRealm.setCacheManager(getEhCacheManager());
		myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myRealm;
	}

	@Bean
	@DependsOn("lifecycleBeanPostProcessor")
	public DefaultAdvisorAutoProxyCreator getAutoProxyCreator(){
		DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
		creator.setProxyTargetClass(true);
		return creator;
	}
	
	@Bean
	public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {
		AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
		aasa.setSecurityManager(defaultWebSecurityManager);
		return aasa;
	}
	

	@Bean(name = "sessionManager")
	public DefaultWebSessionManager defaultWebSessionManager(SessionDao sessionDao) {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setGlobalSessionTimeout(18000000);
//		//url中是否显示session Id
		sessionManager.setSessionIdUrlRewritingEnabled(false);
//		// 删除失效的session
		sessionManager.setDeleteInvalidSessions(true);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		sessionManager.setSessionValidationInterval(18000000);
		sessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler());
		//设置SessionIdCookie 导致认证不成功,不从新设置新的cookie,从sessionManager获取sessionIdCookie
		//sessionManager.setSessionIdCookie(simpleIdCookie());
		sessionManager.getSessionIdCookie().setName("session-z-id");
		sessionManager.getSessionIdCookie().setPath("/");
		sessionManager.getSessionIdCookie().setMaxAge(60*60*24*7);
		
		sessionManager.setSessionDAO(sessionDao);
		Collection c=new ArrayList<>();
		c.add(new MyShiroSessionListener());
		sessionManager.setSessionListeners(c);
		
		return sessionManager;
	}
	
	 @Bean
	 public SessionDao sessionDao(RedisUtil redisUtil) {
	 SessionDao sessionDao = new SessionDao();
	 sessionDao.setRedisUtil(redisUtil);
	 return sessionDao;
	 }
	
	 @Bean
	 public RedisUtil redisUtil(RedisTemplate redisTemplate) {
	 RedisUtil redisUtil = new RedisUtil();
	 redisUtil.setRedisTemplate(redisTemplate);
	 return redisUtil;
	 }

	@Bean(name = "filterRegistrationBean1")
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
		filterRegistrationBean.setFilter(new DelegatingFilterProxy(
				"shiroFilter"));
		filterRegistrationBean
				.addInitParameter("targetFilterLifecycle", "true");
		filterRegistrationBean.setEnabled(true);
		filterRegistrationBean.addUrlPatterns("/");
		return filterRegistrationBean;
	}

	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(
			@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
		// SecurityUtils.setSecurityManager(defaultWebSecurityManager);
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setLoginUrl("/login");
		shiroFilterFactoryBean.setSuccessUrl("/getMyJsp");
		shiroFilterFactoryBean.setUnauthorizedUrl("/login");
		shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
		Map filterMap1 = shiroFilterFactoryBean.getFilters();
		filterMap1.put("authc", new MyFormAuthenticationFilter());
		shiroFilterFactoryBean.setFilters(filterMap1);
		Map filterMap = new LinkedHashMap();
		filterMap.put("/static/**", "anon");
		filterMap.put("/logout", "logout");
		filterMap.put("/login", "authc");
		filterMap.put("/**", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		return shiroFilterFactoryBean;

	}

}

sessionDao 需要自己添加继承EnterpriseCacheSessionDAO:

package com.zyc.springboot.shiro;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;


public class SessionDao extends EnterpriseCacheSessionDAO {
	
	private RedisUtil redisUtil;
	public RedisUtil getRedisUtil() {
		return redisUtil;
	}

	public void setRedisUtil(RedisUtil redisUtil) {
		this.redisUtil = redisUtil;
	}

	// 创建session,保存到数据库
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = super.doCreate(session);
        redisUtil.set(sessionId.toString(), sessionToByte(session),1*60L);
        
        return sessionId;
    }

    // 获取session
    @Override
    protected Session doReadSession(Serializable sessionId) {
        // 先从缓存中获取session,如果没有再去数据库中获取
        Session session = super.doReadSession(sessionId); 
        if(session == null){
            byte[] bytes = (byte[]) redisUtil.get(sessionId.toString());
            if(bytes != null && bytes.length > 0){
                session = byteToSession(bytes);    
            }
        }
        return session;
    }

    // 更新session的最后一次访问时间
    @Override
    protected void doUpdate(Session session) {
        super.doUpdate(session);
        redisUtil.set(session.getId().toString(), sessionToByte(session),1*60L);
    }

    // 删除session
    @Override
    protected void doDelete(Session session) {
        super.doDelete(session);
        redisUtil.remove(session.getId().toString());
    }

    // 把session对象转化为byte保存到redis中
    public byte[] sessionToByte(Session session){
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        byte[] bytes = null;
        try {
            ObjectOutput oo = new ObjectOutputStream(bo);
            oo.writeObject(session);
            bytes = bo.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }
    
    // 把byte还原为session
    public Session byteToSession(byte[] bytes){
        ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
        ObjectInputStream in;
        SimpleSession session = null;
        try {
            in = new ObjectInputStream(bi);
            session = (SimpleSession) in.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        return session;
    }
}

ShiroSessionListener需要自己添加 如下:

package com.zyc.springboot.shiro;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;

import com.zyc.springboot.util.SpringContext;

public class MyShiroSessionListener implements SessionListener {

	@Override
	public void onStart(Session session) {
		
	}

	@Override
	public void onStop(Session session) {
		// TODO Auto-generated method stub
		System.out.println("onStop==="+session.getId());
		RedisUtil redisUtil=(RedisUtil) SpringContext.getBean("redisUtil");
		redisUtil.remove(session.getId().toString());
	}

	@Override
	public void onExpiration(Session session) {
		System.out.println("onExpiration==="+session.getId());
		RedisUtil redisUtil=(RedisUtil) SpringContext.getBean("redisUtil");
		redisUtil.remove(session.getId().toString());

	}

}

这里需要注意,redisUtil类需要在配置文件中使用@Bean配置,如果直接在RedisUtil类头部添加@Service注解 可能redisUtil会报空指针错误

如果从redis获取session时 无法把String类型转换成byte[] 类型,则修改RedisConfig中redisTemplate.setValueSerializer()为JdkSerializationRedisSerializer类型,本例中使用的是Jackson2JsonRedisSerializer

问题1:shiro 调用subject.logout();时返回登录界面,但是浏览器地址却不是登录地址,或者退出后后需要点击2次登录才能登录成功

解决:使用springmvc 时,在Controller 层写logout方法 返回地址 使用重定向 return "redirect:login" 代替 return "login";这里注意 你的shiro 配置中需要有 /logout=logout

你可能感兴趣的:(spring-boot)