简介
- Shiro的组件都是Javabean/pojo式的组件,所以非常容易使用Spring进行组件管理,可以非常方便得从ini配置转为Spring配置(如xml配置文件)。
JavaSE
依赖
<!--单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <!--集成Spring做测试必备的依赖,比如要注入Spring容器中的bean--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.2.4.RELEASE</version> <scope>test</scope> </dependency> <dependency> <!--用于开启事务,否则测试将无法自动回滚--> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</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-ehcache</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.2</version> </dependency> <dependency> <!--使用quartz要用到的--> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <!--mysql数据库及druid连接池--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.25</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>0.2.23</version> </dependency> <dependency> <!--使用JdbcTemplate,方便数据库操作--> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.2.4.RELEASE</version> </dependency>
spring-shiro.xml:取代ini配置,提供了普通JavaSE应用的Spring配置。
<!-- 缓存管理器 使用Ehcache实现 --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> </bean> <!-- 凭证匹配器 --> <bean id="credentialsMatcher" class=" com.github.zhangkaitao.shiro.chapter12.credentials.RetryLimitHashedCredentialsMatcher"> <constructor-arg ref="cacheManager"/> <property name="hashAlgorithmName" value="md5"/> <property name="hashIterations" value="2"/> <property name="storedCredentialsHexEncoded" value="true"/> </bean> <!-- Realm实现 --> <bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter12.realm.UserRealm"> <property name="userService" ref="userService"/> <property name="credentialsMatcher" ref="credentialsMatcher"/> <property name="cachingEnabled" value="true"/> <property name="authenticationCachingEnabled" value="true"/> <property name="authenticationCacheName" value="authenticationCache"/> <property name="authorizationCachingEnabled" value="true"/> <property name="authorizationCacheName" value="authorizationCache"/> </bean> <!-- 会话ID生成器 --> <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> <!-- 会话DAO --> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/> <property name="sessionIdGenerator" ref="sessionIdGenerator"/> </bean> <!-- 会话验证调度器 --> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> <property name="sessionValidationInterval" value="1800000"/> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- 会话管理器 --> <bean id="sessionManager" class="org.apache.shiro.session.mgt.DefaultSessionManager"> <property name="globalSessionTimeout" value="1800000"/> <property name="deleteInvalidSessions" value="true"/> <property name="sessionValidationSchedulerEnabled" value="true"/> <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> <property name="sessionDAO" ref="sessionDAO"/> </bean> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager"> <property name="realms"> <list><ref bean="userRealm"/></list> </property> <property name="sessionManager" ref="sessionManager"/> <property name="cacheManager" ref="cacheManager"/> </bean> <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/> <property name="arguments" ref="securityManager"/> </bean> <!-- Shiro生命周期处理器--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
其中,Realm实现需要注入userService的bean,它不在本配置文件,而在以下spring-beans.xml中,一样可以找到的。
spring-beans.xml
<!-- 数据库连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/shiro"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!-- Base DAO: abstract="true"即该类不能被实例化,默认false;抽象bean可以不映射任何类 --> <bean id="baseDao" abstract="true"> <property name="dataSource" ref="dataSource"/> </bean> <!-- DAO --> <bean id="permissionDao" class="com.haien.shirochapter12.dao.impl.PermissionDaoImpl" parent="baseDao"/> <bean id="roleDao" class="com.haien.shirochapter12.dao.impl.RoleDaoImpl" parent="baseDao"/> <bean id="userDao" class="com.haien.shirochapter12.dao.impl.UserDaoImpl" parent="baseDao"/> <!-- Service --> <bean id="permissionService" class="com.haien.shirochapter12.service.impl.PermissionServiceImpl"> <property name="permissionDao" ref="permissionDao"/> </bean> <bean id="roleService" class="com.haien.shirochapter12.service.impl.RoleServiceImpl"> <property name="roleDao" ref="roleDao"/> </bean> <!--帮助加密密码--> <bean id="passwordHelper" class="com.haien.shirochapter12.service.impl.PasswordHelper"> <property name="algorithmName" value="md5"/> <property name="hashIterations" value="2"/> </bean> <bean id="userService" class="com.haien.shirochapter12.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"/> <property name="passwordHelper" ref="passwordHelper"/> </bean> <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!--开启事务--> <tx:annotation-driven transaction-manager="transactionManager"/>
- 分别写User,Role,Permission的entity,dao,service层、实现身份验证和授权的UserRealm。
- ShiroTest:测试类,测试前删除各表数据,重新插入,分配角色和用户给用户;主要测试方法则是以某个用户登录,判断是否拥有权限。
@Test
public void test() {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(u1.getUsername(), password);
subject.login(token);
Assert.assertTrue(subject.isAuthenticated());
subject.checkRole("admin");
subject.checkPermission("user:create");
userService.changePassword(u1.getId(), password + "1");
userRealm.clearCache(subject.getPrincipals());
token = new UsernamePasswordToken(u1.getUsername(), password + "1");
subject.login(token);
}
web应用
spring-shiro-web.xml:web应用和JavaSE应用的shiro配置是类似的,此处提供一些不一样的配置.
<!-- web:会话Cookie模板 --> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!--设置cookie名,默认为JSESSIONID--> <constructor-arg value="sid"/> <!--设置cookie过期时间,单位秒,默认-1,即关闭浏览器时过期--> <property name="httpOnly" value="true"/> <!--true表示客户端不会暴露脚本代码,有助于减少某些类型的跨站点脚本攻击, Servlet2.5及以上才支持--> <property name="maxAge" value="180000"/> </bean> <!-- web:会话管理器,使用用于web环境的DefaultWebSessionManager --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!--自己管理--> <property name="globalSessionTimeout" value="1800000"/> <property name="deleteInvalidSessions" value="true"/> <property name="sessionValidationSchedulerEnabled" value="true"/> <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> <property name="sessionDAO" ref="sessionDAO"/> <property name="sessionIdCookieEnabled" value="true"/> <property name="sessionIdCookie" ref="sessionIdCookie"/> </bean> <!-- web:安全管理器,使用用于web环境的DefaultWebSecurityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm"/> <property name="sessionManager" ref="sessionManager"/> <property name="cacheManager" ref="cacheManager"/> </bean> <!-- web:基于Form表单的身份验证过滤器 --> <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"> <property name="usernameParam" value="username"/> <property name="passwordParam" value="password"/> <property name="loginUrl" value="/login.jsp"/> </bean> <!-- web:Shiro的Web过滤器,使用ShiroFilterFactoryBean创建ShiroFilter过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login.jsp"/> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <!--定义自己的过滤器--> <property name="filters"> <util:map> <entry key="authc" value-ref="formAuthenticationFilter"/> </util:map> </property> <property name="filterChainDefinitions"> <value> /index.jsp = anon /unauthorized.jsp = anon /login.jsp = authc /logout = logout /** = user </value> </property> </bean>
- 会话管理器和安全管理器都要换成web环境下的,还要加个过滤器。
- 过滤器使用ShiroFilterFactoryBean来创建ShiroFilter过滤器,filters属性用于定义自己的过滤器,即ini配置中的[filters],filterChianDefinitions用于声明url和filter的关系,即ini配置中的[urls]。
web.xml:
<!-- Spring配置文件开始 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-beans.xml, classpath:spring-shiro-web.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring配置文件结束 --> <!-- shiro 安全过滤器 --> <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <async-supported>true</async-supported> <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> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
DelegatingFilterProxy:自动到Spring容器中查找名为shiroFilter的bean(它就定义在spring-shiro-web.xml中),并把filter请求交给它处理。
spring-mvc.xml:
<!--use-default-filters:使用默认过滤器,它会扫描包含@Service、@Component、 @Repository、@Controller注解的类;不使用则需用include-filter指定扫描哪些类--> <context:component-scan base-package="com.haien.shirochapter12.web" use-default-filters="false"> <!--指定只扫描base-package下有@Controller的类--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <!--指定只扫描base-package下有@ControllerAdvice的类--> <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan> <!--添加shiro spring aop权限注解的支持,即使用注解式授权--> <aop:config proxy-target-class="true"></aop:config> <!--表示使用代理类--> <bean class="org.apache.shiro.spring.security.interceptor .AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <!--启动SpringMvc注解功能,完成请求和注解controller类的映射--> <mvc:annotation-driven/> <mvc:view-controller path="/" view-name="index"/> <!--url映射视图,无需通过控制器--> <!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="contentType" value="text/html"/> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 控制器异常处理(好像不写也可以) --> <bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet .mvc.method.annotation.ExceptionHandlerExceptionResolver"> </bean> <bean class="com.haien.shirochapter12.web.exception.DefaultExceptionHandler"/>
AnnotationController:上面开启了Shiro注解式访问控制,那么接下来就可以在Controller中使用注解
@Controller
public class AnnotationController {
@RequestMapping("/hello1")
public String hello1() {
SecurityUtils.getSubject().checkRole("admin");
return "success";
}
@RequiresRoles("admin")
@RequestMapping("/hello2")
public String hello2() {
return "success";
}
}
- DefaultExceptionHandler:全局异常处理,无权限则返回unauthorized.jsp页面,提示用户无权限。目前数据库只有zhang/123拥有权限,其他如li/123都没有权限。
@ControllerAdvice
public class DefaultExceptionHandler {
@ExceptionHandler({UnauthorizedException.class}) //处理controller抛出的该类异常及其子类
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ModelAndView processUnauthenticatedException(NativeWebRequest request
,UnauthorizedException e){
ModelAndView mv=new ModelAndView();
mv.addObject("exception",e);
mv.setViewName("unauthorized");
return mv;
}
}
- 代码示例:ideaProjects/shirochapter12
- 《跟我学shiro第十二章》