社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
简单集成一个Spring5+Thymeleaf+Mybatis+shiro 的maven web项目
项目结构
Maven 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>com.zzp</groupId>
<artifactId>learnSpringMVC</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring.version>5.1.7.RELEASE</spring.version>
<shiro.version>1.3.2</shiro.version>
</properties>
<dependencies>
<!-- Spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.30</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</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.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.3</version>
</dependency>
<!--打印日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
spring-mvc.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"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
<!--这里使用阿里巴巴fastjson 不适用jackjson-->
<!--<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />-->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 使用annotation 自动注册bean,并保证@Required,@Autowired的属性被注入 -->
<context:component-scan base-package="com.zzp.*">
<!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />-->
</context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name="controllerAspect"/>
</aop:aspectj-autoproxy>
<!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
<!-- 支持JSON数据格式 -->
<bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
<property name="fastJsonConfig" ref="fastJsonConfig"/>
</bean>
<bean id="fastJsonConfig" class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="serializerFeatures">
<list>
<!-- 是否输出值为null的字段 -->
<value>WriteMapNullValue</value>
<!-- 输出key时是否使用双引号 -->
<value>QuoteFieldNames</value>
<!-- 数值字段如果为null,输出为0,而非null -->
<value>WriteNullNumberAsZero</value>
<!-- 字符类型字段如果为null,输出为"",而非null -->
<value>WriteNullStringAsEmpty</value>
<!-- Boolean字段如果为null,输出为false,而非null -->
<value>WriteNullBooleanAsFalse</value>
<!-- null String不输出 -->
<value>WriteNullStringAsEmpty</value>
<!-- Date的日期转换器 -->
<value>WriteDateUseDateFormat</value>
<!-- 禁用 FastJson循环引用 -->
<value>DisableCircularReferenceDetect</value>
</list>
</property>
</bean>
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
<!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<!--<property name="prefix" value="/WEB-INF/page/" />-->
<!--<property name="suffix" value=".html" /><!–可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑 –>-->
<!--</bean>-->
</beans>
spring-thymeleaf.xml 配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" 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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:resources location="/images/" mapping="/images/**" />
<mvc:resources location="/css/" mapping="/css/**" />
<!--官网是可以用这个类配置 但是我这报错找不到这个类-->
<!--<bean id="templateResolver"-->
<!--class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">-->
<!--<property name="prefix" value="/WEB-INF/templates/" />-->
<!--<property name="suffix" value=".html" />-->
<!--<property name="templateMode" value="HTML5" />-->
<!--</bean>-->
<!--<!– 模板解析器 Template Resolution –>-->
<bean id="templateResolver"
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" />
<property name="suffix" value=".html" />
<property name="characterEncoding" value="UTF-8" />
<property name="templateMode" value="HTML5" />
<property name="cacheable" value="false" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<!-- Note that we do not need prefix or suffix parameters, because these are already specified at the
Template Resolver (which in turn is passed to the Template Engine). -->
<bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="1" />
<!--<property name="viewNames" value="*.html,*.xhtml" />-->
<property name="characterEncoding" value="UTF-8" />
</bean>
<!-- 可以定义一些静态变量 -->
<bean name="main" class="org.thymeleaf.spring5.view.ThymeleafView">
<property name="staticVariables">
<map>
<entry key="footer" value="Some company: <b>ACME</b>" />
</map>
</property>
</bean>
<!-- JSP 典型配置 -->
<!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<!--<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />-->
<!--<property name="prefix" value="/WEB-INF/jsps/" />-->
<!--<property name="suffix" value=".jsp" />-->
<!--<property name="order" value="2" />-->
<!--<property name="viewNames" value="*jsp" />-->
<!--</bean>-->
</beans>
ehcache-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shiroCache">
<!--
缓存对象存放路径
java.io.tmpdir:默认的临时文件存放路径。
user.home:用户的主目录。
user.dir:用户的当前工作目录,即当前程序所对应的工作路径。
其它通过命令行指定的系统属性,如“java –DdiskStore.path=D:\abc ……”。
-->
<diskStore path="java.io.tmpdir"/>
<!--
name:缓存名称。
maxElementsOnDisk:硬盘最大缓存个数。0表示不限制
maxEntriesLocalHeap:指定允许在内存中存放元素的最大数量,0表示不限制。
maxBytesLocalDisk:指定当前缓存能够使用的硬盘的最大字节数,其值可以是数字加单位,单位可以是K、M或者G,不区分大小写,
如:30G。当在CacheManager级别指定了该属性后,Cache级别也可以用百分比来表示,
如:60%,表示最多使用CacheManager级别指定硬盘容量的60%。该属性也可以在运行期指定。当指定了该属性后会隐式的使当前Cache的overflowToDisk为true。
maxEntriesInCache:指定缓存中允许存放元素的最大数量。这个属性也可以在运行期动态修改。但是这个属性只对Terracotta分布式缓存有用。
maxBytesLocalHeap:指定当前缓存能够使用的堆内存的最大字节数,其值的设置规则跟maxBytesLocalDisk是一样的。
maxBytesLocalOffHeap:指定当前Cache允许使用的非堆内存的最大字节数。当指定了该属性后,会使当前Cache的overflowToOffHeap的值变为true,
如果我们需要关闭overflowToOffHeap,那么我们需要显示的指定overflowToOffHeap的值为false。
overflowToDisk:boolean类型,默认为false。当内存里面的缓存已经达到预设的上限时是否允许将按驱除策略驱除的元素保存在硬盘上,默认是LRU(最近最少使用)。
当指定为false的时候表示缓存信息不会保存到磁盘上,只会保存在内存中。
该属性现在已经废弃,推荐使用cache元素的子元素persistence来代替,如:<persistence strategy=”localTempSwap”/>。
diskSpoolBufferSizeMB:当往磁盘上写入缓存信息时缓冲区的大小,单位是MB,默认是30。
overflowToOffHeap:boolean类型,默认为false。表示是否允许Cache使用非堆内存进行存储,非堆内存是不受Java GC影响的。该属性只对企业版Ehcache有用。
copyOnRead:当指定该属性为true时,我们在从Cache中读数据时取到的是Cache中对应元素的一个copy副本,而不是对应的一个引用。默认为false。
copyOnWrite:当指定该属性为true时,我们在往Cache中写入数据时用的是原对象的一个copy副本,而不是对应的一个引用。默认为false。
timeToIdleSeconds:单位是秒,表示一个元素所允许闲置的最大时间,也就是说一个元素在不被请求的情况下允许在缓存中待的最大时间。默认是0,表示不限制。
timeToLiveSeconds:单位是秒,表示无论一个元素闲置与否,其允许在Cache中存在的最大时间。默认是0,表示不限制。
eternal:boolean类型,表示是否永恒,默认为false。如果设为true,将忽略timeToIdleSeconds和timeToLiveSeconds,Cache内的元素永远都不会过期,也就不会因为元素的过期而被清除了。
diskExpiryThreadIntervalSeconds :单位是秒,表示多久检查元素是否过期的线程多久运行一次,默认是120秒。
clearOnFlush:boolean类型。表示在调用Cache的flush方法时是否要清空MemoryStore。默认为true。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
maxElementsInMemory:缓存最大数目
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
memoryStoreEvictionPolicy:
Ehcache的三种清空策略;
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!-- 授权缓存 -->
<cache name="authorizationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<!-- 认证缓存 -->
<cache name="authenticationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
spring-mybatis.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 自动扫描(自动注入) -->
<!--<context:component-scan base-package="com.zzp.*.service"></context:component-scan>-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" />
<!-- 配置初始化大小、最小、最大 -->
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="minIdle" value="1" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
</bean>
<!-- myBatis文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="mapperLocations" value="classpath:mapper/mybatis-user.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zzp.*.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事物的注解方式注入 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
</beans>
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 启用shrio授权注解拦截方式 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 装配 securityManager -->
<property name="securityManager" ref="securityManager"/>
<!-- 配置登陆页面 -->
<property name="loginUrl" value="/index"/>
<!-- 登陆成功后的一面 -->
<property name="successUrl" value="/success"/>
<property name="unauthorizedUrl" value="/unauthorized"/>
<!-- 具体配置需要拦截哪些 URL, 以及访问对应的 URL 时使用 Shiro 的什么 Filter 进行拦截. -->
<property name="filterChainDefinitions">
<value>
/index=anon
/success.html=anon
/fail.html=anon
/user.html = roles[user]
/admin.html = roles[admin]
/logout = logout
</value>
</property>
</bean>
<!-- 配置缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- 指定 ehcache 的配置文件 -->
<property name="cacheManagerConfigFile" value="classpath:conf/ehcache-shiro.xml"/>
</bean>
<!-- 配置进行授权和认证的 Realm -->
<bean id="myRealm" class="com.zzp.base.shiro.ShiroDbRealm">
<!--<property name="userService" ref="userService" />-->
</bean>
<bean id="userService" class="com.zzp.user.service.UserServiceImpl" />
<!-- 配置 Shiro 的 SecurityManager Bean. -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<property name="realm" ref="myRealm"/>
</bean>
<!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
mybatis-config.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="user" type="com.zzp.user.entity.User"/>
</typeAliases>
</configuration>
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>Archetype Created Web Application</display-name>
<!-- Spring-mybatis的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/spring*.xml</param-value>
</context-param>
<listener>
<description>spring监听器</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 防止spring内存溢出监听器 -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<!-- spring mvc servlet -->
<servlet>
<description>spring mvc servlet</description>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<description>spring mvc 配置文件</description>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<description>字符集过滤器</description>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<description>字符集编码</description>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置 Shiro 的 Filter -->
<filter>
<description>shiro 权限拦截</description>
<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>
<!-- 配置session超时时间,单位分钟 -->
<session-config>
<session-timeout>60</session-timeout>
</session-config>
<!-- DEFAULT PAGE -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
jdbc.properties
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
db.username=root
db.password=123456
loginController.java
package com.zzp.user.controller;
import com.zzp.user.dao.UserDao;
import com.zzp.user.entity.User;
import com.zzp.user.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class LoginController {
@Autowired
UserService userService;
@Autowired
UserDao userDao;
@RequestMapping("/doLogin.do")
public String doLogin(User user,Model model){
String info = loginUser(user);
if(info.equals("SUCC")){
User userInfo = userDao.selectByAccount(user.getAccount());
//model.addAttribute("account",user.getAccount());
model.addAttribute("nickName",userInfo.getNickName());
model.addAttribute("role",userInfo.getRole());
model.addAttribute("successMsg","登录成功!");
return "success";
}else{
model.addAttribute("failMsg","用户不存在或者密码错误!");
return "fail";
}
}
@RequestMapping("/index")
public String showPage(){
return "login";
}
@GetMapping("/{path}")
public String hrefPage(@PathVariable("path") String path){
return path;
}
@GetMapping("/logout.do")
public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
try{
subject.logout();
}catch(Exception ex){
}
}
response.sendRedirect("login");
}
public String loginUser(User user){
if(isRelogin()){
return "SUCC";
}
return shiroLogin(user);
}
private String shiroLogin(User user){
// 组装token,包括客户公司名称、简称、客户编号、用户名称;密码
UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(), user.getPassword().toCharArray(), null);
token.setRememberMe(true);
// shiro登陆验证
try {
SecurityUtils.getSubject().login(token);
} catch (UnknownAccountException ex) {
return "用户不存在或者密码错误!";
} catch (IncorrectCredentialsException ex) {
return "用户不存在或者密码错误!";
} catch (AuthenticationException ex) {
return ex.getMessage(); // 自定义报错信息
} catch (Exception ex) {
ex.printStackTrace();
return "内部错误,请重试!";
}
return "SUCC";
}
private boolean isRelogin(){
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated()){
return true;
}else{
return false;
}
}
}
ShiroDbRealm.java
package com.zzp.base.shiro;
import com.zzp.user.dao.UserDao;
import com.zzp.user.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
public class ShiroDbRealm extends AuthorizingRealm{
@Autowired
UserDao userDao;
public static final String SESSION_USER_KEY = "zzp";
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) SecurityUtils.getSubject().getSession().getAttribute(ShiroDbRealm.SESSION_USER_KEY);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole(user.getRole().trim());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
User user = tokenToUser((UsernamePasswordToken) token);
User dbUser = userDao.selectByAccount(user.getAccount());
if(dbUser==null)return null;
// 设置session
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute(ShiroDbRealm.SESSION_USER_KEY, dbUser);
//当前 Realm 的 name
String realmName = this.getName();
//登陆的主要信息: 可以是一个实体类的对象, 但该实体类的对象一定是根据 token 的 username 查询得到的.
// Object principal = ui.getUsername();
Object principal = token.getPrincipal();
return new SimpleAuthenticationInfo(principal, dbUser.getPassword(), realmName);
}
private User tokenToUser(UsernamePasswordToken authcToken) {
User user = new User();
user.setAccount(authcToken.getUsername());
user.setPassword(String.valueOf(authcToken.getPassword()));
return user;
}
}
User.java
package com.zzp.user.entity;
public class User {
private String account;
private String nickName;
private String role;
private String password;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"account='" + account + ''' +
", nickName='" + nickName + ''' +
", role='" + role + ''' +
", password='" + password + ''' +
'}';
}
}
UserServiceImpl.java
package com.zzp.user.service;
import com.zzp.user.dao.UserDao;
import com.zzp.user.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
public boolean doUserLogin(User user) {
User dbUser = userDao.selectByAccount(user.getAccount());
if(dbUser==null){
return false;
}else{
if(dbUser.getPassword().equals(user.getPassword())){
return true;
}
}
return false;
}
}
dao
package com.zzp.user.dao;
import com.zzp.user.entity.User;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
User selectByAccount(String account);
}
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="#" th:action="@{/doLogin.do}" method="post">
登录账号:<input name="account" type="text">
登录密码:<input name="password" type="password">
<button type="submit">提交</button>
</form>
</body>
</html>
success.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3><span th:text="${successMsg}"></span> Welcome,<span th:text="${nickName}"></span></h3>
<div th:if="${role}">
<div th:if="${role}=='admin'">
<a th:href="@{/admin.html}">Admin Page</a>
</div>
<div th:if="${role}=='user'">
<a th:href="@{/user.html}">User Page</a>
</div>
</div>
<br><br>
<a th:href="@{/logout.do}">Logout</a>
</body>
</html>
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!