ssm+freemark集成shiro

Posted Nightliar

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ssm+freemark集成shiro相关的知识,希望对你有一定的参考价值。


1.导入的jar包

		<!-- shiro start -->
		<!-- freemarker + shiro(标签) begin -->
		<dependency>
		    <groupId>net.mingsoft</groupId>
		    <artifactId>shiro-freemarker-tags</artifactId>
		    <version>0.1</version>
		</dependency>
		<!-- freemarker + shiro(标签) end -->
		<dependency>  
	        <groupId>org.apache.shiro</groupId>  
	        <artifactId>shiro-core</artifactId>  
	    </dependency>  
	    <dependency>  
	        <groupId>org.apache.shiro</groupId>  
	        <artifactId>shiro-web</artifactId>  
	    </dependency>  
	    <dependency>  
	        <groupId>org.apache.shiro</groupId>  
	        <artifactId>shiro-ehcache</artifactId>  
	    </dependency>  
	    <dependency>  
	        <groupId>org.apache.shiro</groupId>  
	        <artifactId>shiro-spring</artifactId>  
	    </dependency>
	    <!-- shiro end -->
2.在web.xml中加入shiro filter

    <!--Shiro过滤器-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
此过滤器要放在第一个,且名称要与spring-shiro,xml中shiro filter一致

3.在freemarker中加入shiro标签

3.1新建一个FreeMarkerConfigExtend类继承FreeMarkerConfigurer,

package com.business.util;

import java.io.IOException;

import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import com.jagregory.shiro.freemarker.ShiroTags;

import freemarker.template.Configuration;
import freemarker.template.TemplateException;

public class FreeMarkerConfigExtend extends FreeMarkerConfigurer {
	
	@Override  
	public void afterPropertiesSet() throws IOException, TemplateException {  
        super.afterPropertiesSet();
        Configuration cfg = this.getConfiguration();
        cfg.setSharedVariable("shiro", new ShiroTags());//shiro标签
        cfg.setNumberFormat("#");//防止页面输出数字,变成2,000
        //可以添加很多自己的要传输到页面的[方法、对象、值]
        
        
        /*
         * 在controller层使用注解再加一层判断
         * 注解 @RequiresPermissions("/delete")
         */
        
        /*shiro标签*/
        /**
        1.游客
        <@shiro.guest>  
        	您当前是游客,<a href="javascript:void(0);" class="dropdown-toggle qqlogin" >登录</a>
        </@shiro.guest> 
		
		2.user(已经登录,或者记住我登录)
		<@shiro.user>  
			欢迎[<@shiro.principal/>]登录,<a href="/logout.shtml">退出</a>  
		</@shiro.user>   

		3.authenticated(已经认证,排除记住我登录的)
		<@shiro.authenticated>  
			用户[<@shiro.principal/>]已身份验证通过  
		</@shiro.authenticated>   		
		
		4.notAuthenticated(和authenticated相反)
		<@shiro.notAuthenticated>
		          当前身份未认证(包括记住我登录的)
		</@shiro.notAuthenticated> 
		
		5.principal标签(能够取到你在realm中保存的信息比如我存的是ShiroUser对象,取出其中urlSet属性)
		<!--需要指定property-->
		<@shiro.principal property="urlSet"/>
		
		6.hasRole标签(判断是否拥有这个角色)
		<@shiro.hasRole name="admin">  
			用户[<@shiro.principal/>]拥有角色admin<br/>  
		</@shiro.hasRole> 
		
		7.hasAnyRoles标签(判断是否拥有这些角色的其中一个)
		<@shiro.hasAnyRoles name="admin,user,member">  
			用户[<@shiro.principal/>]拥有角色admin或user或member<br/>  
		</@shiro.hasAnyRoles> 
		
		8.lacksRole标签(判断是否不拥有这个角色)
		<@shiro.lacksRole name="admin">  
			用户[<@shiro.principal/>]不拥有admin角色
		</@shiro.lacksRole> 
		
		9.hasPermission标签(判断是否有拥有这个权限)
		<@shiro.hasPermission name="user:add">  
			用户[<@shiro.principal/>]拥有user:add权限
		</@shiro.hasPermission>
		
		10.lacksPermission标签(判断是否没有这个权限)
		<@shiro.lacksPermission name="user:add">  
			用户[<@shiro.principal/>]不拥有user:add权限
		</@shiro.lacksPermission> 
		
		**/
    }  
}
3.2修改spring-mvc-servlet.xml中的freemarker配置


4.新建CustomCredentialsMatcher类继承shiro的SimpleCredentialsMatcher类,这个类作用是自定义密码验证

package com.business.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

import com.business.util.MD5Util;

/** 
 * Description: 告诉shiro如何验证加密密码,通过SimpleCredentialsMatcher或HashedCredentialsMatcher 
 * @Author: zh 
 * @Create Date: 2017-5-9 
 */  
  
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {  
      
    @Override   
    public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {    
             
	    UsernamePasswordToken token = (UsernamePasswordToken) authcToken; 
	    
	    
	    Object tokenCredentials = MD5Util.hmac_md5(String.valueOf(token.getPassword()));
        Object accountCredentials = getCredentials(info);
        //将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false
        return equals(tokenCredentials, accountCredentials);
    }  
    
}
5.新建ShiroDbRealm类
package com.business.shiro;

import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import com.business.dao.UserDao;
import com.business.entity.Menu;
import com.business.entity.Role;
import com.business.entity.User;
import com.business.entity.UserRole;
import com.business.service.sysService.MenuService;
import com.business.service.sysService.RoleService;
import com.business.service.sysService.UserRoleService;
import com.business.service.sysService.UserService;
import com.business.util.SessionUtil;
import com.common.util.BizUtil;
import com.google.common.collect.Sets;

/**
 * @description:shiro权限认证
 * @author:zhanghao
 * @date:2017/5/8 14:51
 */
public class ShiroDbRealm extends AuthorizingRealm {
    private static final Logger LOGGER = Logger.getLogger(ShiroDbRealm.class);

    @Autowired private UserService userService;
    @Autowired private UserDao userDao;
    @Autowired private RoleService roleService;
    @Autowired private UserRoleService userRoleService;
    @Autowired private MenuService menuService;
    
    public ShiroDbRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        super(cacheManager, matcher);
    }
    
    /**
     * Shiro登录认证(原理:用户提交 用户名和密码  --- shiro 封装令牌 ---- realm 通过用户名将密码查询返回 ---- shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登陆控制 )
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) throws AuthenticationException {
        LOGGER.info("Shiro开始登录认证");
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        User user = userDao.getByName(token.getUsername());
        // 账号不存在
        if (user == null) {
            return null;
        }
        // 账号未启用
        if (user.getStatus() == 1) {
            throw new DisabledAccountException();
        }
        //将用户信息保存在session中
        SessionUtil.addSession(user);
        UserRole userRole = userRoleService.getByUserId(user.getId());
        Role role = roleService.getById(userRole.getRoleId());
        // 读取用户的url和角色
        Set<String> roles = Sets.newHashSet(role.getName());
        List<Long> menuIds = BizUtil.stringToLongList(role.getMenu(), ",");
        List<Menu> menuList = menuService.getListByIds(menuIds);
        List<String> menuStr = BizUtil.extractToList(menuList, "url");
        
        Set<String> urls = Sets.newHashSet(menuStr);
        urls.remove("");
        urls.remove(null);
        
        ShiroUser shiroUser = new ShiroUser(user.getId(), user.getLoginName(), user.getUsername(), urls);
        shiroUser.setRoles(roles);
        // 认证缓存信息
        return new SimpleAuthenticationInfo(shiroUser, user.getPassword().toCharArray(),getName());
    }

    /**
     * Shiro权限认证
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
        
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(shiroUser.getRoles());
        info.addStringPermissions(shiroUser.getUrlSet());
        
        return info;
    }
    
    @Override
    public void onLogout(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
        removeUserCache(shiroUser);
    }

    /**
     * 清除用户缓存
     * @param shiroUser
     */
    public void removeUserCache(ShiroUser shiroUser){
        removeUserCache(shiroUser.getLoginName());
    }

    /**
     * 清除用户缓存
     * @param loginName
     */
    public void removeUserCache(String loginName){
        SimplePrincipalCollection principals = new SimplePrincipalCollection();
        principals.add(loginName, super.getName());
        super.clearCachedAuthenticationInfo(principals);
    }
    
	@PostConstruct
	public void initCredentialsMatcher() {
		//该句作用是重写shiro的密码验证,让shiro用我自己的验证-->指向重写的CustomCredentialsMatcher
		setCredentialsMatcher(new CustomCredentialsMatcher());

	}
}
6.自定义shiroUser

package com.business.shiro;

import java.io.Serializable;
import java.util.Set;

/**
 * @description:自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
 * @author:zhanghao
 * @date:2017/5/9
 */
public class ShiroUser implements Serializable {
    private static final long serialVersionUID = -1373760761780840081L;
    
    private Long id;
    private final String loginName;
    private String name;
    private Set<String> urlSet;
    private Set<String> roles;

    public ShiroUser(String loginName) {
        this.loginName = loginName;
    }

    public ShiroUser(Long id, String loginName, String name, Set<String> urlSet) {
        this.id = id;
        this.loginName = loginName;
        this.name = name;
        this.urlSet = urlSet;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<String> getUrlSet() {
        return urlSet;
    }

    public void setUrlSet(Set<String> urlSet) {
        this.urlSet = urlSet;
    }

    public Set<String> getRoles() {
        return roles;
    }

    public void setRoles(Set<String> roles) {
        this.roles = roles;
    }

    public String getLoginName() {
        return loginName;
    }

    /**
     * 本函数输出将作为默认的<shiro:principal/>输出.
     */
    @Override
    public String toString() {
        return loginName;
    }
}
7.两个缓存类

package com.business.shiro.cache;

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

import org.apache.log4j.Logger;
import org.apache.shiro.cache.CacheException;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;

/**
 * 使用spring-cache作为shiro缓存
 * @author L.cm
 *
 */
@SuppressWarnings("unchecked")
public class ShiroSpringCache<K, V> implements org.apache.shiro.cache.Cache<K, V> {
	private static final Logger logger = Logger.getLogger(ShiroSpringCache.class);
	
	private final org.springframework.cache.Cache cache;
	
	public ShiroSpringCache(Cache cache) {
		if (cache == null) {
			throw new IllegalArgumentException("Cache argument cannot be null.");
		}
		this.cache = cache;
	}

	@Override
	public V get(K key) throws CacheException {
		if (logger.isTraceEnabled()) {
			logger.trace("Getting object from cache [" + this.cache.getName() + "] for key [" + key + "]key type:" + key.getClass());
		}
		ValueWrapper valueWrapper = cache.get(key);
		if (valueWrapper == null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Element for [" + key + "] is null.");
			}
			return null;
		}
		return (V) valueWrapper.get();
	}

	@Override
	public V put(K key, V value) throws CacheException {
		if (logger.isTraceEnabled()) {
			logger.trace("Putting object in cache [" + this.cache.getName() + "] for key [" + key + "]key type:" + key.getClass());
		}
		V previous = get(key);
		cache.put(key, value);
		return previous;
	}

	@Override
	public V remove(K key) throws CacheException {
		if (logger.isTraceEnabled()) {
			logger.trace("Removing object from cache [" + this.cache.getName() + "] for key [" + key + "]key type:" + key.getClass());
		}
		V previous = get(key);
		cache.evict(key);
		return previous;
	}

	@Override
	public void clear() throws CacheException {
		if (logger.isTraceEnabled()) {
			logger.trace("Clearing all objects from cache [" + this.cache.getName() + "]");
		}
		cache.clear();
	}

	@Override
	public int size() {
		return 0;
	}

	@Override
	public Set<K> keys() {
		return Collections.emptySet();
	}

	@Override
	public Collection<V> values() {
		return Collections.emptySet();
	}

	@Override
	public String toString() {
		return "ShiroSpringCache [" + this.cache.getName() + "]";
	}
}
package com.business.shiro.cache;

import org.apache.log4j.Logger;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.Destroyable;

/**
 * 使用spring-cache作为shiro缓存
 * 缓存管理器
 * @author L.cm
 *
 */
public class ShiroSpringCacheManager implements CacheManager, Destroyable {
	private static final Logger logger = Logger.getLogger(ShiroSpringCacheManager.class);
	private org.springframework.cache.CacheManager cacheManager;
	
	public org.springframework.cache.CacheManager getCacheManager() {
		return cacheManager;
	}

	public void setCacheManager(org.springframework.cache.CacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}

	@Override
	public <K, V> Cache<K, V> getCache(String name) throws CacheException {
		if (logger.isTraceEnabled()) {
			logger.trace("Acquiring ShiroSpringCache instance named [" + name + "]");
		}
		org.springframework.cache.Cache cache = cacheManager.getCache(name);
		return new ShiroSpringCache<K, V>(cache);
	}

	@Override
	public void destroy() throws Exception {
		cacheManager = null;
	}

}
8.spring-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd"
    default-lazy-init="true">

    <description>Shiro安全配置</description>


    <!--安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--设置自定义Realm-->
        <property name="realm" ref="shiroDbRealm"/>
        <!--将缓存管理器,交给安全管理器-->
        <property name="cacheManager" ref="shiroSpringCacheManager"/>
        <!-- 记住密码管理 -->
        <property name="rememberMeManager" ref="rememberMeManager"/>
        <property name="sessionManager" ref="sessionManager"/>
    </bean>

    <!-- 項目自定义的Realm -->
    <bean id="shiroDbRealm" class="com.business.shiro.ShiroDbRealm">
        <constructor-arg index="0" name="cacheManager" ref="shiroSpringCacheManager"/>
        <constructor-arg index="1" name="matcher" ref="credentialsMatcher"/>
        <!-- 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false -->
        <property name="authenticationCachingEnabled" value="true"/>
        <!-- 缓存AuthenticationInfo信息的缓存名称 -->
        <property name="authenticationCacheName" value="authenticationCache"/>
        <!-- 缓存AuthorizationInfo信息的缓存名称 -->
        <property name="authorizationCacheName" value="authorizationCache"/>
    </bean>

    <!-- 记住密码Cookie -->
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">  
        <constructor-arg value="rememberMe"/>
        <property name="httpOnly" value="true"/>
        <!-- 7天,采用spring el计算方便修改[细节决定成败]! -->
        <property name="maxAge" value="#{7 * 24 * 60 * 60}"/>
    </bean>

    <!-- rememberMe管理器  -->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie"/>  
    </bean>

    <!-- Shiro Filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 安全管理器 -->
        <property name="securityManager" ref="securityManager"/>
        <!-- 默认的登陆访问url -->
        <property name="loginUrl" value="/login"/>
        <!-- 登陆成功后跳转的url -->
        <property name="successUrl" value="/menu/index"/>
        <!-- 没有权限跳转的url -->
        <property name="unauthorizedUrl" value="/login"/>
        <property name="filterChainDefinitions">
            <value>
                <!-- 
                    anon  不需要认证
                    authc 需要认证
                    user  验证通过或RememberMe登录的都可以
                -->
                /login = anon
                /favicon.ico = anon
                /static/** = anon
                /** = user
            </value>
        </property>
    </bean>

    <!-- 用户授权信息Cache, 采用spring-cache, 具体请查看spring-ehcache.xml、spring-redis.xml -->
    <bean id="shiroSpringCacheManager" class="com.business.shiro.cache.ShiroSpringCacheManager">
        <property name="cacheManager" ref="cacheManager"/>
    </bean>
    
    <!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- 设置全局会话超时时间 半小时 -->
        <property name="globalSessionTimeout" value="#{30 * 60 * 1000}"/>
        <!-- url上带sessionId 默认为true -->
<!--         <property name="sessionIdUrlRewritingEnabled" value="false"/> -->
        <property name="sessionDAO" ref="sessionDAO"/>
    </bean>
    
    <!-- 会话DAO 用于会话的CRUD -->
    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <!-- Session缓存名字,默认就是shiro-activeSessionCache -->
        <property name="activeSessionsCacheName" value="activeSessionCache"/>
        <property name="cacheManager" ref="shiroSpringCacheManager"/>
    </bean>
    
    <!-- 在方法中 注入  securityManager ,进行代理控制 -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>
    
    <!-- 自定义验证 -->
    <bean id="credentialsMatcher" class="com.business.shiro.CustomCredentialsMatcher"></bean>
    
</beans>
9.如果要再controller层中加入注解判断,还需在spring-mvc-servlet.xml中加入

    <!-- 启用shrio 控制器授权注解拦截方式 -->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <!-- AOP式方法级权限检查  -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor">
          <property name="proxyTargetClass" value="true"/>
    </bean>
10.缓存的两个xml文件ehcache.xml,spring-ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
    <diskStore path="java.io.tmpdir"/>
    
   <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
    
    <cache name="activeSessionCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
    
    <!-- 缓存半小时 -->
    <cache name="halfHour" 
        maxElementsInMemory="10000"
        maxElementsOnDisk="100000" 
        eternal="false" 
        timeToIdleSeconds="1800"
        timeToLiveSeconds="1800" 
        overflowToDisk="false" 
        diskPersistent="false" />
        
    <!-- 缓存一小时 -->
    <cache name="hour" 
        maxElementsInMemory="10000"
        maxElementsOnDisk="100000" 
        eternal="false" 
        timeToIdleSeconds="3600"
        timeToLiveSeconds="3600" 
        overflowToDisk="false" 
        diskPersistent="false" />
    
    <!-- 缓存一天 -->
    <cache name="oneDay" 
        maxElementsInMemory="10000"
        maxElementsOnDisk="100000" 
        eternal="false" 
        timeToIdleSeconds="86400"
        timeToLiveSeconds="86400" 
        overflowToDisk="false" 
        diskPersistent="false" />
    
    <!--
        name:缓存名称。
        maxElementsInMemory:缓存最大个数。
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
        maxElementsOnDisk:硬盘最大缓存个数。
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。   
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
    -->
    <defaultCache name="defaultCache"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="false"
        maxElementsOnDisk="100000"
        diskPersistent="false"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU"/>
        
</ehcache>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!-- Spring提供的基于的Ehcache实现的缓存管理器 -->
    
    <!-- 如果有多个ehcacheManager要在bean加上p:shared="true" -->
    <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:spring/ehcache.xml"/>
    </bean>
    
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcacheManager"/>
        <property name="transactionAware" value="true"/>
    </bean>
    
    <!-- cache注解,和spring-redis.xml中的只能使用一个 -->
    <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
</beans>
11.applicationContext.xml导入配置文件

	<import resource="classpath:spring/spring-shiro.xml" />
	<import resource="classpath:spring/spring-ehcache.xml" />
12.LoginController控制层方法

package com.business.controller.system;

import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
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.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.business.controller.BaseController;
import com.business.service.sysService.UserService;
import com.business.util.SessionUtil;
import com.google.common.collect.Maps;

@Controller
public class LoginController extends BaseController {

    @Resource
    private UserService userService;
    
    @RequestMapping(value = "/login",method=RequestMethod.GET)
    public String login() {
        return "login";
    }

    @RequestMapping(value = "/login",method=RequestMethod.POST)
    public String login(RedirectAttributes attributes, String username, String password, @RequestParam(value = "online", defaultValue = "0") Integer rememberMe) {
        
        Map<String, Object> map = Maps.newHashMap();
        if (StringUtils.isBlank(username)) {
            map.put("errorInfo", "用户名不能为空");
        }
        if (StringUtils.isBlank(password)) {
            map.put("errorInfo", "密码不能为空");
        }
        Subject user = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, username+password);
        // 设置记住密码
        token.setRememberMe(1 == rememberMe);
        try {
            user.login(token);
            return "redirect:/menu/index";
        } catch (UnknownAccountException e) {
            map.put("errorInfo", "账号不存在!");
            attributes.addFlashAttribute("error", map);
            return "redirect:/login";
        } catch (DisabledAccountException e) {
            map.put("errorInfo", "账号未启用!");
            attributes.addFlashAttribute("error", map);
            return "redirect:/login";
        } catch (IncorrectCredentialsException e) {
            map.put("errorInfo", "密码错误!");
            attributes.addFlashAttribute("error", map);
            return "redirect:/login";
        } catch (Throwable e) {
            map.put("errorInfo", "登录异常!");
            attributes.addFlashAttribute("error", map);
            return "redirect:/login";
        }
    }
    
    @RequestMapping(value = "/logout")
    public String logout() {
        SessionUtil.removeSession();
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login";
    }
}
12.如何在控制层使用注解,可以在baseController中加入总的异常处理

	@ExceptionHandler  
    public String exception(HttpServletRequest request, Exception e) {  
        //对异常进行判断做相应的处理  
        if(e instanceof AuthorizationException){  
            return "redirect:/logout";  
        }
        return null;
	}















以上是关于ssm+freemark集成shiro的主要内容,如果未能解决你的问题,请参考以下文章

Shiro + SSM(框架) + Freemarker(jsp)

SSM+Redis+Shiro+Maven框架搭建及集成应用

java SSM框架 代码生成器 websocket即时通讯 shiro redis 后台框架源码

java SSM框架 代码生成器 websocket即时通讯 shiro redis 后台框架源码

java SSM框架 代码生成器 websocket即时通讯 shiro redis 后台框架源码

java SSM框架 多数据源 代码生成器 websocket即时通讯 shiro redis 后台