简单的spring+shiro权限控制
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);" onclick="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>