整合spring、shiro、redis实现会话共享

阅读更多

 什么是Shiro?

Apache旗下强大灵活的开源安全框架

提供认证、授权、会话管理、安全加密等功能

 

 

Shiro整体架构

 

SecurityManager Shiro的核心,Shiro通过SecurityManager 提供安全服务;

Authenticator 认证器,管理主体的登录、登出;

Authorizer 授权器,赋予主体有哪些权限;

SessionManager Session管理器,可以脱离web容器;

SessionDao 提供Session的增删改查操作;

CacheManager 缓存管理器,可以缓存主体的角色、权限数据;

Realm Shiro和数据源之间的桥梁,通过Realm获取认证、角色和权限数据等

 

主体提交请求到SecurityManagerSecurityManager调用Authenticator对主体进行认证。Authenticator通过Realm来获取认证信息,和主体提交的信息进行比对;

 

Shiro认证过程:

1、创建SecurityManager

2、主体提交认证;

3、SecurityManager认证;

4、Authenticator认证;

5、Realm认证;

 

代码实现:

1、pom中添加依赖


			org.springframework
			spring-webmvc
			${spring.version}
		
		
		
		
			org.aspectj
			aspectjweaver
			1.8.5
		
		
		
		
			org.apache.shiro
			shiro-spring
			1.4.0
		
		
		
		
		    org.springframework.data
		    spring-data-redis
		    1.7.1.RELEASE
		
		
			redis.clients
			jedis
			2.8.1
		

 

2、web.xml中配置shiro拦截器

  
  
	
		contextConfigLocation
		classpath:spring/spring-context.xml
	
	
	
		org.springframework.web.context.ContextLoaderListener
	
	
	
	
		shiroFilter
		org.springframework.web.filter.DelegatingFilterProxy
		
			targetFilterLifecycle
			true
		
	
	
		shiroFilter
		/*
	
	
	
	
		encodingFilter
		org.springframework.web.filter.CharacterEncodingFilter
		
			encoding
			UTF-8
		
		
			forceEncoding
			true
		
	
	
		encodingFilter
		/*
	
	
	
		dispatherServlet
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			classpath:spring/spring-servlet.xml
		
	
	
		dispatherServlet
		/
	
	
	
  	
  

 

3、自定义认证Realm,查询数据库认证信息

package com.huatech.support.shiro;

import java.util.HashSet;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;

import com.huatech.core.domain.User;
import com.huatech.core.service.PermService;
import com.huatech.core.service.RoleService;
import com.huatech.core.service.UserService;

@Component
public class SystemAuthorizingRealm extends AuthorizingRealm {
	
	@Resource UserService userService;
	@Resource RoleService roleService;
	@Resource PermService permService;

	/**
	 * 角色、权限认证
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		String username = (String) principals.getPrimaryPrincipal();
		Set roles = new HashSet(roleService.findByUsername(username));
		Set perms = new HashSet();
		if(roles != null && roles.size() > 0) {
			perms = new HashSet(permService.findByRoles(roles));
		}
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(roles);
		authorizationInfo.addStringPermissions(perms);
		return authorizationInfo;
	}

	/**
	 * 身份认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String username = (String) token.getPrincipal();
		User user = userService.findByUsername(username);
		if(user == null) {
			throw new UnauthenticatedException();
		}
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), getName());
		authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(username));
		return authenticationInfo;
	}
	
	@Override
	public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("md5");
		matcher.setHashIterations(1);
		super.setCredentialsMatcher(matcher);
	}
	
	
	@Override
	public String getName() {
		return this.getClass().getSimpleName();
	}

}

 

4、继承AbstractSessionDAO,实现Redis Session的增刪改查操作

 

package com.huatech.support.shiro;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

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.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;

/**
 * 继承AbstractSessionDAO,实现Redis Session的增刪改查操作
 * @author [email protected]
 * @since 2019-01-28
 *
 */
public class RedisSessionDao extends AbstractSessionDAO {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(RedisSessionDao.class);
	
	public static final String SESSION_PREFIX = "shiro_session:";
	public static final int DEFAILT_TIME_OUT = 30;
	@Resource RedisTemplate redisTemplate;
	//@Resource RedisUtil redisUtil;
	private int timeout = DEFAILT_TIME_OUT;
	
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	@Override
	protected Serializable doCreate(Session session) {
		Serializable id = generateSessionId(session);
		LOGGER.debug("id:{}", id.toString());
		assignSessionId(session, id);//将session 和 sessionId捆绑在一起
		saveSession(session);
		return id;
	}
	
	public void update(Session session) throws UnknownSessionException {
		LOGGER.debug("id:{}", session.getId().toString());
		saveSession(session);
	}

	public void delete(Session session) {
		LOGGER.debug("id:{}", session.getId().toString());
		if(session == null || session.getId() == null){
			return;
		}
		redisTemplate.delete(getKey(session.getId()));
	}
	
	public Collection getActiveSessions() {
		 Set keys = keys();
		 Set sessions = new HashSet();
		 if(keys.size() == 0){
			 return sessions;
		 }
		 for (String id : keys) {
			 Session _session = getSession(id);
			 if(_session == null){
				 continue;
			 }
			 sessions.add(_session);			 
		 }		 
		return sessions;
	}

	@Override
	protected Session doReadSession(Serializable sessionId) {
		if(sessionId == null){
			return null;
		}
		LOGGER.debug("id:{}", sessionId.toString());
		return getSession(sessionId);
	}
	
	private static String getKey(Serializable id){
		return SESSION_PREFIX + id.toString();
	}

	private void saveSession(Session session){
		if(session != null && session.getId() != null){
			Serializable id = session.getId();
			redisTemplate.opsForValue().set(getKey(id), session, timeout, TimeUnit.MINUTES);
		}
	}
	
	private Session getSession(Serializable id){
		return (Session)redisTemplate.boundValueOps(getKey(id)).get();
	}
	
	private Set keys(){		  
    	return redisTemplate.execute(new RedisCallback>() {
			public Set doInRedis(RedisConnection connection) throws DataAccessException {
				Set binaryKeys = new HashSet();
    	        Cursor cursor = connection.scan( new ScanOptions.ScanOptionsBuilder().match(SESSION_PREFIX + "*").count(1000).build());
    	        while (cursor.hasNext()) {    	        	
    	            binaryKeys.add(new String(cursor.next()));
    	        }
    	        connection.close();
    	        return binaryKeys;
			}
    	});    
	}
	 
}

 

5、实现用户角色、权限缓存管理

 

package com.huatech.support.shiro;

import java.util.Collection;
import java.util.Set;

import javax.annotation.Resource;

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.data.redis.core.RedisTemplate;

import com.alibaba.fastjson.JSONObject;

/**
 * 实现用户角色、权限缓存管理
 * @author [email protected]
 *
 */
public class RedisCacheManager implements CacheManager {
	
	@Resource RedisTemplate redisTemplate;
	private static final Logger LOGGER = LoggerFactory.getLogger(RedisCacheManager.class);
	//@Resource RedisUtil redisUtil;
	
	@Override
	public  Cache getCache(String arg0) throws CacheException {
		return new RedisCache();
	}
	
	class RedisCache implements Cache{

		private static final String CACHE_KEY = "redis-cache";
		
		@Override
		public void clear() throws CacheException {
			redisTemplate.delete(CACHE_KEY);
		}

		private String toString(Object obj){
			if(obj instanceof String){
				return obj.toString();
			}else{
				return JSONObject.toJSONString(obj);
			}
		}

		@SuppressWarnings("unchecked")
		@Override
		public V get(K k) throws CacheException {
			LOGGER.info("get field:{}", toString(k));
			return (V)redisTemplate.boundHashOps(CACHE_KEY).get(k);
		}

		@SuppressWarnings("unchecked")
		@Override
		public Set keys() {
			LOGGER.info("keys");
			return (Set)redisTemplate.boundHashOps(CACHE_KEY).keys();
		}

		@Override
		public V put(K k, V v) throws CacheException {
			LOGGER.info("put field:{}, value:{}", toString(k), toString(v));
			redisTemplate.boundHashOps(CACHE_KEY).put(k, v);
			return v;
		}

		@Override
		public V remove(K k) throws CacheException {
			LOGGER.info("remove field:{}", toString(k));
			V v = get(k);
			redisTemplate.boundHashOps(CACHE_KEY).delete(k);
			return v;
		}

		@Override
		public int size() {
			int size = redisTemplate.boundHashOps(CACHE_KEY).size().intValue();
			LOGGER.info("size:{}", size);
			return size;
		}

		@SuppressWarnings("unchecked")
		@Override
		public Collection values() {
			LOGGER.info("values");
			return (Collection)redisTemplate.boundHashOps(CACHE_KEY).values();
		}
		
		public String getCacheKey() {
			return "RedisCache";
		}
		
	}

}

 

6、重写DefaultWebSessionManager的retrieveSession方法,防止一个接口重复读取redis的session

 

package com.huatech.support.shiro;

import java.io.Serializable;

import javax.servlet.ServletRequest;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 重写DefaultWebSessionManager的retrieveSession方法,防止一个接口重复读取redis的session
 * @author [email protected]
 *
 */
public class RedisSessionManager extends DefaultWebSessionManager {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(RedisSessionManager.class);
	
	@Override
	protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
		Serializable sessionId = getSessionId(sessionKey);
		ServletRequest request = null;
		if(sessionKey instanceof WebSessionKey){
			request = ((WebSessionKey)sessionKey).getServletRequest();			
		}
		Session session = null;
		if(request != null && sessionId != null){
			session =  (Session) request.getAttribute(sessionId.toString());
		}
		if(session != null){
			return session;
		}
		try{
			session = super.retrieveSession(sessionKey);
		}catch(UnknownSessionException e){
			LOGGER.error(e.getMessage());
		}
		if(request != null && sessionId != null && session != null){
			request.setAttribute(sessionId.toString(), session);			
		}
		
		return session;
	}
	
}

 

7、spirng-servlet配置,启用shiro注解

 

  

        
      
    
    	
			
		    	
			
		
      
    
      
          
          
          
    
    
    
    
	
	
    
    	
	
	
  

 

8、Shiro配置

 

  

    
    
	
		
		
		
		
			
				/login.jsp = anon
				/doLogin = anon
				/rolesOr = rolesOr["admin","admin1"]
				/roleAll = roles["admin", "admin1"]
				/* = authc
			
		
		
			
				
			
		
	
	
	
	
	
	
		
		
		
		
	
	
	

	
		
	
	
	
		
	
	
	
	
	
	
	
		
		
	
		
	
	
    
  

 

9、Redis配置

 

  

     
    
		 
		
		
	
 	
         
         
         
         
         
 	
 	
 	
		 
	
 	
    
  

 

代码已全部上传到gitee,完整代码请参考:https://gitee.com/hualee/spring4-shiro-redis-demo

 

  • 整合spring、shiro、redis实现会话共享_第1张图片
  • 大小: 665.6 KB
  • 查看图片附件

你可能感兴趣的:(spring,shiro,redis)