SSM整合之企业级后台管理系统(8) - Maven项目整合框架Spring+SpringMVC+MyBatis+Shiro+Log4j

“纸上得来终觉浅,绝知此事要躬行”

                                         ------陆游

经过前面教程的层层铺垫,我们来到了整合SSM框架(SSM: Spring SpringMVC MyBatis)的关键一步。本篇教程将给大家介绍如何在上一篇教程创建的Maven项目中,一步步整合Spring+SpringMVC+MyBatis+Shiro+Log4j框架。整合完成以后,能查询到数据库中用户表的详细字段信息并返回到前端页面上即为成功。

项目目录和框架整合完成后的演示:

一、写在前面

本篇教程是教程系列《SSM整合之企业级后台管理系统》中的第8篇,前面教程介绍了搭建集成开发环境、项目使用的技术和使用GitHub托管项目代码相关的内容,后续将继续介绍如何搭建一个企业级的后台管理系统,包括前端和后端实现。

温馨提示:本篇教程涉及的框架和配置文件都比较多,整合起来比较复杂,在学习过程中遇到项目启动报错或其它问题都属于比较正常的现象,希望同学们遇到问题时不要气馁和放弃,要根据报错信息查找问题所在。

在学习本系列过程中,有任何疑问或不明白的地方欢迎加qq群和我交流:584017112

二、整合顺序

我们按照以下顺序逐个添加或完善配置文件内容:

  1. pom.xml(配置项目依赖库)
  2. web.xml(配置项目启动加载文件)
  3. spring-mvc.xml(配置SpringMVC)
  4. jdbc.properties(数据库连接信息)
  5. spring-mybatis.xml(配置MyBatis连接数据库)
  6. spring-shiro.xml(配置Shiro)
  7. XBasicDataSource.java(关闭数据库资源)
  8. MyShiroDbRealm.java(Shiro验证登录方法)
  9. log4j.properties(配置系统日志)

还有用于测试的用户类相关实现:

  1. User.java
  2. UserDao.java
  3. UserMapper.xml
  4. IUserService.java
  5. UserServiceImpl.java

以及,数据库中新建User表并插入一条数据:

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
  `status` enum('IN_USE','LOCKED') CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'IN_USE' COMMENT '用户状态 IN_USE=使用中 LOCKED=锁定',
  `email` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  `cname` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '中文名',
  `mobile` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号',
  `profile` mediumblob NULL COMMENT '用户头像',
  `create_time` timestamp(0) NOT NULL COMMENT '创建时间',
  `last_login_time` timestamp(0) NULL DEFAULT NULL COMMENT '最后一次登录时间',
  `valid_until_time` date NULL DEFAULT NULL COMMENT '有效期止',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `user` VALUES (1, 'admin', '202cb962ac59075b964b07152d234b70', 'IN_USE', '[email protected]', '超级管理员', '18712345678', null, '2019-07-09 16:11:44', '2019-09-26 09:50:03', '2099-12-31');

三、配置文件详情

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>uchan-group</groupId>
  <artifactId>OMS</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>OMS Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <!-- spring版本号 -->
    <spring.version>4.3.17.RELEASE</spring.version>
    <!-- mybatis版本号 -->
    <mybatis.version>3.2.6</mybatis.version>
    <!-- log4j日志文件管理包版本 -->
    <slf4j.version>1.7.7</slf4j.version>
    <log4j.version>1.2.17</log4j.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <!-- 表示开发的时候引入,发布的时候不会加载此包 -->
      <scope>test</scope>
    </dependency>

    <!-- spring核心包 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</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-oxm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</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-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</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-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!-- mybatis核心包 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>
    <!-- mybatis/spring包 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.2.2</version>
    </dependency>

    <!-- 导入java ee jar 包 -->
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>7.0</version>
    </dependency>

    <!-- 导入Mysql数据库链接jar包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>
    <!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 -->
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.2.2</version>
    </dependency>

    <!-- JSTL标签类 -->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- 日志文件管理包 -->
    <!-- log4j start -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>${log4j.version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.bval</groupId>
      <artifactId>bval-jsr303</artifactId>
      <version>0.5</version>
    </dependency>

    <!-- 格式化对象,方便输出日志 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.31</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.9</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.9.2</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <!-- log end -->
    <!-- 映入JSON -->
    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-mapper-asl</artifactId>
      <version>1.9.13</version>
    </dependency>
    <!-- 上传组件包 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.9</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-core</artifactId>
      <version>1.3.5</version>
    </dependency>

    <!-- Spring 整合Shiro需要的依赖 -->
    <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>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.2.2</version>
    </dependency>
    <!-- Spring 整合Shiro需要的依赖 -->

    <!-- 验证码工具 -->
    <dependency>
      <groupId>com.github.penggle</groupId>
      <artifactId>kaptcha</artifactId>
      <version>2.3.2</version>
    </dependency>

    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.2</version>
        <configuration>
          <verbose>true</verbose>
          <overwrite>true</overwrite>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

2. 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"
         version="3.0">
  <display-name>Spring+SpringMVC+MyBatis</display-name>
  <!-- Spring和mybatis的配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-shiro.xml,classpath:spring-mybatis.xml</param-value>
  </context-param>
  <!-- 编码过滤器 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>
      <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>
    <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>

  <!-- Spring监听器 -->
  <listener>
    <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>
    <servlet-name>SpringMVC</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>SpringMVC</servlet-name>
    <!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!-- Log4j -->
  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
  </context-param>
  <!-- 3000表示 开一条watchdog线程每60秒扫描一下配置文件的变化;这样便于日志存放位置的改变 -->
  <context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>3000</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>

  <!-- 配置session超时时间,单位分钟 -->
  <session-config>
    <session-timeout>30</session-timeout>
  </session-config>

  <welcome-file-list>
    <welcome-file>/WEB-INF/jsp/login.jsp</welcome-file>
  </welcome-file-list>

  <error-page>
    <error-code>404</error-code>
    <location>/WEB-INF/jsp/error/404.jsp</location>
  </error-page>
  <error-page>
    <error-code>401</error-code>
    <location>/WEB-INF/jsp/error/401.jsp</location>
  </error-page>

  <error-page>
    <error-code>500</error-code>
    <location>/WEB-INF/jsp/error/500.jsp</location>
  </error-page>


</web-app>

3. spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
    <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 -->
    <context:component-scan base-package="com.ssm.controller"/>
    <!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
    <mvc:annotation-driven/>
    <!-- 静态资源处理  css js image fonts -->
    <mvc:resources location="/resources/" mapping="/resources/**"/>
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:resources location="/font/" mapping="/font/**"/>
    <mvc:resources location="/image/" mapping="/image/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/>

    <!-- 使用fastJson来支持JSON数据格式 -->
    <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
                <value>application/json</value>
            </list>
        </property>
        <property name="features">
            <list>
                <value>WriteMapNullValue</value>
                <value>QuoteFieldNames</value>
            </list>
        </property>
    </bean>

    <!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->
    <bean
           class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <!--                <ref bean="mappingJacksonHttpMessageConverter" /> &lt;!&ndash; JSON转换器 &ndash;&gt;-->
                <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/><!-- 解析导出文件byte流 -->
                <ref bean="fastJsonHttpMessageConverter"/>
            </list>
        </property>
    </bean>
    <!-- 配置文件上传,如果没有使用文件上传可以不用配置,当然如果不配,那么配置文件中也不必引入上传组件包 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 默认编码 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 文件大小最大值 -->
        <property name="maxUploadSize" value="10485760000"/>
        <!-- 内存中的最大值 -->
        <property name="maxInMemorySize" value="40960"/>
        <!-- 启用是为了推迟文件解析,以便捕获文件大小异常 -->
        <property name="resolveLazily" value="true"/>
    </bean>
    <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="favorParameter" value="true"/>
        <property name="parameterName" value="format"/>
        <property name="ignoreAcceptHeader" value="false"/>
        <property name="mediaTypes">
            <value>
                json=application/json
                xml=application/xml
                html=text/html
            </value>
        </property>
        <property name="defaultContentType" value="text/html"/>
    </bean>
    <!-- 定义跳转的文件的前后缀 ,视图模式配置-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

4. jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://127.0.0.1\:3306/oms?characterEncoding\=UTF-8&allowMultiQueries\=true
jdbc.username=root
jdbc.password=root
jdbc.initialSize=5
jdbc.maxActive=40
jdbc.maxIdle=40
jdbc.minIdle=5
jdbc.maxWait=60000
jdbc.defaultAutoCommit=false

5. 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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 自动扫描 -->
    <context:component-scan base-package="com.ssm" >
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>
    <!-- 引入配置文件 -->
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties" />
    </bean>
    <bean id="dataSource" class="com.ssm.core.XBasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="${jdbc.initialSize}"></property>
        <!-- 连接池最大数量 -->
        <property name="maxActive" value="${jdbc.maxActive}"></property>
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="${jdbc.maxIdle}"></property>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="${jdbc.minIdle}"></property>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="${jdbc.maxWait}"></property>
        <!-- 自动提交 -->
        <property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"></property>
    </bean>
    <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自动扫描mapping.xml文件 -->
<!--        <property name="mapperLocations" value="classpath:com/oms/mapper/*.xml"></property>-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    </bean>
    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ssm.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>
    <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

6. 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-3.0.xsd"
       default-lazy-init="true">
    <description>Shiro Configuration</description>
    <!-- Shiro's main business-tier object for web-enabled applications -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroDbRealm" />
        <property name="cacheManager" ref="cacheManager" />
    </bean>
    <!-- 項目自定义的Realm -->
    <bean id="shiroDbRealm" class="com.ssm.core.MyShiroDbRealm">
        <property name="cacheManager" ref="cacheManager" />
        <!-- 登录校验前,先对输入的密码进行加密 -->
        <property name="credentialsMatcher" >
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5"></property>
                <property name="hashIterations" value="1"></property>
            </bean>
        </property>
    </bean>
    <!-- Shiro Filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login" />
        <property name="successUrl" value="/index.jsp" />
        <property name="unauthorizedUrl" value="/error/noperms.jsp" />
        <property name="filterChainDefinitions">
            <value>
                /css/** = anon
                /image/** = anon
                /js/** = anon
                /font/** = anon
                /vcode/** = anon
<!--                /index.jsp = anon-->
<!--                /index = anon-->
<!--                /welcome = anon-->
                /user/checkLogin = anon
                /user/getUserByUsername/** = anon
                /login = anon
                /logout = logout
                /** = authc
            </value>
        </property>
    </bean>
    <!-- 用户授权信息Cache -->
    <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

<!--    &lt;!&ndash; AOP式方法级权限检查 &ndash;&gt;-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
</beans>

7. XBasicDataSource.java

package com.ssm.core;
import com.mysql.jdbc.AbandonedConnectionCleanupThread;
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.DriverManager;
import java.sql.SQLException;

public class XBasicDataSource extends BasicDataSource {
    @Override
    public synchronized void close() throws SQLException {
        //以下两句代码分别对应两个资源的关闭
        DriverManager.deregisterDriver(DriverManager.getDriver(getUrl()));
        AbandonedConnectionCleanupThread.checkedShutdown();
        super.close();
    }
}

8. MyShiroDbRealm.java

package com.ssm.core;
import com.ssm.model.User;
import com.ssm.service.IUserService;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.Set;

public class MyShiroDbRealm extends AuthorizingRealm {
    private static Logger logger = Logger.getLogger(MyShiroDbRealm.class);
    @Autowired
    private IUserService userService;

    public MyShiroDbRealm() {
        super();
    }

    /**
     * 验证登陆
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        logger.info(token.getUsername());
        User user = userService.getUserByUsername(token.getUsername());
        if (user != null) {
            //账号被锁定
            if ("LOCKED".equals(user.getStatus())) {
                throw new LockedAccountException();
            }
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), this.getName());
        } else {
            throw new UnknownAccountException();
        }
    }

    /**
     * 登陆成功之后,进行角色和权限验证
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        /*这里应该根据userName使用role和permission 的serive层来做判断,并将对应 的权限加进来,下面简化了这一步*/
        Set<String> roleNames = new HashSet<String>();
        Set<String> permissions = new HashSet<String>();
        roleNames.add("admin");//添加角色。对应到index.jsp
        roleNames.add("administrator");
        permissions.add("create");//添加权限,对应到index.jsp
        permissions.add("login.do?main");
        permissions.add("login.do?logout");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
        info.setStringPermissions(permissions);
        return info;
    }

    /**
     * 清除所有用户授权信息缓存.
     */
    public void clearCachedAuthorizationInfo(String principal) {
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
        clearCachedAuthorizationInfo(principals);
    }

    /**
     * 清除所有用户授权信息缓存.
     */
    public void clearAllCachedAuthorizationInfo() {
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        if (cache != null) {
            for (Object key : cache.keys()) {
                cache.remove(key);
            }
        }
    }
}

9. log4j.properties

log4j.rootLogger = debug,stdout,D,E  

log4j.appender.stdout = org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.Target = System.out  
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n  

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender  
log4j.appender.D.File = F://logs/log.log  
log4j.appender.D.Append = true  
log4j.appender.D.Threshold = DEBUG   
log4j.appender.D.layout = org.apache.log4j.PatternLayout  
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n  

log4j.appender.E = org.apache.log4j.DailyRollingFileAppender  
log4j.appender.E.File =F://logs/error.log   
log4j.appender.E.Append = true  
log4j.appender.E.Threshold = ERROR   
log4j.appender.E.layout = org.apache.log4j.PatternLayout  
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ][%C.%M(%L)]  %m%n

四、用户类相关实现

1. User.java

package com.ssm.model;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.util.Date;

public class User {
    private Integer id;
    private String username;
    private String password;
    private String status;
    private String email;
    private String cname;
    private String mobile;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date lastLoginTime;
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date validUntilTime;
    private byte[] profile;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username == null ? null : username.trim();
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status == null ? null : status.trim();
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email == null ? null : email.trim();
    }
    public String getCname() {
        return cname;
    }
    public void setCname(String cname) {
        this.cname = cname == null ? null : cname.trim();
    }
    public String getMobile() {
        return mobile;
    }
    public void setMobile(String mobile) {
        this.mobile = mobile == null ? null : mobile.trim();
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public Date getLastLoginTime() {
        return lastLoginTime;
    }
    public void setLastLoginTime(Date lastLoginTime) {
        this.lastLoginTime = lastLoginTime;
    }
    public Date getValidUntilTime() {
        return validUntilTime;
    }
    public void setValidUntilTime(Date validUntilTime) {
        this.validUntilTime = validUntilTime;
    }
    public byte[] getProfile() {
        return profile;
    }
    public void setProfile(byte[] profile) {
        this.profile = profile;
    }
}

2. UserDao.java

package com.ssm.dao;
import com.ssm.model.User;
import javax.annotation.Resource;

@Resource
public interface UserDao {
    User selectByUsername(String username);
}

3. UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.dao.UserDao">
    <resultMap id="BaseResultMap" type="com.ssm.model.User">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="username" jdbcType="VARCHAR" property="username"/>
        <result column="password" jdbcType="VARCHAR" property="password"/>
        <result column="status" jdbcType="VARCHAR" property="status"/>
        <result column="email" jdbcType="VARCHAR" property="email"/>
        <result column="cname" jdbcType="VARCHAR" property="cname"/>
        <result column="mobile" jdbcType="VARCHAR" property="mobile"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="last_login_time" jdbcType="TIMESTAMP" property="lastLoginTime"/>
        <result column="valid_until_time" property="validUntilTime" jdbcType="DATE"/>
    </resultMap>
    <resultMap id="ResultMapWithBLOBs" type="com.ssm.model.User" extends="BaseResultMap">
        <result column="profile" property="profile" jdbcType="LONGVARBINARY"
                typeHandler="org.apache.ibatis.type.BlobTypeHandler"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, username, password, status, email, cname, mobile, create_time, last_login_time,
        valid_until_time
    </sql>
    <sql id="Blob_Column_List">
        profile
    </sql>
    <!-- 根据username返回user对象 -->
    <select id="selectByUsername" parameterType="java.lang.String" resultMap="ResultMapWithBLOBs">
        select
        <include refid="Base_Column_List"/>
        ,
        <include refid="Blob_Column_List"/>
        from user
        where username = #{username,jdbcType=VARCHAR}
    </select>
</mapper>

4. IUserService.java

package com.ssm.service;
import com.ssm.model.User;

public interface IUserService {
    public User getUserByUsername(String username);
}

5. UserServiceImpl.java

package com.ssm.service.impl;
import com.ssm.dao.UserDao;
import com.ssm.model.User;
import com.ssm.service.IUserService;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service("userService")
public class UserServiceImpl implements IUserService {
    private static Logger logger = Logger.getLogger(UserServiceImpl.class);
    @Resource
    private UserDao userDao;

    public User getUserByUsername(String username) {
        return this.userDao.selectByUsername(username);
    }
}

五、本篇结束语

码字不易,点个赞再走呗~

发布了36 篇原创文章 · 获赞 75 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u012586848/article/details/102675964
今日推荐