商品管理系统SSM练习开发详细手册

SSM综合练习

1.功能介绍

1.1 环境搭建

主要讲解maven工程搭建,以及基于oracle数据库的商品表信息,并完成SSM整合。

1.2 商品查询

基于SSM整合基础上完成商品查询,要掌握主面页面main.jsp及商品显示页面product-list.jsp页面的创建。

1.3 商品添加

进一步巩固SSM整合,并完成商品添加功能,要注意事务操作以及product-add.jsp页面生成。

1.4 订单查询

订单的查询操作,它主要完成简单的多表查询操作,查询订单时,需要查询出与订单关联的其它表中信息,所以大 家一定要了解订单及其它表关联关系

1.5 订单分页查询

订单分页查询,我们使用的是mybatis分页插件PageHelper,要掌握PageHelper的基本使用。

1.6 订单详情查询

订单详情是用于查询某一个订单的信息,这个知识点主要考核学生对复杂的多表查询操作的掌握。

1.7 Spring Security 概述

Spring Security是 Spring 项目组中用来提供安全认证服务的框架,它的使用很复杂,我们在课程中只介绍了 spring Security的基本操作,大家要掌握spring Security框架的配置及基本的认证与授权操作。

1.8 用户管理

用户管理中我们会介绍基于spring Security的用户登录、退出操作。以及用户查询、添加、详情有等操作,这些功 能的练习是对前期SSM知识点的进一步巩固。

1.9 角色管理

角色管理主要完成角色查询、角色添加

1.10 资源权限管理

资源权限管理主要完成查询、添加操作,它的操作与角色管理类似,角色管理以及资源权限管理都是对权限管理的补充。

1.11 权限关联与控制

主要会讲解用户角色关联、角色权限关联,这两个操作是为了后续我们完成授权操作的基础,关于授权操作我们会 在服务器端及页面端分别讲解

1.12 AOP日志处理

AOP日志处理,我们使用spring AOP切面来完成系统级别的日志收集。

2.数据库介绍

Oracle10g 账号:ssm 密码:ssm

2.1 产品表

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2productNumvarchar2(50)产品编号,唯一,不为空
3productNamevarchar2(50)产品名称(路线名称)
4cityNamevarchar2(50)出发城市
5departureTimetimestamp出发时间
6productPricenumber产品价格
7productDescvarchar(500)产品描述
8productStatusint状态(0关闭1开启)

2.2 订单表

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2ordertNumvarchar2(50)订单编号 不为空 唯一
3orderTimetimestamp下单时间
4peopleCountint出行人数
5orderDescvarchar2(500)订单描述(其他信息)
6payTypeint支付方式(0支付宝1微信2其他)
7orderStatusint订单状态(0未支付1已支付)
8productIdint产品id外键
9memberidint会员(联系人)id外键

2.3 会员表

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2namevarchar2(50)名称
3nickNamevarchar2(50)呢称
4phoneNumvarchar2(50)电话号码
5emailvarchar2(50)邮箱

2.4 旅客表

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2namevarchar2(20)姓名
3sexvarchar2(20)性别
4phoneNumvarchar2(20)电话号码
5credentialsNumint证件类型0身份证 1护照 2军官证
6credentialsNumvarchar(50)证件号码
7travellerTypeint旅客类型(人群)0成人 1儿童

2.5 用户表

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2emailvarchar2(20)非空,唯一
3usernamevarchar2(50)用户名
4passwordvarchar2(20)密码(加密)
5phoneNumvarchar2(20)电话
6statusint状态(0 未开启 1开启)

2.6 角色表

序号字段名字段类型字段描述
1idvarchar2(50)无意义,主键uuid
2roleNamevarchar2(50)角色名
3roleDescvarchar2(500)角色描述

2.7 资源权限表

序号字段名字段类型字段描述
1idvarchar2(50)无意义,主键uuid
2permissionNamevarchar2(50)权限名
3urlvarchar2(500)资源路径

2.8 日志表

序号字段名字段类型字段描述
1idvarchar2(50)无意义,主键uuid
2visitTimetimestamp访问时间
3usernamevarchar2(500)操作者用户名
4ipvarchar(50)访问ip
5urlvarchar2(20)访问资源url
6executionTimeint执行时长
7methodvarchar(20)访问方法

3.SSM 环境搭建与产品操作

1.环境准备

1.1 数据库与表结构
1.1.1 创建用户与授权

数据库我们使用Oracle Oracle 为每个项目创建单独user,oracle数据表存放在表空间下,每个用户有独立表空间
创建用户及密码:

语法[创建用户]create user 用户名 identified by 口令[即密码]; 
例子:
create user test identified by test;   

授权

语法: 
grant connect, resource to 用户名; 
例子:
grant connect, resource to test;

PL/SQL Developer是一个集成开发环境,专门面向Oracle数据库存储程序单元的开发PL/SQL Developer侧重于易 用性、代码品质和生产力,充分发挥Oracle应用程序开发过程中的主要优势。

连接oracle数据库
在这里插入图片描述
创建用户及授权

a) 创建用户

在这里插入图片描述
在这里插入图片描述

b) 授权
在这里插入图片描述
对象权限是指针对于某一张表的操作权限,系统权限是指对表的CRUD操作权限, 角色权限是系统权限的集合,我 们设置 时,一般是设置角色权限,设置resource与connect

1.1.2 创建表

产品表信息描述

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2productNumvarchar2(50)产品编号,唯一,不为空
3productNamevarchar2(50)产品名称(路线名称)
4cityNamevarchar2(50)出发城市
5departureTimetimestamp出发时间
6productPricenumber产品价格
7productDescvarchar(500)产品描述
8productStatusint状态(0关闭1开启)

创建表sql

CREATE TABLE product(
    id varchar2(32) default SYS_GUID() PRIMARY KEY,
    productNum VARCHAR2(50) NOT NULL,
    productName VARCHAR2(50),
    cityName VARCHAR2(50),
    DepartureTime timestamp,
    productPrice Number,
    productDesc VARCHAR2(500),
    productStatus INT,
    CONSTRAINT product UNIQUE (id, productNum)
)
 
insert into PRODUCT (id, productnum, productname, cityname, departuretime, productprice, productdesc,productstatus) 
values ('676C5BD1D35E429A8C2E114939C5685A', 'itcast-002', '北京三日游', '北京', to_timestamp('1010-2018 10:10:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 1200, '不错的旅行', 1); 
insert into PRODUCT (id, productnum, productname, cityname, departuretime, productprice, productdesc, productstatus) 
values ('12B7ABF2A4C544568B0A7C69F36BF8B7', 'itcast-003', '上海五日游', '上海', to_timestamp('2504-2018 14:30:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 1800, '魔都我来了', 0);
insert into PRODUCT (id, productnum, productname, cityname, departuretime, productprice, productdesc, productstatus)
values ('9F71F01CB448476DAFB309AA6DF9497F', 'itcast-001', '北京三日游', '北京', to_timestamp('1010-2018 10:10:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 1200, '不错的旅行', 1);
 

1.2 maven工程搭建

1.2.1 创建maven工程

新建一个maven工程
在这里插入图片描述
创建子模块
pjy_ssm_web

pjy_ssm_domain

pjy_ssm_service

pjy_ssm_dao

pjy_ssm_utils

其中创建itcast-ssm-web 时注意我们选择一个web工程

在这里插入图片描述
在父工程下创建子模块:
在这里插入图片描述

在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.pjy.ssm</groupId>
    <artifactId>pjy_ssm</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>5.0.2.RELEASE</spring.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <oracle.version>10.2.0.1.0</oracle.version>
        <mybatis.version>3.4.5</mybatis.version>
        <spring.security.version>5.0.1.RELEASE</spring.security.version>
    </properties>

    <dependencies>        <!-- spring -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</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-web</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-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</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-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>        <!-- log start -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</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 -->

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring.security.version}</version>
        </dependency>

        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc14</artifactId>
            <version>${oracle.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                        <showWarnings>true</showWarnings>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>



    <modules>
        <module>pjy_ssm_dao</module>
        <module>pjy_ssm_service</module>
        <module>pjy_ssm_domain</module>
        <module>pjy_ssm_utils</module>
        <module>pjy_ssm_web</module>
    </modules>


</project>

初始化maven-webapp缓慢,可以在创建maven时,使用标签:

archetypeCatalog internal

导入pom.xml坐标,出现Oracle14.jar包如法加载的情况,可以自行打入maven本地仓库。

1.3编写实体类

在domain模块下创建商品实体类:

package com.pjy.ssm.domain;

import java.util.Date;

/*
* 商品实体类
* */
public class Product {
    private String id; // 主键
    private String productNum; // 编号 唯一
    private String productName; // 名称
    private String cityName; // 出发城市
    private Date departureTime; // 出发时间
    private String departureTimeStr;        //格式化数据为字符串类型,方便前台展示
    private double productPrice; // 产品价格
    private String productDesc; // 产品描述
    private Integer productStatus; // 状态 0 关闭 1 开启
    private String productStatusStr;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getProductNum() {
        return productNum;
    }

    public void setProductNum(String productNum) {
        this.productNum = productNum;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public Date getDepartureTime() {
        return departureTime;
    }

    public void setDepartureTime(Date departureTime) {
        this.departureTime = departureTime;
    }

    public String getDepartureTimeStr() {
        return departureTimeStr;
    }

    public void setDepartureTimeStr(String departureTimeStr) {
        this.departureTimeStr = departureTimeStr;
    }

    public double getProductPrice() {
        return productPrice;
    }

    public void setProductPrice(double productPrice) {
        this.productPrice = productPrice;
    }

    public String getProductDesc() {
        return productDesc;
    }

    public void setProductDesc(String productDesc) {
        this.productDesc = productDesc;
    }

    public Integer getProductStatus() {
        return productStatus;
    }

    public void setProductStatus(Integer productStatus) {
        this.productStatus = productStatus;
    }

    public String getProductStatusStr() {
        return productStatusStr;
    }

    public void setProductStatusStr(String productStatusStr) {
        this.productStatusStr = productStatusStr;
    }

    @Override
    public String toString() {
        return "Product{" + "id='" + id + '\'' + ", productNum='" + productNum + '\'' + ", productName='" + productName + '\'' + ", cityName='" + cityName + '\'' + ", departureTime=" + departureTime + ", departureTimeStr='" + departureTimeStr + '\'' + ", productPrice=" + productPrice + ", productDesc='" + productDesc + '\'' + ", productStatus=" + productStatus + ", productStatusStr='" + productStatusStr + '\'' + '}';
    }
}

1.4 编写持久层接口

package com.pjy.ssm.dao;

import com.pjy.ssm.domain.Product;

import java.util.List;

public interface IProductDao {

    //查询所有的商品信息
    public List<Product> findAll() throws Exception;

}

1.5 编写业务接口实现类

package com.pjy.ssm.service.impl;

import com.pjy.ssm.dao.IProductDao;
import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class productServiceImpl implements IProductService {

    @Autowired
    private IProductDao productDao;

    @Override
    public List<Product> findAll() throws Exception {
        return productDao.findAll();
    }
}

4.SSM整合与产品查询

在这里插入图片描述

4.1 Spring环境搭建

2.1.1.编写Spring配置文件applicationContext.xml

在ssm_web工程下创建java、resources资源文件夹,并制定相应的文件类型:

在这里插入图片描述

在resources下创建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"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 开启注解扫描,管理service和dao -->
    <context:component-scan base-package="com.pjy.ssm.service">
    </context:component-scan>
    <context:component-scan base-package="com.pjy.ssm.dao">
    </context:component-scan>

    <!--扫描数据库配置文件-->
    <context:property-placeholder location="classpath:db.properties"/>

    <!-- 配置连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!-- 把交给IOC管理 SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 扫描dao接口 -->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.pjy.ssm.dao"/>
    </bean>

    <!-- 配置Spring的声明式事务管理 -->
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>
2.1.2.使用注解配置业务层

在ssm_sercive中实现接口,并为接口实现impl类,通过注解配置:

package com.pjy.ssm.service.impl;

import com.pjy.ssm.dao.IProductDao;
import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class productServiceImpl implements IProductService {

    @Autowired
    private IProductDao productDao;

    @Override
    public List<Product> findAll() throws Exception {
        return productDao.findAll();
    }
}

4.2 Spring MVC 环境搭建

4.2.1.web.xml配置Spring MVC核心控制器

在ssm_web下webapp-WEB-INF-web.xml中配置核心控制器:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

  <!-- 配置加载类路径的配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml</param-value>
  </context-param>

  <!-- 配置监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- 配置监听器,监听request域对象的创建和销毁的 -->
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>
  <!-- 前端控制器(加载classpath:springmvc.xml 服务器启动创建servlet) -->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <!-- 解决中文乱码过滤器 -->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>
4.2.2.Spring MVC配置文件springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd
           ">
    <!-- 扫描controller的注解,别的不扫描 -->
    <context:component-scan base-package="com.pjy.ssm.controller">
    </context:component-scan>

    <!-- 配置视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- JSP文件所在的目录 -->
        <property name="prefix" value="/pages/" />
        <!-- 文件的后缀名 -->
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- 设置静态资源不过滤 -->
    <mvc:resources location="/css/" mapping="/css/**" />
    <mvc:resources location="/img/" mapping="/img/**" />
    <mvc:resources location="/js/" mapping="/js/**" />
    <mvc:resources location="/plugins/" mapping="/plugins/**" />

    <!-- 开启对SpringMVC注解的支持 -->
    <mvc:annotation-driven />

    <!--
        支持AOP的注解支持,AOP底层使用代理技术
        JDK动态代理,要求必须有接口
        cglib代理,生成子类对象,proxy-target-class="true" 默认使用cglib的方式
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
4.2.3.编写Controller

在ssm_web-main-java下创建com.pjy.ssm.controller包,创建ProductController类:

package com.pjy.ssm.controller;

import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private IProductService productService;

    @RequestMapping("/findAll.do")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List<Product> ps = productService.findAll();
        mv.addObject("productList",ps);
        mv.setViewName("product-list");
        return mv;
    }
}

4.3 Spring与Spring MVC整合

<!-- 配置加载类路径的配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml</param-value>
  </context-param>

  <!-- 配置监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- 配置监听器,监听request域对象的创建和销毁的 -->
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>

4.4 Spring与MyBatis整合

4.4.1.整合思路

把 mybatis 配置文件(mybatis.xml)中内容配置到 spring 配置文件中 同时,把 mybatis 配置文件的内容清掉。

<?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>
 
</configuration>

注意: 理 由于我们使用的是代理 Dao 的模式,Dao 具体实现类由 MyBatis 使用代理方式创建,所以此时 mybatis 配置文件不能删。 当我们整合 spring 和 mybatis 时,mybatis 创建的 Mapper.xml 文件名必须和 Dao 接口 文件 名一致

4.4.2.Spring接管mybatis的Session工厂

在ssm_web-resources下创建db.properties资源文件,设置值:

jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
jdbc.username=ssm
jdbc.password=ssm

在applicationContext.xml中配置连接池:

<!-- 配置连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!-- 把交给IOC管理 SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>
2.4.3.自动扫描所有Mapper接口和文件
  <!-- 扫描dao接口 -->
   <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="com.pjy.ssm.dao"/>
   </bean>
2.4.4 配置spring事务
 <!-- 配置Spring的声明式事务管理 -->
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>

4.5 测试运行

使用AdminLTE开源框架,将HTML文件,转换为jsp文件,在webapp下引入资源文件,css、img、plugins并创建pages包存放页面信息。使用maven中的Tomcat7启动,这样可以查看项目在服务器上运行的状态,在启动前在pom.xml中配置(可以使用快捷键)
在这里插入图片描述

        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8888</port>
                </configuration>
                <version>2.2</version>
            </plugin>
        </plugins>

在maven管理中清理工程,打包,启动Tomcat服务器:

在这里插入图片描述

2.5.1.编写jsp页面
2.5.1.1 请求发起页面 index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello World!</h2>
<a href="${pageContext.request.contextPath}/product/findAll.do">查询所有的商品信息</a>
</body>
</html>

在这里插入图片描述
点击请求连接,触发controller控制器中的ProductController类,执行findAll()方法,方法中创建一个新的视图解析器,调用service层中ProductService实现类中的findAll()方法,在service实现中调用持久层findAll()方法,通过注解配置查询语句:

@Select("select * from product")

在将查询到的数据封装List中返回给业务层,业务层返回给controller中,在通过addObject()方法添加到视图解析器中,通过前端页面product-list中的items属性,遍历视图解析器中的元素

<tbody>
    <c:forEach items="${productList}" var="product">
        <tr>
            <td><input name="ids" type="checkbox"></td>
            <td>${product.id }</td>
            <td>${product.productNum }</td>
            <td>${product.productName }</td>
            <td>${product.cityName }</td>
            <td>${product.departureTimeStr }</td>
            <td class="text-center">${product.productPrice }</td>
            <td>${product.productDesc }</td>
            <td class="text-center">${product.productStatusStr }</td>
            <td class="text-center">
                <button type="button" class="btn bg-olive btn-xs">订单</button>
                <button type="button" class="btn bg-olive btn-xs">详情</button>
                <button type="button" class="btn bg-olive btn-xs">编辑</button>
            </td>
        </tr>
    </c:forEach>
</tbody>

controller:

package com.pjy.ssm.controller;

import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private IProductService productService;

    @RequestMapping("/findAll.do")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List<Product> ps = productService.findAll();
        mv.addObject("productList",ps);
        mv.setViewName("product-list");
        return mv;
    }
}

启动服务器:

http://localhost:8888/pjy_ssm_web 点击链接: [查询所有的商品信息],得到所有的商品信息

在这里插入图片描述

5.商品添加

在这里插入图片描述

5.1 商品添加页面product-add.jsp

在web-pages下创建页面

在这里插入图片描述

在web-controller包下ProductController中创建save方法

/*
* 产品添加
* */
@RequestMapping("/save.do")
public void save(Product product) throws Exception {
	productService.save(product);
}

跳转到service中,添加实现类,调用save方法:

@Override
public void save(Product product) throws Exception {
	productDao.save(product);
}

跳转到Dao中,实现商品的添加:

package com.pjy.ssm.dao;

import com.pjy.ssm.domain.Product;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface IProductDao {

    //查询所有的商品信息
    @Select("select * from product")
    public List<Product> findAll() throws Exception;

    //添加商品
    @Insert("insert into product(productNum,productName,cityName,departureTime,productPrice,productDesc,productStatus) values(#{productNum},#{productName},#{cityName},#{departureTime},#{productPrice},# {productDesc},#{productStatus})")
    void save(Product product);
}

启动服务器发现出现400错误:

在这里插入图片描述

加入log4g到resources资源下,启动服务器,查看日志信息,检查错误来源:

在这里插入图片描述

查看日志,可以看到错误信息中显示,departureTime类型错误,要将页面传送的String类型转换为date类型,保存到数据库中,这是一个典型了SpringMVC中类型转换问题。

解决方法:

可以使用

1、局部解决方法:在product实体类中date属性上加入注解:

在这里插入图片描述

2、使用全局设置(暂未实现)

在这里插入图片描述

清空项目包,从新启动tomcat:

在这里插入图片描述

得到查询结果。

6.SSM订单操作

在这里插入图片描述

6.1.订单表信息描述 order

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2ordertNumvarchar2(50)订单编号 不为空 唯一
3orderTimetimestamp下单时间
4peopleCountint出行人数
5orderDescvarchar2(500)订单描述(其他信息)
6payTypeint支付方式(0支付宝1微信2其他)
7orderStatusint订单状态(0未支付1已支付)
8productIdint产品id外键
9memberidint会员(联系人)id外键

productId描述了订单与产品之间的关系。

memberid描述了订单与会员之间的关系。

创建表sql

 CREATE TABLE orders(  
     id varchar2(32) default SYS_GUID() PRIMARY KEY,  
     orderNum VARCHAR2(20) NOT NULL UNIQUE,  
     orderTime timestamp,  
     peopleCount INT,  
     orderDesc VARCHAR2(500),  
     payType INT,  
     orderStatus INT,  
     productId varchar2(32),  
     memberId varchar2(32),  
     FOREIGN KEY (productId) 
     REFERENCES product(id),  
     FOREIGN KEY (memberId) REFERENCES member(id) 
 );

insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('0E7231DC797C486290E8713CA3C6ECCC', '12345', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '676C5BD1D35E429A8C2E114939C5685A', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('5DC6A48DD4E94592AE904930EA866AFA', '54321', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '676C5BD1D35E429A8C2E114939C5685A', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('2FF351C4AC744E2092DCF08CFD314420', '67890', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('A0657832D93E4B10AE88A2D4B70B1A28', '98765', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('E4DD4C45EED84870ABA83574A801083E', '11111', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('96CC8BD43C734CC2ACBFF09501B4DD5D', '22222', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('55F9AF582D5A4DB28FB4EC3199385762', '33333', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '9F71F01CB448476DAFB309AA6DF9497F', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('CA005CF1BE3C4EF68F88ABC7DF30E976', '44444', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '9F71F01CB448476DAFB309AA6DF9497F', 'E61D65F673D54F68B0861025C69773DB'); 
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid) 
values ('3081770BC3984EF092D9E99760FDABDE', '55555', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '9F71F01CB448476DAFB309AA6DF9497F', 'E61D65F673D54F68B0861025C69773DB');

实体类:

public class Orders {
    private String id;    private String orderNum;    private Date orderTime;
    private String orderTimeStr;
     private int orderStatus;    
     private int peopleCount;    
     private Product product;    
     private List<Traveller> travellers;    
     private Member member;    
     private Integer payType;    
     private String payTypeStr;    
     private String orderDesc;    
     //省略getter/setter 
     }

6.2 会员表信息描述member

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2namevarchar2(50)名称
3nickNamevarchar2(50)呢称
4phoneNumvarchar2(50)电话号码
5emailvarchar2(50)邮箱

订单与会员之间是多对一关系,我们在订单表中创建一个外键来进行关联。

CREATE TABLE member(       
id varchar2(32) default SYS_GUID() PRIMARY KEY,
NAME VARCHAR2(20),
nickname VARCHAR2(20),
phoneNum VARCHAR2(20),
email VARCHAR2(20)
) 
insert into MEMBER (id, name, nickname, phonenum, email) values ('E61D65F673D54F68B0861025C69773DB', '张三', '小三', '18888888888', 'zs@163.com');

实体类:

public class Member {
    private String id;    
    private String name;    
    private String nickname;    
    private String phoneNum;    
    private String email;    
    //省略getter/setter 
}

6.3.旅客表信息描述 traveller

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2namevarchar2(20)姓名
3sexvarchar2(20)性别
4phoneNumvarchar2(20)电话号码
5credentialsNumint证件类型0身份证 1护照 2军官证
6credentialsNumvarchar(50)证件号码
7travellerTypeint旅客类型(人群)0成人 1儿童

创建表sql

CREATE TABLE traveller(    
    id varchar2(32) default SYS_GUID() PRIMARY KEY,    
    NAME VARCHAR2(20),    
    sex VARCHAR2(20),    
    phoneNum VARCHAR2(20),    
    credentialsType INT,    
    credentialsNum VARCHAR2(50),    
    travellerType INT 
) ;
insert into TRAVELLER (id, name, sex, phonenum, credentialstype, credentialsnum, travellertype) values ('3FE27DF2A4E44A6DBC5D0FE4651D3D3E', '张龙', '男', '13333333333', 0, '123456789009876543', 0); 
insert into TRAVELLER (id, name, sex, phonenum, credentialstype, credentialsnum, travellertype) values ('EE7A71FB6945483FBF91543DBE851960', '张小龙', '男', '15555555555', 0, '987654321123456789', 1);

实体类:

public class Traveller {    
    private String id;    
    private String name;    
    private String sex;    
    private String phoneNum;    
    private Integer credentialsType;    
    private String credentialsTypeStr;    
    private String credentialsNum;    
    private Integer travellerType;    
    private String travellerTypeStr;    
    //省略getter/setter
}

旅客与订单之间是多对多关系,所以我们需要一张中间表(order_traveller)来描述。

序号字段名称字段类型字段描述
1orderidvarchar2(32)订单id
2travellerIdvarchar2(32)旅客id

创建sql

CREATE TABLE order_traveller(    
orderId varchar2(32),    
travellerId varchar2(32),    
PRIMARY KEY (orderId,travellerId),    
FOREIGN KEY (orderId) REFERENCES orders(id),    
FOREIGN KEY (travellerId) REFERENCES traveller(id) 
);

insert into ORDER_TRAVELLER (orderid, travellerid) values ('0E7231DC797C486290E8713CA3C6ECCC', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('2FF351C4AC744E2092DCF08CFD314420', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('3081770BC3984EF092D9E99760FDABDE', 'EE7A71FB6945483FBF91543DBE851960'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('55F9AF582D5A4DB28FB4EC3199385762', 'EE7A71FB6945483FBF91543DBE851960'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('5DC6A48DD4E94592AE904930EA866AFA', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('96CC8BD43C734CC2ACBFF09501B4DD5D', 'EE7A71FB6945483FBF91543DBE851960'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('A0657832D93E4B10AE88A2D4B70B1A28', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('CA005CF1BE3C4EF68F88ABC7DF30E976', 'EE7A71FB6945483FBF91543DBE851960'); 
insert into ORDER_TRAVELLER (orderid, travellerid) values ('E4DD4C45EED84870ABA83574A801083E', 'EE7A71FB6945483FBF91543DBE851960');

6.4 订单查询

在这里插入图片描述

6.4.1 订单查询页面 order-list.jsp

在这里插入图片描述

6.4.2 Controller

在ssm_web板块创建OrderController

package com.pjy.ssm.controller;

import com.pjy.ssm.domain.Orders;
import com.pjy.ssm.service.IOrdersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("/orders")
public class OrdersController {

    @Autowired
    private IOrdersService ordersService;

    @RequestMapping("/findAll.do")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List<Orders> ordersList = ordersService.findAll();
        mv.addObject("ordersList",ordersList);
        mv.setViewName("orders-list");
        return mv;
    }
}

创建service层接口IOrdersService添加实现类:

package com.pjy.ssm.service.impl;

import com.pjy.ssm.dao.IOrdersDao;
import com.pjy.ssm.domain.Orders;
import com.pjy.ssm.service.IOrdersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class OrdersServiceImpl implements IOrdersService {

    @Autowired
    private IOrdersDao ordersDao;

    @Override
    public List<Orders> findAll() throws Exception {
        return ordersDao.findAll();
    }
}

在service中调用Dao层IOrdersDao类中的方法:

package com.pjy.ssm.dao;

import com.pjy.ssm.domain.Orders;
import com.pjy.ssm.domain.Product;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface IOrdersDao {

    /*
    * 在查询中添加映射关系,在此实体类和数据库中的字段名相同,不用改别名
    * 在查询中Product和Orders是一对多的关系(由于旅游产品的业务需求决定的,一个订单中只有一个旅游地址,一个旅游地址可以对应多个订单(一对多))
    * id = true:设置Orders表中的主键
    * 元素名称和属性名相同(实体类对应表结构)
    * column = "productId":实体类product对应product实体类中的productId属性,
    * one = @One:指定对应关系:调用IProductDao:中的findById查询方法
    * 此处用的是注解形式,也可以配置xml文件
    * */
    @Select("select * from orders")
    @Results({
            @Result(id = true, property = "id", column = "id"),
            @Result(property = "orderNum", column = "orderNum"),
            @Result(property = "orderTime", column = "orderTime"),
            @Result(property = "orderStatus", column = "orderStatus"),
            @Result(property = "peopleCount", column = "peopleCount"),
            @Result(property = "payType", column = "payType"),
            @Result(property = "payTypeStr", column = "payTypeStr"),
            @Result(property = "orderDesc", column = "orderDesc"),
            @Result(property = "product", column = "productId",javaType = Product.class, one = @One(select = "com.pjy.ssm.dao.IProductDao.findById")),
    })
    public List<Orders> findAll() throws Exception;

}

在Dao层中的IProduct接口中添加查询方法:

//根据id查询产品
@Select("select * from product where id=#{id}")
public Product findById(String id) throws Exception;

重新打包,启动服务器,查看状态,得到查询结果,如上图所示。

6.4.3 订单分页查询

在这里插入图片描述

6.4.3.1 PageHelper介绍

PageHelper是国内非常优秀的一款开源的mybatis分页插件,它支持基本主流与常用的数据库,例如mysql、 oracle、mariaDB、DB2、SQLite、Hsqldb等。

本项目在 github 的项目地址:https://github.com/pagehelper/Mybatis-PageHelper

本项目在 gitosc 的项目地址:http://git.oschina.net/free/Mybatis_PageHelper

6.4.3.2 PageHelper使用

1、集成
引入分页插件有下面2种方式,推荐使用 Maven 方式。

1.1、引入 Jar 包
你可以从下面的地址中下载最新版本的 jar 包

https://oss.sonatype.org/content/repositories/releases/com/github/pagehelper/pagehelper/

http://repo1.maven.org/maven2/com/github/pagehelper/pagehelper/

由于使用了sql 解析工具,你还需要下载 jsqlparser.jar:

http://repo1.maven.org/maven2/com/github/jsqlparser/jsqlparser/0.9.5/

1.2、使用 Maven

在 pom.xml 中添加如下依赖:

<dependency>    
    <groupId>com.github.pagehelper</groupId>    
    <artifactId>pagehelper</artifactId>    
    <version>最新版本</version> 
</dependency>

2、配置

特别注意,新版拦截器是 com.github.pagehelper.PageInterceptor 。

com.github.pagehelper.PageHelper 现 在是一个特殊的 dialect 实现类,是分页插件的默认实现类,提供了和以前相同的用法。

2.1、 在 MyBatis 配置 xml 中配置拦截器插件

<!--
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:    
    properties?, settings?,     
    typeAliases?, typeHandlers?,     
    objectFactory?,objectWrapperFactory?,     
    plugins?,     
    environments?, 
    databaseIdProvider?, mappers? 
--> 
<plugins>    
    <!-- com.github.pagehelper为PageHelper类所在包名 -->    
    <plugin interceptor="com.github.pagehelper.PageInterceptor">        
        <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->        
        <property name="param1" value="value1"/>    
    </plugin> 
</plugins>

2.2、在 Spring 配置文件中配置拦截器插件

使用 spring 的属性配置方式,可以使用 plugins 属性像下面这样配置:

<!-- 把交给IOC管理 SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--传入PageHelper的插件-->
        <property name="plugins">
            <array>
                <!--传入插件对象-->
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">oracle</prop>
                            <prop key="reasonable">true</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

2.3、 分页插件参数介绍

1、helperDialect :分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置 helperDialect 属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值: oracle , mysql , mariadb , sqlite , hsqldb , postgresql , db2 , sqlserver , informix , h2 , sqlserver2012 , derby

特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012 ,否则会使用 SqlServer2005 的 方式进行分页。 你也可以实现 AbstractHelperDialect ,然后配置该属性为实现类的全限定名称即可使用自定义的实现方 法。

2、 offsetAsPageNum :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分 页。

3、 rowBoundsWithCount :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置 为 true 时,使用 RowBounds 分页会进行 count 查询。

4、pageSizeZero :默认值为 false ,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。

5、 reasonable :分页合理化参数,默认值为 false 。当该参数设置为 true 时, pageNum<=0 时会查询第一 页, pageNum>pages (超过总数时),会查询最后一页。默认 false 时,直接根据参数进行查询。

6、 params :为了支持 startPage(Object params) 方法,增加了该参数来配置参数映射,用于从对象中根据属 性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable ,不配置映射的用默认值, 默认 值为 pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero 。

7、supportMethodsArguments :支持通过 Mapper 接口参数来传递分页参数,默认值 false ,分页插件会从查 询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法 可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest 。

8、autoRuntimeDialect :默认值为 false 。设置为 true 时,允许在运行时根据多数据源自动识别对应方言 的分页 (不支持自动选择 sqlserver2012 ,只能使用 sqlserver ),用法和注意事项参考下面的场景五。

9、 closeConn :默认值为 true 。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类 型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认 true 关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。

2.4、基本使用

PageHelper的基本使用有6种,大家可以查看文档,最常用的有两种

1、RowBounds方式的调用(了解)

List<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));

使用这种调用方式时,你可以使用RowBounds参数进行分页,这种方式侵入性最小,我们可以看到,通过 RowBounds方式调用只是使用了这个参数,并没有增加其他任何内容。

分页插件检测到使用了RowBounds参数时,就会对该查询进行物理分页。 关于这种方式的调用,有两个特殊的参数是针对 RowBounds 的,你可以参看上面的分页插件参数介绍

注:不只有命名空间方式可以用RowBounds,使用接口的时候也可以增加RowBounds参数,例如:

//这种情况下也会进行物理分页查询 
List<Country> selectAll(RowBounds rowBounds); 

注意: 由于默认情况下的 RowBounds 无法获取查询总数,分页插件提供了一个继承自 RowBounds 的 PageRowBounds ,这个对象中增加了 total 属性,执行分页查询后,可以从该属性得到查询总数。

2、PageHelper.startPage 静态方法调用(重点)

这种方式是我们要掌握的 在你需要进行分页的 MyBatis 查询方法前调用PageHelper.startPage 静态方法即可,紧 跟在这个方法后的第一个MyBatis 查询方法会被进行分页。

//获取第1页,10条内容,默认查询总数
count PageHelper.startPage(1, 10);
//紧跟着的第一个select方法会被分页 
List<Country> list = countryMapper.selectIf(1);
6.4.3.3 订单分页查询

1、Controller

在OrdersController中的findAll()方法中,添加查询方法:

 @RequestMapping("/findAll.do")
    public ModelAndView findAll(@RequestParam(name = "page", required = true,defaultValue = "1") int page,@RequestParam(name = "size",required = true,defaultValue = "5") int size) throws Exception {
        //创建视图模板
        ModelAndView mv = new ModelAndView();
        //在查询方法中添加分页参数
        List<Orders> ordersList = ordersService.findAll(page, size);
        //PageInfo--一个分页的JavaBean对象
        PageInfo pageInfo = new PageInfo(ordersList);
        mv.addObject("pageInfo",pageInfo);
        mv.setViewName("orders-page-list");
        return mv;
    }

2、service

在OrdersService接口实现类中findAll方法上传入参数page、size,得到前端页面中传递的当前页数和分页数,

注意:PageHelper方法必须在调用Dao方法的上方,写到其他地方都没法正常使用:

@Override
public List<Orders> findAll(int page, int size) throws Exception {
    //参数pageNum是页码值,参数pageSize代表是每页显示条数
    PageHelper.startPage(page,size);
    return ordersDao.findAll();
}

3、order-page-list.jsp界面:

在前端界面中注意Controller传递的list集合是pageInfo,所以在遍历时,要修改成对应的集合信息(items):

<c:forEach items="${pageInfo.list}" var="orders">
    <tr>
        <td><input name="ids" type="checkbox"></td>
        <td>${orders.id }</td>
        <td>${orders.orderNum }</td>
        <td>${orders.product.productName }</td>
        <td>${orders.product.productPrice }</td>
        <td>${orders.orderTimeStr }</td>
        <td class="text-center">${orders.orderStatusStr }</td>
        <td class="text-center">
            <button type="button" class="btn bg-olive btn-xs">订单</button>
            <button type="button" class="btn bg-olive btn-xs" οnclick="location.href='${pageContext.request.contextPath}/orders/findById.do?id=${orders.id}'">详情</button>
            <button type="button" class="btn bg-olive btn-xs">编辑</button>
        </td>
    </tr>
</c:forEach>

在Controller中我们接受了PageInfo对象,通过调用封装的对应属性值,完成页面的跳转功能,在每页显示条数功能上用js实现,在applicationContext.xml中,配置了边界限制,所以当超过第一页和最后一页时,会默认回到当前页码:

<div class="box-footer">
    <div class="pull-left">
        <div class="form-group form-inline">
            总共${pageInfo.pages} 页,共${pageInfo.total} 条数据。 每页
            <select class="form-control" id="changePageSize" οnchange="changePageSize()">
                <option>1</option>
                <option>2</option>
                <option>3</option>
                <option>4</option>
                <option>5</option>
            </select> 条
        </div>
    </div>

    <div class="box-tools pull-right">
        <ul class="pagination">
            <li>
                <a href="${pageContext.request.contextPath}/orders/findAll.do?page=1&size=${pageInfo.pageSize}" aria-label="Previous">首页</a>
            </li>
            <li>
                <a href="${pageContext.request.contextPath}/orders/findAll.do?page=${pageInfo.pageNum-1}&size=${pageInfo.pageSize}">上一页</a>
            </li>
            <c:forEach begin="1" end="${pageInfo.pages}" var="pageNum">
                <li>
                    <a href="${pageContext.request.contextPath}/orders/findAll.do?page=${pageNum}&size=${pageInfo.pageSize}">${pageNum}</a>
                </li>
            </c:forEach>
            <li>
                <a href="${pageContext.request.contextPath}/orders/findAll.do?page=${pageInfo.pageNum+1}&size=${pageInfo.pageSize}">下一页</a>
            </li>
            <li>
                <a href="${pageContext.request.contextPath}/orders/findAll.do?page=${pageInfo.pages}&size=${pageInfo.pageSize}" aria-label="Next">尾页</a>
            </li>
        </ul>
    </div>
</div>

清空项目,重启项目,点击基础数据-订单管理,查看是否正确

在这里插入图片描述

6.5 订单详情

在这里插入图片描述

6.5.1 前台页面处理

在order-page-list.jsp页面上对"详情"添加链接

<td class="text-center">
    <button type="button" class="btn bg-olive btn-xs">订单</button>
    <button type="button" class="btn bg-olive btn-xs" onclick="location.href='${pageContext.request.contextPath}/orders/findById.do?id=${orders.id}'">详情</button>
    <button type="button" class="btn bg-olive btn-xs">编辑</button>
</td>

导入order-show.jsp页面到pages中

6.5.2 Controller

在OrdersController中添加方法findById,传入id参数:

@RequestMapping("/findById.do")
public ModelAndView findById(@RequestParam(name = "id", required = true) String ordersId) throws Exception {
    ModelAndView mv = new ModelAndView();
    Orders orders = ordersService.findById(ordersId);
    mv.addObject("orders",orders);
    mv.setViewName("orders-show");
    return mv;
}

6.5.2 Service

在OrdersServiceIpml实现类中添加findById方法,传入ordersId:

@Override
public Orders findById(String ordersId) throws Exception {
	return ordersDao.findById(ordersId);
}

6.5.3 Dao

在IOrdersDao中添加findById在查询订单orders中包含member(会员)信息(多对一)、product(产品)信息(多对一)、travellers(游客)信息(多对多)。

/*
* 根据id查询订单详情
*  在查询过程中有多表操作
* */
@Select("select * from orders where id = #{ordersId}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "orderNum", column = "orderNum"),
@Result(property = "orderTime", column = "orderTime"),
@Result(property = "orderStatus", column = "orderStatus"),
@Result(property = "peopleCount", column = "peopleCount"),
@Result(property = "payType", column = "payType"),
@Result(property = "payTypeStr", column = "payTypeStr"),
@Result(property = "orderDesc", column = "orderDesc"),
@Result(property = "product", column = "productId",javaType = Product.class, one = @One(select = "com.pjy.ssm.dao.IProductDao.findById")),
@Result(property = "member", column = "memberId",javaType = Member.class, one = @One(select = "com.pjy.ssm.dao.IMemberDao.findById")),
@Result(property = "travellers",column = "id",javaType = java.util.List.class, many = @Many(select = "com.pjy.ssm.dao.ITravellerDao.findByOrdersId")),
})
Orders findById(String ordersId) throws Exception;

在dao层,分别创建IMemberDao、ITravellerDao接口,的查询方法,分别对应Result中的查询。

IMemberDao的findById方法

@Select("select * from member where id=#{id}") 
Member findById(String id) throws Exception;

ITravellerDao.findByOrdersId方法

 @Select("select * from traveller where id in (select travellerId from order_traveller where orderId=#{ordersId})")  
 List<Traveller> findByOrdersId(String ordersId) throws Exception;

重启服务器,访问订单详情页面:

在这里插入图片描述

7. SSM权限操作

7.1.数据库与表结构

在这里插入图片描述

7.1.1 用户表

1、用户表信息描述users

序号字段名字段类型字段描述
1idvarchar2(32)无意义,主键uuid
2emailvarchar2(20)非空,唯一
3usernamevarchar2(50)用户名
4passwordvarchar2(20)密码(加密)
5phoneNumvarchar2(20)电话
6statusint状态(0 未开启 1开启)

2、sql语句

CREATE TABLE users(    
    id varchar2(32) default SYS_GUID() PRIMARY KEY,    
    email VARCHAR2(50) UNIQUE NOT NULL,    
    username VARCHAR2(50),    
    PASSWORD VARCHAR2(50),    
    phoneNum VARCHAR2(20),    
    STATUS INT  
)

3、实体类

public class UserInfo {
    private String id;    
    private String username;    
    private String email;    
    private String password;    
    private String phoneNum;    
    private int status;    
    private String statusStr;    
    private List<Role> roles;
}
7.1.2 角色表

1、 角色表信息描述role

序号字段名字段类型字段描述
1idvarchar2(50)无意义,主键uuid
2roleNamevarchar2(50)角色名
3roleDescvarchar2(500)角色描述

2、SQL语句

CREATE TABLE role(    
id varchar2(32) default SYS_GUID() PRIMARY KEY,    
roleName VARCHAR2(50) ,    
roleDesc VARCHAR2(50) 
)

3、实体类

public class Role {
    private String id;    
    private String roleName;    
    private String roleDesc;    
    private List<Permission> permissions;    
    private List<User> users;
    }

4、用户与角色关联关系

用户与角色之间是多对多关系,我们通过user_role表来描述其关联,在实体类中User中存在List,在Role中有List. 而角色与权限之间也存在关系,我们会在后面介绍

CREATE TABLE users_role(    
    userId varchar2(32),   
    roleId varchar2(32),    
    PRIMARY KEY(userId,roleId),    
    FOREIGN KEY (userId) REFERENCES users(id),    
    FOREIGN KEY (roleId) REFERENCES role(id)
)
7.1.3 资源权限表

1、权限资源表描述permission

序号字段名字段类型字段描述
1idvarchar2(50)无意义,主键uuid
2permissionNamevarchar2(50)权限名
3urlvarchar2(500)资源路径

2、SQL语句

CREATE TABLE permission(    
id varchar2(32) default SYS_GUID() PRIMARY KEY,    
permissionName VARCHAR2(50) ,    
url VARCHAR2(50) 
)

3、实体类

public class Permission {
    private String id;    
    private String permissionName;    
    private String url;    
    private List<Role> roles; 
    }

4、权限资源与角色关联关系

权限资源与角色是多对多关系,我们使用role_permission表来描述。在实体类Permission中存在List,在Role类中 有List

CREATE TABLE role_permission(    
permissionId varchar2(32),    
roleId varchar2(32),    
PRIMARY KEY(permissionId,roleId),    
FOREIGN KEY (permissionId) REFERENCES permission(id),    
FOREIGN KEY (roleId) REFERENCES role(id)
)

7.2 Spring Security概述

1、Spring Security介绍

Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。

(https://projects.spring.io/spring-security/) Spring Security 为基于J2EE企业应用软件提供了全面安全服务。特别 是使用领先的J2EE解决方案-Spring框架开发的企业软件项目。人们使用Spring Security有很多种原因,不过通常吸 引他们的是在J2EE Servlet规范或EJB规范中找不到典型企业应用场景的解决方案。 特别要指出的是他们不能再 WAR 或 EAR 级别进行移植。这样,如果你更换服务器环境,就要,在新的目标环境进行大量的工作,对你的应用 系统进行重新配 置安全。使用Spring Security 解决了这些问题,也为你提供很多有用的,完全可以指定的其他安 全特性。 安全包括两个主要操作。

“认证”,是为用户建立一个他所声明的主体。主题一般式指用户,设备或可以在你系 统中执行动作的其他系 统。

“授权”指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由 身份验证 过程建立了。

这些概念是通用的,不是Spring Security特有的。在身份验证层面,Spring Security广泛支持各种身份验证模式, 这些验证模型绝大多数都由第三方提供,或则正在开发的有关标准机构提供的,例如 Internet Engineering Task Force.作为补充,Spring Security 也提供了自己的一套验证功能。

Spring Security 目前支持认证一体化如下认证技术:

HTTP BASIC authentication headers (一个基于IEFT RFC 的 标准)

HTTP Digest authentication headers (一个基于IEFT RFC 的标准)

HTTP X.509 client certificate exchange (一个基于IEFT RFC 的标准)

LDAP (一个非常常见的跨平台认证需要做法,特别是在大环境)

Form-based authentication (提供简单用户接口的需求)

OpenID authentication Computer Associates Siteminder JA-SIG Central Authentication Service (CAS,这是一个流行的开源单点登录系统)

Transparent authentication context propagation for Remote Method Invocation and HttpInvoker (一个Spring远程调用协议)

Maven依赖:

<dependencies>    
    <dependency>        
        <groupId>org.springframework.security</groupId>        
        <artifactId>spring-security-web</artifactId>        
        <version>5.0.1.RELEASE</version>    
    </dependency>    
    <dependency>        
        <groupId>org.springframework.security</groupId>        
        <artifactId>spring-security-config</artifactId>        
        <version>5.0.1.RELEASE</version>    
    </dependency> 
</dependencies>

7.3 Spring Security快速入门

创建一个新的maven工程,选择webapp模板,命名为springSecurityTest

在这里插入图片描述
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>com.pjy.springSecurityTest</groupId>
    <artifactId>springSecurityTest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>springSecurityTest Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <spring.version>5.0.2.RELEASE</spring.version>
        <spring.security.version>5.0.1.RELEASE</spring.security.version>
    </properties>

    <dependencies>
        <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-webmvc</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>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <!-- 指定端口 -->
                    <port>8080</port>
                    <!-- 请求路径 -->
                    <path>/</path>
                </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_2_5.xsd"
         version="2.5">
  <display-name>SpringSecurity314</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-security.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

3、spring-security配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       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/security
       http://www.springframework.org/schema/security/spring-security.xsd">
    <security:http auto-config="true"
                   use-expressions="false">
        <!-- intercept-url定义一个过滤规则 pattern表示对哪些url进行权限控制,ccess属性表示在请求对应的URL时需要什么权限,
                     默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应 的URL
                     -->
        <security:intercept-url pattern="/**"
                                access="ROLE_USER"/>
        <!-- auto-config配置后,不需要在配置下面信息
        <security:form-login /> 定义登录表单信息
        <security:http-basic
        /> <security:logout /> -->
    </security:http>
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{noop}user"
                               authorities="ROLE_USER"/>
                <security:user name="admin" password="{noop}admin"
                               authorities="ROLE_ADMIN"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

4、测试

我们在webapp下创建一个index.html页面,在页面中任意写些内容。

启动Tomcat,访问页面,在输入设定好的账号信息。

当输入非正确数据时,显示错误(红色信息)

当输入user账户时,可以显示index.jsp界面,当输入admin账号时,显示的403错误界面,因为权限不足导致无法访问index界面

在这里插入图片描述

当我们访问index.html页面时发现会弹出登录窗口,可能你会奇怪,我们没有建立下面的登录页面,为什么Spring Security会跳到上面的登录页面呢?这是我们设置http的auto-config=”true”时Spring Security自动为我们生成的

5、使用自定义页面
5.1 spring-security.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       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/security
       http://www.springframework.org/schema/security/spring-security.xsd">
    <!-- 配置不过滤的资源(静态资源及登录相关) -->
    <security:http security="none" pattern="/login.html" />
    <security:http security="none" pattern="/failer.html" />
    <security:http auto-config="true" use-expressions="false" >
        <!-- 配置资料连接,表示任意路径都需要ROLE_USER权限 -->
        <security:intercept-url pattern="/**" access="ROLE_USER" />
        <!-- 自定义登陆页面,login-page 自定义登陆页面 authentication-failure-url 用户权限校验失败之后才会跳转到这个页面,如果数据库中没有这个用户则不会跳转到这个页面。
            default-target-url 登陆成功后跳转的页面。 注:登陆页面用户名固定 username,密码 password,action:login -->
        <security:form-login login-page="/login.html"
                             login-processing-url="/login"
                             username-parameter="username"
                             password-parameter="password"
                             authentication-failure-url="/failer.html"
                             default-target-url="/success.html"
                             authentication-success-forward-url="/success.html"
        />
        <!-- 关闭CSRF,默认是开启的 -->
        <security:csrf disabled="true" />
    </security:http>
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{noop}user"
                               authorities="ROLE_USER" />
                <security:user name="admin" password="{noop}admin"
                               authorities="ROLE_ADMIN" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

5.2、配置前端界面
在webapp下login.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title></head>
<body>
<form action="login" method="post">
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"/></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"/></td>
        </tr>
        <tr>
            <td colspan="2" align="center"><input type="submit" value="登录"/> <input type="reset" value="重置"/></td>
        </tr>
    </table>
</form>
</body>
</html>

在分别配置failer、success界面,在启动服务器,输入登录信息,

在这里插入图片描述
当输入正确时,跳转到success界面,由于设置了默认跳转success界面,当输入成功时,没有区分角色权限user、admin账户跳转到同一个界面中:

在这里插入图片描述

7.4 Spring Security使用数据库认证

在Spring Security中如果想要使用数据进行认证操作,有很多种操作方式,这里我们介绍使用UserDetails、 UserDetailsService来完成操作。

在这里插入图片描述
UserDetails是一个接口,我们可以认为UserDetails作用是于封装当前进行认证的用户信息,但由于其是一个 接口,所以我们可以对其进行实现,也可以使用Spring Security提供的一个UserDetails的实现类User来完成 操作

以下是User类的部分代码

public class User implements UserDetails, CredentialsContainer {
 	private String password;    
 	private final String username;    
 	private final Set<GrantedAuthority> authorities;    
 	private final boolean accountNonExpired; //帐户是否过期    
 	private final boolean accountNonLocked; //帐户是否锁定    
 	private final boolean credentialsNonExpired; //认证是否过期    
 	private final boolean enabled; //帐户是否可用

UserDetailsService

public interface UserDetailsService {        
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; 
}

上面将UserDetails与UserDetailsService做了一个简单的介绍,那么我们具体如何完成Spring Security的数据库认 证操作哪,我们通过用户管理中用户登录来完成Spring Security的认证操作。

7.5 用户管理

7.5.1 用户登录

spring security的配置

 <security:authentication-manager>        
     <security:authentication-provider user-service-ref="userService">            
     <!-- 配置加密的方式             
     <security:password-encoder ref="passwordEncoder"/>            
     -->        
     </security:authentication-provider>    
 </security:authentication-manager>

7.5.2 登录页面login.jsp

把登录界面login.jsp放在webapp下,设置表单的提交方式:

<form action="${pageContext.request.contextPath}/login.do" method="post">
    <div class="form-group has-feedback">
        <input type="text" name="username" class="form-control"
        placeholder="用户名"> <span
        class="glyphicon glyphicon-envelope form-control-feedback"></span>
    </div>
    <div class="form-group has-feedback">
        <input type="password" name="password" class="form-control"
        placeholder="密码"> <span
        class="glyphicon glyphicon-lock form-control-feedback"></span>
    </div>
    <div class="row">
        <div class="col-xs-8">
            <div class="checkbox icheck">
            <label><input type="checkbox"> 记住 下次自动登录</label>
            </div>
        </div>
        <!-- /.col -->
        <div class="col-xs-4">
    	<button type="submit" class="btn btn-primary btn-block btn-flat">登录</button>
    </div>
    <!-- /.col -->
    </div>
</form>

在这里插入图片描述

7.5.1.1 Service

创建IUserService对象,继承UserDetailsService,创建接口实现类:UserServiceImpl,实现loadUserByUsername

@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = null;
        try {
           userInfo = userDao.findByUsername(username);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //处理自己的用户对象封装成UserDetails
        //User user = new User(userInfo.getUsername(), "{noop}"+userInfo.getPassword(),getAuthority(userInfo.getRoles()));
        User user = new User(userInfo.getUsername(), "{noop}"+userInfo.getPassword(), userInfo.getStatus()==0 ? false:true,true,true,true,getAuthority(userInfo.getRoles()));
        return user;
    }
    //作用就是返回一个List集合,集合中转入的是角色描述
    public List<SimpleGrantedAuthority> getAuthority(List<Role> roles){
        List<SimpleGrantedAuthority> list = new ArrayList<>();
        for (Role role:roles){
            list.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));
        }
        return list;
    }
7.5.1.2 IUserDao

在Dao中添加IUserDao接口,添加findByUsername()方法,在查询中roles对象是一个多对多关系,创建IRoleDao接口类,添加findRoleByUserId()方法,根据用户id查询出所有对应的角色

public interface IUserDao {
    @Select("select * from users where username=#{username}")
    @Results({
            @Result(id=true, property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "email", column = "email"),
            @Result(property = "password", column = "password"),
            @Result(property = "phoneNum", column = "phoneNum"),
            @Result(property = "status", column = "status"),
            @Result(property = "roles", column = "id", javaType = java.util.List.class, many = @Many(select = "com.pjy.ssm.dao.IRoleDao.findRoleByUserId")),
    })
    public UserInfo findByUsername(String username) throws Exception;
}
7.5.2 用户退出

使用spring-security完成用户退出,非常简单
配置:

<security:logout invalidate-session="true" logout-url="/logout.do" logout-successurl="/login.jsp" />

页面中:

<a href="${pageContext.request.contextPath}/logout.do" class="btn btn-default btn-flat">注销</a>
7.5.3 用户查询

用户查询页面 user-list.jsp

在这里插入图片描述

1、添加user-list.jsp界面到pages中,设置表单提交的方式:

<c:forEach items="${userList}" var="user">
    <tr>
        <td><input name="ids" type="checkbox"></td>
        <td>${user.id }</td>
        <td>${user.username }</td>
        <td>${user.email }</td>
        <td>${user.phoneNum }</td>
        <td>${user.statusStr }</td>											
        <td class="text-center">
        <a href="${pageContext.request.contextPath}/user/findById.do?id=${user.id}" class="btn bg-olive btn-xs">详情</a>
        <a href="${pageContext.request.contextPath}/user/findUserByIdAndAllRole.do?id=${user.id}" class="btn bg-olive btn-xs">添加角色</a>
        </td>
    </tr>
</c:forEach>

在controller中创建UserController类,添加findAll()方法:

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    @RequestMapping("/findAll.do")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List<UserInfo> userList = userService.findAll();
        mv.addObject("userList",userList);
        mv.setViewName("user-list");
        return mv;
    }
}

2、Service

在IUserService中创建创建findAll()方法,并且在实现类中实现调用dao层中的findAll()方法:

@Override
public List<UserInfo> findAll() throws Exception {
	return userDao.findAll();
}

3、Dao

在IUserDao中创建findAll()方法:

@Select("select * from users")
List<UserInfo> findAll() throws Exception;

重启服务器:

在这里插入图片描述

7.5.4 用户添加

在这里插入图片描述
1、用户添加页面 user-add.jsp
在pages中添加user-add.jsp页面

在这里插入图片描述
2、UserController

在UserController中添加save()方法,保存用户信息:

//用户添加
@RequestMapping("save.do")
public String save(UserInfo userInfo) throws Exception {
    userService.save(userInfo);
    return "redirect:findAll.do";
}

3、Service

在UserServiceImpl实现类中,添加save()方法,在调用dao方法前,对password属性加密,采用SpringSecurity中的BCryptPasswordEncoder类进行MD5封装,生成一个随机“言”对象,保存在数据库中(注意是60个字节),在spring-security.xml中配置加密方式,修改UserServiceImpl实现类中"{noop}"+拼接。

/*
* 用户添加
* */
@Override
public void save(UserInfo userInfo) throws Exception{
    //对密码加密
        //在将加密后的数据封装到password中
    userInfo.setPassword(bCryptPasswordEncoder.encode(userInfo.getPassword()));
    userDao.save(userInfo);
}

前期我们的用户密码没有加密,现在添加用户时,我们需要对用户密码进行加密spring-security.xml

<!-- 切换成数据库中的用户名和密码 -->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userService">
            <!-- 配置加密的方式-->
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- 配置加密类 -->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

项目中在Utils工具类中对BCryptPasswordEncoder方法进行了重写:

public class BCryptPasswordEncoderUtils {

    //将springSecurity 中的BCryptPasswordEncoder提取出来单独使用
    private static BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();

    public static String encodePassword(String password){
        return bCryptPasswordEncoder.encode(password);
    }

    /*
    * 测试加密结果
    * */
    public static void main(String[] args) {
        String password = "123";
        String pwd = encodePassword(password);
        //通过 言 方式加密 每一次加密的key值都不一样
        //$2a$10$Y2m2ZMobCsXJp2q23I0okuwoG/AtuKx.FksVUNGsJl7bnDasj1y.C
        System.out.println(pwd);
    }
}

4、dao

在IUserDao中添加save()方法,注意数据库字段问题(password字段要大于60个字节)

//添加用户
@Select("insert into users(email,username,password,phoneNum,status) values(#{email},#{username},#{password},#{phoneNum},#{status})")
public void save(UserInfo userInfo) throws Exception;

重启服务器:
在这里插入图片描述

7.5.5 用户详情

在这里插入图片描述
1、用户详情页面user-show.jsp

在这里插入图片描述

2、Controller

在controller中UserController类下,添加findById()方法,通过id查询用户信息

//查询指定id的用户
@RequestMapping("/findById")
public ModelAndView findById(String id) throws Exception {
    ModelAndView mv = new ModelAndView();
    UserInfo userInfo = userService.findById(id);
    mv.addObject("user",userInfo);
    mv.setViewName("user-show1");
    return mv;
}

3、Service
调用UserServiceImpl实现类创建findById()方法,根据传递的id在dao层查询数据:

/*
* 根据Id查询用户所有信息
* */
@Override
public UserInfo findById(String id) throws Exception {
	return userDao.findById(id);
}

4、Dao

//根据Id查询用户信息
    @Select("select * from users where id=#{id}")
    @Results({
            @Result(id=true, property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "email", column = "email"),
            @Result(property = "password", column = "password"),
            @Result(property = "phoneNum", column = "phoneNum"),
            @Result(property = "status", column = "status"),
            @Result(property = "roles", column = "id", javaType = java.util.List.class, many = @Many(select = "com.pjy.ssm.dao.IRoleDao.findRoleByUserId")),
    })
    UserInfo findById(String id) throws Exception;

我们需要将用户的所有角色及权限查询出来所以需要调用IRoleDao中的findRoleByUserId,而在IRoleDao中需要调用IPermissionDao的findByRoleId

//根据用户id查询出所有对应的角色
@Select("select * from role where id in(select roleId from users_role where userId=#{userId})")
@Results({
    @Result(id = true, property = "id", column = "id"),
    @Result(property = "roleName", column = "roleName"),
    @Result(property = "roleDesc", column = "roleDesc"),
    @Result(property = "permissions", column = "id", javaType = java.util.List.class,many = @Many(select = "com.pjy.ssm.dao.IPermissionDao.findPermissionByRoleId"))

})
public List<Role> findRoleByUserId(String userId) throws Exception;

重启服务器,登录查询:

在这里插入图片描述

8.角色管理

8.1 角色查询

在这里插入图片描述

8.1.1 角色页面role-list.jsp

在这里插入图片描述

8.1.2 RoleControlller

创建RoleController,添加findAll()方法:

@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
    ModelAndView mv = new ModelAndView();
    List<Role> roleList = roleService.findAll();
    mv.addObject("roleList",roleList);
    mv.setViewName("role-list");
    return mv;
}
8.1.3 Service
/*
* 查询所有角色信息
* */
@Override
public List<Role> findAll() throws Exception {
	return roleDao.findAll();
}
8.1.4 Dao
/*
* 查询所有角色信息
* */
@Select("select * from role")
public List<Role> findAll() throws Exception;

重新打包,启动服务器:

在这里插入图片描述

8.2 角色添加

在这里插入图片描述

8.2.1.角色添加页面role-add.jsp

在这里插入图片描述

8.2.2 RoleControlller
@RequestMapping("/save.do")
public String save(Role role) throws Exception {
    roleService.save(role);
    //添加完成以后重定向到findAll.do
    return "redirect:findAll.do";
}
8.2.3 RoleService
/*
* 添加角色信息
* */
@Override
public void save(Role role) throws Exception {
	roleDao.save(role);
}
8.2.4 RoleDao
/*
* 新增角色信息
* */
@Insert("insert into role(roleName, roleDesc) values(#{roleName},#{roleDesc})")
public abstract void save(Role role) throws Exception;

重启服务器:

在这里插入图片描述

9.资源权限管理

9.1 资源权限查询

9.1.1 权限资源页面permission-list.jsp

在这里插入图片描述

9.1.2 PermissionController

创建PermissionController类,创建findAll()方法,调用service,查询:

@RequestMapping("/findAll.do")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List<Permission> permissionList = permissionService.findAll();
        mv.addObject("permissionList",permissionList);
        mv.setViewName("permission-list");
        return mv;
    }

9.1.3 Dao

调用dao中的

 /*
* 查询所有权限
* */
@Select("select * from permission")
public List<Permission> findAll() throws Exception;

重启服务器:

在这里插入图片描述

9.2 资源权限添加

9.2.1 权限资源添加页面permission-add.jsp

请在资料中查看页面详细代码

在这里插入图片描述

9.2.2 PermissionController
@RequestMapping("/save.do")
public String save(Permission permission) throws Exception{
	permissionService.save(permission);
	return "redirect:findAll.do";
}

9.2.3 Dao

/*
* 添加资源权限
* */
@Insert("insert into permission(permissionName, url) values(#{permissionName}, #{url})")
public abstract void save(Permission permission) throws Exception;

重启服务器:

在这里插入图片描述

10.权限关联与控制

10.1 用户角色关联

在这里插入图片描述
用户与角色之间是多对多关系,我们要建立它们之间的关系,只需要在中间表user_role插入数据即可。

10.1.1 用户角色关联相关页面
<a href="${pageContext.request.contextPath}/user/findUserByIdAndAllRole.do?id=${user.id}" class="btn bg-olive btn-xs">添加角色</a>

展示可以添加角色的页面user-roe-add.jsp

在这里插入图片描述

10.1.2 UserController

findUserByIdAndAllRole(Long id)方法

此方法用于查找要操作的用户及可以添加的角色,参数是要操作的用户id

/*
*   查询用户以及用户可以添加的角色
* */
@RequestMapping("/findUserByIdAndAllRole.do")
public ModelAndView findUserByIdAndAllRole(@RequestParam(name = "id", required = true) String userId) throws Exception {
    ModelAndView mv = new ModelAndView();
    //1.根据用户id查询用户
    UserInfo userInfo = userService.findById(userId);
    //2.根据用户Id查询可以添加的角色
    List<Role> otherRoles = userService.findOtherRoles(userId);
    mv.addObject("user",userInfo);
    mv.addObject("roleList",otherRoles);
    mv.setViewName("user-role-add");
    return mv;
}

调用IUserService的findById方法获取要操作的User

调用IRoleService的findOtherRole方法用于获取可以添加的角色信息

addRoleToUser(Long userId,Long[] ids)方法

方法用于在用户与角色之间建立关系,参数userId代表要操作的用户id,参数ids代表的是角色id数组

/*
* 给用户添加角色
* userId:你要添加用户的id
* ids:要添加的角色的id
* */
@RequestMapping("/addRoleToUser.do")
public String addRoleToUser(@RequestParam(name = "userId",required = true) String userId, @RequestParam(name = "ids",required = true) String[] roleIds) throws Exception {
    userService.addRoleToUser(userId, roleIds);
    return "redirect:findAll.do";
}
10.1.3 Dao

IRoleDao

/*
* 查询可以添加的角色信息
* 子查询:当前用户包含的角色信息
* 查询:不包含子查询的角色信息
* */
@Select("select * from role where id not in (select roleId from users_role where userId=#{id})")
List<Role> findOtherRoles(String userId) throws Exception;

IUserDao

/*
* 根据Id添加用户角色
* */
@Insert("insert into users_role(userId, roleId) values(#{userId}, #{roleId})")
void addRoleToUser(@Param("userId") String userId, @Param("roleId") String roleId) throws Exception;

重启服务器:

在这里插入图片描述

10.2 角色权限关联

角色与权限之间是多对多关系,我们要建立它们之间的关系,只需要在中间表role_permission插入数据即可。

10.2.1 角色权限关联相关页面

在role-list.jsp页面上添加链接

<a href="${pageContext.request.contextPath}/user/findUserByIdAndAllRole.do?id=${user.id}" class="btn bg-olive btn-xs">添加权限</a>

展示可以添加权限的页面roe-permission-add.jsp

在这里插入图片描述

10.2.2 RoleController

findRoleByIdAndAllPermission(Long roleId)方法

此方法用于查找要操作的角色及可以添加的权限,参数是要操作的角色id

/*
* 根据roleId查询role,并查询出可以添加的权限
* */
@RequestMapping("/findRoleByIdAndAllPermission.do")
public ModelAndView findRoleByIdAndAllPermission(@RequestParam(name = "id",required = true) String roleId) throws Exception {
    ModelAndView mv = new ModelAndView();
    //根据roleId查询role
    Role role = roleService.findById(roleId);
    //根据roleId查询可以添加的权限
    List<Permission> otherPermissions = roleService.findOtherPermissions(roleId);
    mv.addObject("role",role);
    mv.addObject("permissionList",otherPermissions);
    mv.setViewName("role-permission-add");
    return mv;
}

调用IRoleService的findById方法获取要操作的Role

调用IPermissionService的findOtherPermission方法用于获取可以添加的权限信息

addPermissionToRole(Long roleId,Long[] ids)方法

此方法用于在角色与权限之间建立关系,参数roleId代表要操作的角色id,参数permissionIds代表的是权限id数组

/*
* 给角色添加权限
* */
@RequestMapping("/addPermissionToRole.do")
public String addPermissionToRole(@RequestParam(name = "roleId",required = true) String roleId,@RequestParam(name = "ids",required = true) String[] permissionIds) throws Exception{
    roleService.addPermissionToRle(roleId, permissionIds);
    return "redirect:findAll.do";
}
10.2.3 service

在RoleServiceImpl中添加findOtherPermissions()方法实现,传入roleId,调用Dao方法

/*
* 查询当前用户没有的权限信息
* */
@Override
public List<Permission> findOtherPermissions(String roleId) throws Exception {
	return roleDao.findOtherPermissions(roleId);
}

在RoleServiceImpl中添加addPermissionToRle()方法,传入角色id,权限id遍历权限数组,将数组中的元素插入中间表中:

/*
* 为当前id下的用户添加权限
* */
@Override
public void addPermissionToRle(String roleId, String[] permissionIds) throws Exception {
    for (String permissionId : permissionIds) {
        roleDao.addPermissionToRole(roleId,permissionId);
    }
}
10.2.4 Dao

IPermissionDao

 /*
  * 查询当前用户没有关联的权限信息
  * */
@Select("select * from permission where id not in (select permissionId from role_permission where roleId=#{roleId})")
List<Permission> findOtherPermissions(String roleId) throws Exception;

用于查找可以添加的权限

IRoleDao

/*
* 在中间表role_permission中查询权限URL
* */
@Insert("insert into role_permission(roleId, permissionId) values(#{roleId},#{permissionId})")
void addPermissionToRole(@Param("roleId") String roleId, @Param("permissionId") String permissionId) throws Exception;

用于绑定角色与权限的关系

在这里插入图片描述

10.3 服务器端方法级权限控制

在服务器端我们可以通过Spring security提供的注解对方法来进行权限控制。Spring Security在方法的权限控制上 支持三种类型的注解,JSR-250注解、@Secured注解和支持表达式的注解,这三种注解默认都是没有启用的,需要 单独通过global-method-security元素的对应属性进行启用

10.3.1 开启注解使用

配置文件

<security:global-method-security jsr250-annotations="enabled"/>
<security:global-method-security secured-annotations="enabled"/>
<security:global-method-security pre-post-annotations="disabled"/> 

注解开启

@EnableGlobalMethodSecurity :Spring Security默认是禁用注解的,要想开启注解,需要在继承 WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,并在该类中将 AuthenticationManager定义为Bean。 
10.3.2 JSR-250注解

在这里插入图片描述

@RolesAllowed表示访问对应方法时所应该具有的角色

 示例:
 @RolesAllowed({"USER", "ADMIN"})  该方法只要具有"USER", "ADMIN"任意一种权限就可以访问。这里可以省 略前缀ROLE_,实际的权限可能是ROLE_ADMIN 

@PermitAll表示允许所有的角色进行访问,也就是说不进行权限控制

@DenyAll是和PermitAll相反的,表示无论什么角色都不能访问

在这里插入图片描述

10.3.3 支持表达式的注解

在这里插入图片描述

@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问

示例: 
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)") void changePassword(@P("userId") long userId ){  } 这里表示在changePassword方法执行之前,判断方法参数userId的值是否等于principal中保存的当前用户的 userId,或者当前用户是否具有ROLE_ADMIN权限,两种符合其一,就可以访问该方法。 

@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常

示例: 
@PostAuthorize User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");

@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果

@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值

10.3.4 @Secured注解

在这里插入图片描述
@Secured注解标注的方法进行权限控制的支持,其值默认为disabled。

示例:   
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")   
public Account readAccount(Long id);   
@Secured("ROLE_TELLER")

10.4 页面端标签控制权限

在jsp页面中我们可以使用spring security提供的权限标签来进行权限控制

10.4.1 导入

maven导入

<dependency>   
	<groupId>org.springframework.security</groupId>   
	<artifactId>spring-security-taglibs</artifactId>   
	<version>version</version> 
</dependency>

页面导入

<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%> 
10.4.2 常用标签

在jsp中我们可以使用以下三种标签,其中authentication代表的是当前认证对象,可以获取当前认证对象信息,例 如用户名。其它两个标签我们可以用于权限控制

10.4.2.1 authentication
<security:authentication property="" htmlEscape="" scope="" var=""/> 

property: 只允许指定Authentication所拥有的属性,可以进行属性的级联获取,如“principle.username”, 不允许直接通过方法进行调用

htmlEscape:表示是否需要将html进行转义。默认为true。

scope:与var属性一起使用,用于指定存放获取的结果的属性名的作用范围,默认我pageContext。Jsp中拥有的作用范围都进行进行指定

var: 用于指定一个属性名,这样当获取到了authentication的相关信息后会将其以var指定的属性名进行存 放,默认是存放在pageConext中

在这里插入图片描述

10.4.2.2 authorize

authorize是用来判断普通权限的,通过判断用户是否具有对应的权限而控制其所包含内容的显示

<security:authorize access="" method="" url="" var=""></security:authorize>

access: 需要使用表达式来判断权限,当表达式的返回结果为true时表示拥有对应的权限

<li id="system-setting">
    <security:authorize access="hasRole('ROLE_ADMIN')">
    <a href="${pageContext.request.contextPath}/user/findAll.do">
    <i class="fa fa-circle-o"></i> 用户管理
    </a>
    </security:authorize>
</li>

method:method属性是配合url属性一起使用的,表示用户应当具有指定url指定method访问的权限, method的默认值为GET,可选值为http请求的7种方法

url:url表示如果用户拥有访问指定url的权限即表示可以显示authorize标签包含的内容

var:用于指定将权限鉴定的结果存放在pageContext的哪个属性中

在这里插入图片描述
四火用户无法看到用户管理菜单。

10.4.2.3 accesscontrollist

accesscontrollist标签是用于鉴定ACL权限的。其一共定义了三个属性:hasPermission、domainObject和var, 其中前两个是必须指定的

<security:accesscontrollist hasPermission="" domainObject="" var=""></security:accesscontrollist> 

hasPermission:hasPermission属性用于指定以逗号分隔的权限列表

domainObject:domainObject用于指定对应的域对象

var:var则是用以将鉴定的结果以指定的属性名存入pageContext中,以供同一页面的其它地方使用

11.SSMAOP日志

11.1数据库与表结构

11.1.1 日志表信息描述sysLog
序号字段名字段类型字段描述
1idvarchar2(50)无意义,主键uuid
2visitTimetimestamp访问时间
3usernamevarchar2(500)操作者用户名
4ipvarchar(50)访问ip
5urlvarchar2(20)访问资源url
6executionTimeint执行时长
7methodvarchar(20)访问方法
11.1.2 sql语句
CREATE TABLE sysLog(    
    id VARCHAR2(32) default SYS_GUID() PRIMARY KEY,    
    visitTime timestamp,    
    username VARCHAR2(50),    
    ip VARCHAR2(30),    
    url VARCHAR2(50),    
    executionTime int,    
    method VARCHAR2(200) 
)

11.1.3 实体类
public class SysLog {
    private String id;
    private Date visitTime;
    private String visitTimeStr;
    private String username;
    private String ip;
    private String url;
    private Long executionTime;
    private String method;
}

11.2 基于AOP日志处理

在这里插入图片描述

11.2.1 页面syslog-list.jsp

在这里插入图片描述

详细内容请查看资源中页面信息

11.2.2 创建切面类处理日志

在这里插入图片描述

package com.pjy.ssm.controller;

import com.pjy.ssm.domain.SysLog;
import com.pjy.ssm.service.ISysLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/*
* 使用AOP中的切面配置日志
* */

@Component
@Aspect
public class LogAop {

    private Date visitTime;     //开始时间

    private Class clazz;        //访问的类

    private Method method;      //访问的方法

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private ISysLogService sysLogService;

    //前置通知——获取开始的时间,执行的类是哪一个,执行的方法是哪一个
    //拦截controller下的所有方法
    @Before("execution(* com.pjy.ssm.controller.*.*(..))")
    public void doBefore(JoinPoint jp) throws NoSuchMethodException {
        visitTime = new Date();     //当前时间就是开始访问的时间
        clazz = jp.getTarget().getClass();      //具体要访问的类
        String methodName = jp.getSignature().getName();    //获取访问的方法的名称

        Object[] args = jp.getArgs();//获取访问的方法的参数

        //获取具体执行的方法的Method对象
        if (args==null || args.length==0){
            method = clazz.getMethod(methodName);   //只能获取无参数的方法
        }else {
            Class[] classArgs = new Class[args.length];
            for (int i = 0; i < args.length; i++){
                classArgs[i] = args[i].getClass();
            }
            clazz.getMethod(methodName,classArgs);
        }
    }

    //后置通知
    @After("execution(* com.pjy.ssm.controller.*.*(..))")
    public void doAfter(JoinPoint jp) throws Exception {
        //获取访问的时常
        long time = new Date().getTime() - visitTime.getTime();
        String url = "";
        //获取url
        if (clazz != null && method != null && clazz != LogAop.class){
            //1.获取类上的@requestMapping("/orders")
             RequestMapping classAnnotation = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
             if (classAnnotation != null){
                 String[] classValue = classAnnotation.value();
                 //2.获取方法上的RequestMapping()值
                 RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                 if (methodAnnotation != null){
                     String[] methodValue = methodAnnotation.value();
                     url = classValue[0] + methodValue[0];

                     //获取访问的IP地址
                     String ip = request.getRemoteAddr();

                     //获取当前操作的用户
                     //从上下文中获取当前登录的用户
                     SecurityContext context = SecurityContextHolder.getContext();
                     User user = (User) context.getAuthentication().getPrincipal();
                     String username = user.getUsername();

                     //将日志相关信息封装到SYSLog对象
                     SysLog sysLog = new SysLog();
                     sysLog.setExecutionTime(time);
                     sysLog.setIp(ip);
                     sysLog.setMethod("[类名] "+clazz.getName()+"[方法名] "+method.getName());
                     sysLog.setUrl(url);
                     sysLog.setUsername(username);
                     sysLog.setVisitTime(visitTime);

                     //调用service完成日志的记录
                     sysLogService.save(sysLog);
                 }
             }
        }
    }
}

在切面类中我们需要获取登录用户的username,还需要获取ip地址,我们怎么处理?

​ username获取
​ SecurityContextHolder获取
​ ip地址获取
​ ip地址的获取我们可以通过request.getRemoteAddr()方法获取到。

在Spring中可以通过RequestContextListener来获取request或session对象。

11.2.3 SysLogController
package com.pjy.ssm.controller;

import com.pjy.ssm.domain.SysLog;
import com.pjy.ssm.service.ISysLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("/sysLog")
public class SysLogController {

    @Autowired
    private ISysLogService sysLogService;

    @RequestMapping("/findAll.do")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List<SysLog> sysLogList = sysLogService.findAll();
        mv.addObject("sysLogs",sysLogList);
        mv.setViewName("syslog-list");
        return mv;
    }
}
11.2.4 Service
package com.pjy.ssm.service.impl;

import com.pjy.ssm.dao.ISysLogDao;
import com.pjy.ssm.domain.SysLog;
import com.pjy.ssm.service.ISysLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class SysLogServiceImpl implements ISysLogService {

    @Autowired
    private ISysLogDao sysLogDao;

    @Override
    public void save(SysLog sysLog) throws Exception {
        sysLogDao.save(sysLog);
    }

    @Override
    public List<SysLog> findAll() throws Exception {
        return sysLogDao.findAll();
    }
}
11.2.5 Dao
package com.pjy.ssm.dao;

import com.pjy.ssm.domain.SysLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface ISysLogDao {

    /*
    * 将日志操作保存到数据库中
    * */
    @Insert("insert into syslog(visitTime,username,ip,url,executionTime,method) values(#{visitTime},#{username},#{ip},#{url},#{executionTime},#{method})")
    public void save(SysLog sysLog) throws Exception;


    /*
    * 查询所有日志记录
    * */
    @Select("select * from sysLog")
    List<SysLog> findAll throws Exception;
}

重启服务器:
在这里插入图片描述学习资料源于网络,个人学习梳理笔记,仅供参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值