Shiro 整合 Spring/SpringMVC — Shiro(二)
1. 搭建 SpringMVC 的环境
要整合 SpringMVC 就必须先搭建 SpringMVC 的环境, 这不是本文的重点, 帅帅只把配置给大家, 大家自行复制黏贴或者看看就好.
1.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>club.javafamily</groupId>
<artifactId>shiro02</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>shiro02 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://localhost/shiro/</url>
<properties>
<springframework.version>5.2.3.RELEASE</springframework.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<tomcat7.port>80</tomcat7.port>
<tomcat7.path>/shiro</tomcat7.path>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<!--Spring MVC + Spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- java EE -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>6.0.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- tools -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
<build>
<finalName>shiro02</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>${tomcat7.port}</port>
<path>${tomcat7.path}</path>
<uriEncoding>${project.build.sourceEncoding}</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
</build>
</project>
1.2 web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Shiro-Spring Demo</display-name>
<!-- 定义 Spring 配置文件, 默认为 /webapp/WEB-INF/applicationContext.xml -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--
配置spring核心监听器,默认会以 /WEB-INF/applicationContext.xml作为配置文件
配合 context-param(contextConfigLocation) 加载自定义的配置文件.
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- SpringMVC 的 DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 自定义 springmvc 的配置文件, 默认为 /WEB-INF/dispatcher-servlet.xml -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
1.3 dispatcher-servlet.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 包扫描 -->
<context:component-scan base-package="club.javafamily.shiro"></context:component-scan>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 注解驱动 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- Servlet Handler 配置 DefaultServletHttpRequestHandler 将静态资源请求
交还给 Tomcat 处理(因为 DispatchServlet 的 servlet-mapping 指定的是 / 将拦截
所有请求, 因此 springmvc 会把静态资源请求也当成一个普通的 request 请求).
-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
1.4 applicationContext.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">
</beans>
1.5 SpringMVC 环境测试
- 添加一个 controller 用于测试 mvc 环境
package club.javafamily.shiro.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PingController {
@GetMapping("/ping")
public String ping() {
return "pong";
}
}
- 然后用浏览器访问 http://localhost/shiro/ping 查看结果
1.6 目录结构
2. 引入 Shiro
2.1 引入 pom 依赖
<!--Shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>runtime</scope>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>runtime</scope>
<version>${log4j.version}</version>
</dependency>
2.2 配置 web.xml
在 Springmvc 环境中使用 Shiro 需要配置一个 Shiro Filter 来拦截请求.
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!--
targetFilterLifecycle 指定为 true 去
强制调用 Filter 的 init 和 destroy 方法
-->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter2</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意 DelegatingFilterProxy 这是一个代理类, 默认情况下, 这将从 Spring IOC 容器中查找 <filter-name> 指定的 Filter Name 的 Bean 作为具体的 Filter, 也可以通过 targetBeanName 初始化参数去指定 IOC 中具体 Filter Bean 的名称(这样的设计思想是来源于 Spring Security.)
2.3 配置 Shiro 组件
在
applicationContext.xml
中配置 Shiro 的核心组件
<!-- 配置 SecurityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"></property>
<property name="realm" ref="shiroRealm"></property>
</bean>
<!-- 使用 EhCache 作为 Shiro 缓存 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
</bean>
<!-- 配置 Realm, 现在只是一个实现了 Realm 接口的空实现 -->
<bean id="shiroRealm" class="club.javafamily.shiro.realm.ShiroRealm"></bean>
<!--
定义 Shiro BeanPostProcess 来自动调用实现了 {@link org.apache.shiro.util.Initializable}
或者 {@link org.apache.shiro.util.Destroyable} 接口的 init 和 destroy 方法
需要注意的是: LifecycleBeanPostProcessor 并不能确定 init 和 destroy 方法是否已经
被调用, 因此当配置了这个 BeanPostProcess 之后不要手动调用 init 和 destroy 方法,
也不要再配置 spring 的 init-method 和 destroy-method 属性
-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!-- 启用 IOC 容器中使用 Shiro 的注解, 但是必须在配置了 lifecycleBeanPostProcessor 之后,
因为 DefaultAdvisorAutoProxyCreator 会扫描上下文(因为 DefaultAdvisorAutoProxyCreator
也是一个 BeanPostProcess ), 寻找所有的 Advisor 并应用到所有符合切点的 Bean.
-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"></bean>
<!--
配置 Shiro Filter
-->
<bean id="shiroFilter2" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<property name="loginUrl" value="/login.jsp"></property>
<property name="successUrl" value="/index.jsp"></property>
<property name="unauthorizedUrl" value="/unauthorizedUrl.jsp"></property>
<!--
指定拦截规则, 除了 login.jsp 和 /ping 请求外都需要认证后才可以访问.
-->
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/ping = anon
/** = authc
</value>
</property>
</bean>
2.3.1 ShiroFilter
上面配置的 Shiro Filter 的 id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 targetBeanName 一致, 如果没有指定 targetBeanName 则默认为 <filter-name> 指定的 filter 名称. 如果不一致则会抛出以下异常, 因为 web.xml 配置的是 Filter 代理
严重: Exception starting filter shiroFilter
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'shiroFilter' available
2.3.2 filterChainDefinitions
ShiroFilter
的filterChainDefinitions
指定那些页面需要受保护, 以及访问这些页面那些权限
- anon: 代表可以被匿名访问(不配置也代表可以匿名访问, 但是需要注意 /** 是配置所有的请求)
- authc: 代表必须认证登录后才能访问
- /** 代表所有请求
filterChainDefinitions 采用第一次匹配优先的方式. 并支持通配符匹配
- ?: 匹配一个任意字符
- *: 匹配 0 个或者多个任意字符
- **: 匹配多层路径
2.3.3 jsp 页面
index.jsp
默认的项目首页, 也是我们通过Shiro
的successUrl
配置的登录成功跳转的页面.list.jsp
需要授权的页面login.jsp
登录页面unauthorizedUrl.jsp
没有权限跳转的提示页面
2.3.4 ShiroRealm
上面的
ShiroRealm
目前我们只是一个实现了Reaml
接口的空实现, 关于 Reaml 的作用可以参考我们上一篇 Shiro 文章 认证与授权 — Shiro (一)
package club.javafamily.shiro.realm;
import org.apache.shiro.authc.*;
import org.apache.shiro.realm.Realm;
public class ShiroRealm implements Realm {
@Override
public String getName() {
return null;
}
@Override
public boolean supports(AuthenticationToken token) {
return false;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
2.4 ehcache.xml
因为我们 shiro-cache 使用了 ehcache, 所以需要添加 ehcache 的配置, 不多说, 不懂得.
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
updateCheck="true" monitoring="autodetect" dynamicConfig="true">
<defaultCache
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
2.5 log4j.properties
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
# General Apache libraries
log4j.logger.org.apache=WARN
# Spring
log4j.logger.org.springframework=WARN
# Default Shiro logging
log4j.logger.org.apache.shiro=INFO
# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
2.6 运行测试
因为我们引入了
tomcat7-maven-plugin
所以只需要执行mvn tomcat7:run
来发布运行项目就可以进行测试.
- 访问
http://localhost/shiro/ping
或者http://localhost/shiro/login.jsp
都可以直接访问
- 访问
http://localhost/shiro/list.jsp
或者任何不存在的路径将都会自动跳转到登录界面.
诺, 源码传送门:
具体的登录认证我们下一篇再聊哦…