1. 程式人生 > >Shiro+Redis Session共享

Shiro+Redis Session共享

一、pom匯入包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>2.4.2.1-RELEASE</version>
</dependency>

二、配置

1、config

package com.cfl.account.admin.config;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

import javax.servlet.Filter;

import org.apache.shiro.mgt.SecurityManager;
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.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

@Configuration
@PropertySource(value={"classpath:config.properties"})
public class ShiroConfiguration {

	@Value("${redis_url}")
	private String redisUrl;
	
	@Value("${redis_port}")
	private String redisPort;
	
	@Value("${redis_password}")
	private String redisPassword;
	
	@Value("${reids_expire}")
	private String reidsExpire;
	
	@Value("${redis_timeout}")
	private String redisTimeout;
	
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 沒有登陸的使用者只能訪問登陸頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登入成功後要跳轉的連結
        shiroFilterFactoryBean.setSuccessUrl("/home");
        //自定義攔截器
        Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        shiroFilterFactoryBean.setFilters(filtersMap);
        // 許可權控制map.
        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("/login", "anon");
        map.put("/role_error", "anon");
	map.put("/static/**", "anon");
	map.put("/admin/**", "roles");
	map.put("/login/outLogin", "logout");	// 退出
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 設定realm.
        securityManager.setRealm(myShiroRealm());
        // 自定義快取實現 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定義session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 身份認證realm; (這個需要自己寫,賬號密碼校驗;許可權等)
     *
     * @return
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }

    /**
     * cacheManager 快取 redis實現
     * 使用的是shiro-redis開源外掛
     *
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis開源外掛
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(redisUrl);
        redisManager.setPort(Integer.valueOf(redisPort));
        redisManager.setExpire(Integer.valueOf(reidsExpire));// 配置快取過期時間
        redisManager.setTimeout(Integer.valueOf(redisTimeout));
        // redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis開源外掛
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao層的實現 通過redis
     * 使用的是shiro-redis開源外掛
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /***
     * 使授權註解起作用不如不想配置可以在pom檔案中加入
     * <dependency>
     *<groupId>org.springframework.boot</groupId>
     *<artifactId>spring-boot-starter-aop</artifactId>
     *</dependency>
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro生命週期處理器,必須加static,不然獲取不到spring裡面的東西,比如@Value註解
     *
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    
    /***
     * 授權所用配置
     *
     * @return
     */
    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        return defaultAdvisorAutoProxyCreator;
    }
    
    @Bean
    public static SimpleMappingExceptionResolver resolver() {
    	SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); 
    	Properties properties = new Properties(); 
    	properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/role_error"); 
    	resolver.setExceptionMappings(properties); 
    	return resolver;
    }
}

2、認證

package com.cfl.account.admin.config;

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

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.UsernamePasswordToken;
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 org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Reference;
import com.cfl.account.commons.result.APIResultDO;
import com.cfl.account.commons.result.ResultDO;
import com.cfl.account.interfaces.user.facade.UserFacade;
import com.cfl.account.model.vo.RoleVo;
import com.cfl.account.model.vo.UserVo;

@Component
public class MyShiroRealm extends AuthorizingRealm {

	@Reference
	private UserFacade userFacade;

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		Set<String> roleSet = new HashSet<String>(); // 角色
//		Set<String> permissionSet = new HashSet<String>(); // 許可權

		 ResultDO<RoleVo> resultDO = userFacade.findUserRoleByName(principals.toString());
		 if(APIResultDO.CODE_SUCCESS == resultDO.getResultCode()) {
			 // 登入成功
			 RoleVo roleVo = resultDO.getModule();
			 if(roleVo != null) {
				 roleSet.add(roleVo.getName());
			 }
		 }
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//		simpleAuthorizationInfo.setStringPermissions(permissionSet);
		simpleAuthorizationInfo.setRoles(roleSet);
		return simpleAuthorizationInfo;
	}

	/**
	 * 使用者認證
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
			throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
		String name = token.getUsername();
		String password = new String((char[]) token.getCredentials());
		ResultDO<UserVo> resultDO = userFacade.login(name, password);
		if (APIResultDO.CODE_SUCCESS == resultDO.getResultCode()) {
			// 登入成功
			return new SimpleAuthenticationInfo(token.getPrincipal(), password, getName());
		} else {
			throw new AuthenticationException(resultDO.getErrorMesssage());
		}
	}
}

3、解決使用註解RequiresRoles攔截頁面無法調整的問題

package com.cfl.account.admin.config;

import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class MyControllerAdvice {
	
	/**
     * 全域性異常捕捉處理,當shiro碰到AuthorizationException時進入到無許可權頁面或登入頁面 解決使用@RequiresRoles註解時bug
     * @param 
     * @return
     */
    @ExceptionHandler(value = AuthorizationException.class)
    public Object errorHandler(AuthorizationException ex) {
    	if(ex.getMessage().indexOf("Subject does not have role") != -1) {
    		// 無許可權頁面
    		return "/role_error";
    	} else {
    		// 登入頁面
    		return "redirect:/login";
    	}
    }
}