spring shiro 集成
1、向spring项目中添加shiro相关的依赖
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.8</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.2</version> </dependency>
2、在web.xml配置ShiroFilter
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- shiro过虑器,DelegatingFilterProx会从spring容器中找shiroFilter --> <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> </filter-mapping>
3、建立一个 spring 的配置文件 spring-shiro.xml
a、使用 import 标签把此文件引入为spring的配置文件
<import resource="classpath:spring-shiro.xml"></import>
b、配置spring-shiro.xml文件
<!-- shiro 拦截器配置 --> <!-- ShiroFilter 名字必须要和web.xml中配置的名字一致 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="unauthorizedUrl" value="/nopermission.jsp"/> <!-- filterChainDefinitions 相当于.ini文件中的[urls] --> <property name="filterChainDefinitions"> <value> /logout=logout /**=authc </value> </property> </bean> <!--shiro权限异常处理 --> <!-- 上面对“/nopermission.jsp”的配置不会生效,因为 spring 默认会抛出异常到页面;需要添加下面这个配置才可以生效--> <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页名作为值 --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.apache.shiro.authz.UnauthorizedException">redirect:/nopermission.jsp</prop> </props> </property> </bean> <!-- 配置安全管理器SecurityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm"/> <!-- 给shiro添加缓存配置 --> <property name="cacheManager" ref="cacheManager"></property> </bean> <!-- 配置自定义的Realm --> <bean id="userRealm" class="cn.wolfcode.shiro.realm.UserRealm"> <!-- 加密器 --> <property name="credentialsMatcher" ref="credentialsMatcher" /> </bean> <!-- 在自定义的realm那个类里还要指定盐 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!-- 加密算法 --> <property name="hashAlgorithmName" value="md5" /> <!-- 散列次数 --> <property name="hashIterations" value="3" /> </bean> <!-- 开启aop,对类代理;并开启shiro注解支持 --> <aop:config proxy-target-class="true"></aop:config> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> <!-- 缓存管理器 --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManager" ref="ehCacheManager"/> </bean> <bean id="ehCacheManager" class ="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:shiro-ehcache.xml" /> <property name="shared" value="true"></property> </bean>
4、实现自己的realm
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class UserRealm extends AuthorizingRealm { @Override public String getName() { return "UserRealm"; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String principal = (String) token.getPrincipal(); if(!"admin".equals(principal)){ return null; } Employee currentUser = new Employee(); currentUser.setName(principal); currentUser.setPassword("1"); currentUser.setAdmin(true); SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(currentUser, currentUser.getPassword(),getName()); return authenticationInfo; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Employee currentUser = (Employee) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); List<String> roles = new ArrayList<String>(); roles.addAll(Arrays.asList("HR MGR","ORDER MGR")); authorizationInfo.addRoles(roles); List<String> perms = new ArrayList<String>(); perms.addAll(Arrays.asList("employee:view","employee:delete")); authorizationInfo.addStringPermissions(perms); return authorizationInfo; } }
5、 实现登陆方法
Suject 会自动创建,不需要我们配置;相应的登陆方法也会自动调用,不需要我们写。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径 @RequestMapping("/login") public String login(Model model, HttpServletRequest req) throws Exception{ //如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名 String exceptionClassName = (String) req.getAttribute("shiroLoginFailure"); //根据shiro返回的异常类路径判断,抛出指定异常信息 if(exceptionClassName!=null){ if (UnknownAccountException.class.getName().equals(exceptionClassName)) { //最终会抛给异常处理器 model.addAttribute("errorMsg", "账号不存在"); } else if (IncorrectCredentialsException.class.getName().equals( exceptionClassName)) { model.addAttribute("errorMsg", "用户名/密码错误"); } else { //最终在异常处理器生成未知错误. model.addAttribute("errorMsg", "其他异常信息"); } } //登陆失败还到login页面 return "forward:/login.jsp"; }
6、缓存管理(登陆时把当前用户的权限缓存起来,之后不需要再次查询)
a、添加依赖
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.8</version> </dependency>
b、securityManager引用缓存管理器(如上)
c、配置缓存管理器,并指定缓存框架配置文件路径(如上)
ehcache版本在2.5.0以下,需要配置如下:
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"></property>
</bean>
ehcache版本在2.5.0以上,需要配置如下:
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="ehCacheManager"/>
</bean>
<bean id="ehCacheManager" class ="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:shiro-ehcache.xml" />
<property name="shared" value="true"></property>
</bean>
d、配置 shiro-ehcache.xml 文件
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
7、清除缓存
如果用户正常退出,缓存自动清空;如果用户非正常退出,缓存自动清空。
如果修改了用户的权限,而用户不退出系统,修改的权限无法立即生效;用户退出并再次登陆系统时,shiro会自动调用realm从数据库重新获取权限数据,才会使修改的权限得以生效。
如果在修改权限后想要立即生效,需要清除缓存,可以调用realm的clearCache方法。
// 在realm中定义该方法,在角色或权限service中,delete或者update方法里来调用 // 清除缓存 public void clearCached() { PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals(); super.clearCache(principals); }