1.配置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>



 

2.web.xml添加shiro过滤器



<!-- 
    1. 配置  Shiro 的 shiroFilter.  
    2. DelegatingFilterProxy 实际上是 Filter 的一个代理对象. 默认情况下, Spring 会到 IOC 容器中查找和 
    <filter-name> 对应的 filter bean. 也可以通过 targetBeanName 的初始化参数来配置 filter bean 的 id. 
    -->
    <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>
       <!--  <init-param>    
            <param-name>targetBeanName</param-name>    
            <param-value>abc</param-value>    
        </init-param>  -->
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>



3.配置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" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">  
    
   <!--  
    1. 配置 SecurityManager!
    -->     
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"></property>
        
        <property name="realms">
            <list>
                <ref bean="jdbcRealm"/>
                <!-- <ref bean="secondRealm"/> -->
            </list>
        </property>
        
        <property name="rememberMeManager.cookie.maxAge" value="10"></property>
    </bean>

   
    <!--  
    2. 配置 CacheManager. 
    2.1 需要加入 ehcache 的 jar 包及配置文件. 
    -->     
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        
        <property name="cacheManagerConfigFile" value="classpath:ehcache/ehcache.xml"/> 
    </bean>
    
    <bean id="authenticator" 
        class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
        </property>
    </bean>

    
    <!-- 
        3. 配置 Realm 
        3.1 直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
    -->     
    <bean id="jdbcRealm" class="com.shenqz.shiro.realms.ShiroRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5"></property>
                <property name="hashIterations" value="1024"></property>
            </bean>
        </property>
    </bean>
    
    <!-- <bean id="secondRealm" class="com.shenqz.shiro.realms.SecondRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="SHA1"></property>
                <property name="hashIterations" value="1024"></property>
            </bean>
        </property>
    </bean> -->

    
    <!--  
    4. 配置 LifecycleBeanPostProcessor. 可以自定的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法. 
    -->       
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
         the lifecycleBeanProcessor has run: -->
    <!--  
    5. 启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 LifecycleBeanPostProcessor 之后才可以使用. 
    -->     
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    
    <!--  
    6. 配置 ShiroFilter. 
    6.1 id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
                      若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean.
    -->     
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="successUrl" value="/WEB-INF/jsp/list.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
        
        <!--  
            配置哪些页面需要受保护. 
            以及访问这些页面需要的权限. 
            1). anon 可以被匿名访问
            2). authc 必须认证(即登录)后才可能访问的页面. 
            3). logout 登出.
            4). roles 角色过滤器
        -->
        <!--  
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = anon
                /shiro/login = anon
                /shiro/logout = logout
                
                /user.jsp = roles[user]
                /admin.jsp = roles[admin]
                
                # everything else requires authentication:
                /** = authc
            </value>
        </property>
        -->
    </bean>
    
    <!-- 配置一个 bean, 该 bean 实际上是一个 Map. 通过实例工厂方法的方式 -->
    <bean id="filterChainDefinitionMap" 
        factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean>
    
    <bean id="filterChainDefinitionMapBuilder" class="com.shenqz.shiro.factory.FilterChainDefinitionMapBuilder"></bean>
    
    <bean id="shiroService" class="com.shenqz.shiro.service.ShiroService"></bean>

</beans>



4.配置spring-ehcache.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" 
    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:ehcache/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>



5.在applicationContext.xml文件中引入spring-shiro.xml文件



<import resource="classpath:spring/spring-shiro.xml"/>



6.编写FilterChainDefinitionMapBuilder类



package com.shenqz.shiro.factory;

import java.util.LinkedHashMap;

public class FilterChainDefinitionMapBuilder {

    public LinkedHashMap<String, String> buildFilterChainDefinitionMap(){
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        //key表示访问的路径,页面,value代表对应的访问权限
        map.put("/js/**", "anon");
        map.put("/captcha-image.do", "anon");
        map.put("/checkCode.do", "anon");
        map.put("/login.do", "anon");
        map.put("/logout.do", "logout");
        
        map.put("/login.jsp", "anon");
        map.put("/list.jsp", "user");
        
        map.put("/**", "authc");
        
        return map;
    }
    
}



7.创建ShiroRealm.java类



package com.shenqz.shiro.realms;

import java.util.HashSet;
import java.util.List;
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.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import com.shenqz.entity.User;
import service.UserService;

public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        
        //1. 把 AuthenticationToken 转换为 UsernamePasswordToken 
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        
        //2. 从 UsernamePasswordToken 中来获取 username
        String login_name = upToken.getUsername();
        User user = userService.getUserByName(login_name);
        if(user == null){
            return null;
        }
        //3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
        System.out.println("从数据库中获取 username: " + login_name + " 所对应的用户信息.");
        
        //4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
        if("unknown".equals(login_name)){
            throw new UnknownAccountException("用户不存在!");
        }
        
        //5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常. 
        if("monster".equals(login_name)){
            throw new LockedAccountException("用户被锁定");
        }
        
        //6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
        //以下信息是从数据库中获取的.
        //1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象. 
        /*Object principal = username;
        //2). credentials: 密码. 
        Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
        if("admin".equals(username)){
            credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
        }else if("test".equals(username)){
            credentials = "4292bb58be34c59d28a0dcbd11932d49";
        }*/
        
        //3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
        String realmName = getName();
        //4). 盐值. 
        ByteSource credentialsSalt = ByteSource.Util.bytes(login_name);
        
        SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
        info = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(login_name), realmName);
        return info;
    }

    public static void main(String[] args) {
        String hashAlgorithmName = "MD5";
        Object credentials = "123456";
        Object salt = ByteSource.Util.bytes("test");
        int hashIterations = 1024;
        
        Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        System.out.println(result);
    }

    //授权会被 shiro 回调的方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        //1. 从 PrincipalCollection 中来获取登录用户的信息
        Object principal = principals.getPrimaryPrincipal();
        
        //2. 利用登录的用户的信息来用户当前用户的角色或权限(可能需要查询数据库)
        Set<String> roles = new HashSet<>();
        roles.add("test");
        if("admin".equals(principal)){
            roles.add("admin");
        }
        
        //3. 创建 SimpleAuthorizationInfo, 并设置其 reles 属性.
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
        info.addStringPermission("/user/add");
        //4. 返回 SimpleAuthorizationInfo 对象. 
        return info;
    }
}



8.对应的jsp页面设置



<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>    
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
    
    <h4>List Page</h4>
    
    Welcome: <shiro:principal></shiro:principal>
    <br><br>
    
    <shiro:hasRole name="admin">
        <a href="admin.jsp">Admin Page</a>
    </shiro:hasRole>
    <!-- <a href="admin.jsp">Admin Page</a> -->
    <shiro:hasRole name="test">
    <br><br>
    <a href="user.jsp">User Page</a>
    </shiro:hasRole>
    <!-- <a href="user.jsp">User Page</a> -->
    
    <shiro:hasPermission name="/user/dataGrid">
        <br><br>
        <a href="javascript:void(0)">User dataGrid</a>
    </shiro:hasPermission>
    
    <shiro:hasPermission name="/user/add">
        <br><br>
        <a href="useradd.jsp">User add</a>
    </shiro:hasPermission>
    <br>
    <a href="javascript:void(0);" οnclick="getUsers();">查询用户</a>
    <br>
    <a href="getRoles.do">查询角色</a>
    <br>
    <a href="getResurces.do">查询功能</a>
    
    <br><br>
    <a href="testShiroAnnotation.do">Test ShiroAnnotation</a>
    
    <br><br>
    <a href="logout.do">Logout</a>
    <br>
    <div id="d2">
        <table id="t2">
        
        </table>
    </div>
</body>
</html>
<script type="text/javascript" src="js/jquery-1.8.3.js"></script>
<script type="text/javascript">
    function getUsers(){
        $("post","getUsers.do",function(data){
            var data=JSON.parse(data);
            var str;
            str+="<tr><td>人员编号</td><td>人员姓名</td>年龄<td></td><td>联系电话</td></tr>"
            $.each(data,function(k){
                str+="<tr><td>"+data[k].id+"</td><td>"+data[k].name+"</td><td>"+data[k].age+"</td><td>"+data[k].phone+"</td></tr>"
            });
            $("#t2").html(str);
            
        });
    }
</script>