使用IDEA基于Maven搭建SSM(Spring+SpringMVC+MyBatis)框架并完成登录注册案例(附源码)

一、环境准备

  1. 开发工具:IntelliJ IDEA 2017.3.1 x64
  2. 数据库:MySQL 5.5,其中MySQL的管理工具为Navicat
  3. JDK版本:JDK1.8
  4. Maven版本:apache-maven-3.2.2
  5. Tomcat版本:apache-tomcat-7.0.91
    当然了,以上只是我个人电脑上的开发环境,只要版本不过于老旧,应该是没有什么问题的。除此之外,有关于IDEA的基本操作以及JDK、Maven、Tomcat等的配置,这些也需要大家配置好(可以百度搜索一下)。本案例的完整代码会在文章末尾给出。

二、框架搭建

1.使用IDEA创建一个基本的Maven工程

(1)打开IDEA,点击Create New Project
在这里插入图片描述

(2)
在这里插入图片描述

(3)
在这里插入图片描述
(4)
在这里插入图片描述
(5)进去之后的界面如下
在这里插入图片描述
(6)由于IDEA创建maven的Java web项目时,main文件夹下没有java,resources目录等源文件夹,所以我们需要自己创建,具体过程可以查看这篇博客。这样一个基本的项目结构就创建好了。

2.在pom.xml中添加该项目所需要依赖jar包

<?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/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <packaging>war</packaging>

  <name>ssmtest</name>
  <groupId>com.test</groupId>
  <artifactId>ssmtest</artifactId>
  <version>1.0-SNAPSHOT</version>

  <build>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <version>6.1.7</version>
        <configuration>
          <connectors>
            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
              <port>8888</port>
              <maxIdleTime>30000</maxIdleTime>
            </connector>
          </connectors>
          <webAppSourceDirectory>${project.build.directory}/${pom.artifactId}-${pom.version}</webAppSourceDirectory>
          <contextPath>/</contextPath>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <!--dependency>
      <groupId>com.test</groupId>
      <artifactId>[the artifact id of the block to be mounted]</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency-->

    <!-- Maven中央仓库官网: https://mvnrepository.com/ -->
    <!-- 引入项目依赖的jar包 -->

    <!-- SpringMVC、Spring
      包含Spring MVC框架相关的所有类,包括框架的Servlets,Web MVC框架,控制器和视图支持
      外部依赖spring-web, (spring-support,Tiles,iText,POI)
   -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.3.7.RELEASE</version>
    </dependency>

    <!-- Spring-JDBC
      包含对Spring对JDBC数据访问进行封装的所有类。外部依赖spring-beans,spring-dao
   -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.3.7.RELEASE</version>
    </dependency>

    <!-- Spring面向切面编程
      提供对AspectJ的支持,以便可以方便的将面向方面的功能集成进IDE中,比如Eclipse AJDT
   -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>4.3.7.RELEASE</version>
    </dependency>

    <!--MyBatis包 -->
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.2</version>
    </dependency>

    <!-- MyBatis整合Spring的适配包 -->
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!-- 数据库连接池、驱动 -->
    <!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.41</version>
    </dependency>

    <!-- JSP:标准标签库
      引入jstl/jar保证jsp页面可以使用EL表达式 -->
    <!-- https://mvnrepository.com/artifact/jstl/jstl -->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <!-- provided表明该包只在编译和测试的时候用,
          当启动tomcat(在其中有servlet-api包)的时候,就不会冲突了-->
      <scope>provided</scope>
    </dependency>

    <!-- 单元测试junit包 -->
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

    <!--与json有关的包-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.6</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.6</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>
    
  </dependencies>

</project>

添加完成之后,刷新该工程
在这里插入图片描述

这样便可以看见已经添加成功的jar包
在这里插入图片描述

3.配置web.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  <!--
      1.将Spring框架集成到项目中
      1).Spring环境构建时读取web应用的初始化参数contextConfigLocation,
            从classpath中读取配置文件spring/spring-*.xml
      2).Spring的初始化:ContextLoaderListener的作用是启动Web容器时, 自动装配
        spring-*.xml的配置信息(因为它实现了ServletContextListener
          这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。)
   -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:spring/spring-*.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>


  <!--
      2.集成SpringMVC框架
      1)SpringMVC环境构建时需要读取servlet初始化参数init-param,
           从classpath中读取配置文件spring/springmvc-context.xml
      2)load-on-startup:当值为0或者大于0时,表示容器在应用启动时就加载这个servlet,
           当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
          正数的值越小,启动该servlet的优先级越高。
   -->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/springmvc-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>   <!-- 拦截所有请求 -->
  </servlet-mapping>


  <!--
      3.使用spring提供的过滤器CharacterEncodingFilter解决乱码问题
        乱码问题有get方式提交和post方式提交2种情况
        1)解决get方式:在Tomcat的配置文件server.xml中的<Connector  connectionTimeout="20000"
          port="8080" protocol="HTTP/1.1" redirectPort="8443"/>添加URIEncoding="UTF-8"即可
        2)解决post方式:如下所示,增加过滤器
  -->
  <filter>
    <filter-name>encoding</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>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encoding</filter-name>
    <servlet-name>springmvc</servlet-name>
  </filter-mapping>

</web-app>

4.在resource目录下分别创建spring和mybatis目录,并且添加相应的配置文件
在这里插入图片描述
(1)spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--
        1.base-package:指定Spring IOC容器扫描的包
        2.context:exclude-filter:指定排除哪些表达式的组件,此处排除了Controller注解
          原因:Controller注解的的作用是声明控制器(处理器)类。从数据流转的角度,
                    这个类应该是由SpringMVC框架进行管理和组织的,所以不需要由Spring框架扫描。
     -->
    <context:component-scan base-package="com.test.*" >
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 3.加载数据库的属性文件并配置数据库连接池(c3p0) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssmtest?rewriteBatchedStatements=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="user" value="root"/>
        <property name="password" value="15272595644"/>
    </bean>

    <!-- 4.集成Mybatis框架 ,通过spring配置Mybatis的核心对象SqlSessionFactory以及完成其他配置-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
        <property name="configLocation" value="classpath:mybatis/config.xml" />
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" >
            <list>
                <value>classpath*:mybatis/mapper-*.xml</value>
            </list>
        </property>
    </bean>
    <!--
         在spring与mybatis整合时,需要对每一个mapper定义对应的一个MapperFactoryBean,
         可以使用MapperScannerConfigurer自动扫描mapper,然后自动注册对应的MapperFactoryBean对象
    -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer" >
        <property name="basePackage" value="com.test.dao" />
    </bean>


    <!-- 5.Spring框架采用声明式事务,通过AOP的方式将事务增加到业务中 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--控制住数据源  -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager" >
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.Exception" />
            <tx:method name="query*" read-only="true" />
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:advisor advice-ref="transactionAdvice" pointcut="execution(* com..*Service.*(..))"/>
    </aop:config>


</beans>

(2)springmvc-context.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:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!--
        1.context:include-filter:表示要包含的目标类,
             此处要扫描Controller,与spring-context.xml中的相反
        2.use-default-filters:当其值为false时,表示不使用默认的 Filter进行扫描,
             默认的扫描是全部都扫描
     -->
    <context:component-scan base-package="com.*" use-default-filters="false" >
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--
       3.配置<mvc:default-servlet-handler/>的作用:
            会在Spring MVC上下文中定义一个DefaultServletHttpRequestHandler,
           它像一个检查员,对进入DispatcherServlet的URL进行筛查,
           如果发现是静态资源的请求, 就将该请求转由Web应用服务器默认的Servlet处理,
           如果不是静态资源的请求, 才由DispatcherServlet继续处理。
    -->
    <mvc:default-servlet-handler/>
    <!--4.解决@Controller标识的类的bean的注入和使用-->
    <mvc:annotation-driven/>

    <!-- 5.配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

(3)config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>

    </typeAliases>
</configuration>

(4)mapper-user.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.dao.UserDao" >

</mapper>

5.在java目录下创建一些基本的包
在这里插入图片描述

包名作用
bean存放一些实体类,有些一与数据库的表相对应,例如,用户类等等
controller控制层
dao数据传输层,与数据库进行交数据交互
service编写 业务逻辑的接口
serviceImpl实现业务逻辑

当然了,每个人给包的命名或者结构安排可能不一样,我所给出的只是一种参考。

三、分析需求创建表并连接数据库

1.由于本文的案例比较简单,只有用户的登录和注册功能,所以在名为ssmtest的数据库(自己在MySQL创建)中只建立了一张t_user表,如下图所示。
在这里插入图片描述
2.在IDEA中连接到该名为ssmtest的数据库
在这里插入图片描述在这里插入图片描述
这样便连接成功了!与此同时在User.java中添加相应的属性
User.java

package com.test.bean;

public class User {

    private String username;
    private String password;
    //用于判断是否记住用户名
    private String flag;

    public String getFlag() {
        return flag;
    }

    public void setFlag(String flag) {
        this.flag = flag;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

四、添加前端页面

具体目录结构如下图所示
在这里插入图片描述
1.login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.test.utils.*" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="./css/login.css">

</head>

<%
    //完成用户名的回显
    String username="";
    //获得从客户端携带过来的所有Cookie
    Cookie[] cookies=request.getCookies();
    //从Cookie的数组中查找指定名称的Cookie
    Cookie cookie = CookieUtils.findCookie(cookies, "username");
    if(cookie!=null)
    {
        username=cookie.getValue();
    }
%>


<body>
    <div class="login">
        <div class="header">
            <h1>
                <a href="./login">登录</a> <a href="./register">注册</a>
            </h1>
        </div>

        <form action="" method="post">
            <table>
                <tr>
                    <td class="td1">用户名</td>
                    <td><input type="text" class="input1" id="username" name="username" value="<%= username %>" placeholder="请输入用户名"></td>
                </tr>
                <tr>
                    <td class="td1">密码</td>
                    <td><input type="password" class="input1" id="password" name="password" placeholder="请输入密码"></td>
                </tr>
                <tr>
                    <td class="td1" colspan="2">
                        <input type="checkbox" name="rememberUsername" value="true" checked="checked"> 记住用户名</td>
                </tr>
                <tr>
                    <td colspan="2">
                        <div class="btn-red">
                            <input type="button" value="登录" id="login-btn" onclick="doLogin()">
                        </div>
                    </td>
                </tr>
            </table>
        </form>
    </div>

    <script src="jquery/jquery-2.1.1.min.js"></script>
    <script src="layer/layer.js"></script>


    <script>
        //1.登录单击事件
        function doLogin(){
            //账号和密码的非空校验(使用jQuery做的id选择器)
            var username = $("#username").val();
            var password = $("#password").val();

            //表单元素的value取值不会为null,当为空时是空字符串
            if(username == "" || password==""){
                //alert("用户登录账号或密码为空,请输入");
                /*为了使弹出框的样式保持一致,不受浏览器的影响,可以使用组件layer
                * layer.msg(arg1,arg2,arg3)
                * arg1:提示信息
                * arg2: time为弹出框持续的时间,单位为ms
                *       icon为弹出框呈现的样式
                *       shift为弹出框出现的样式
                * arg3: 回调方法
                * 具体细节可以参考layer文档
                * */
                layer.msg("用户登录账号或密码为空,请输入", {time:2000, icon:5, shift:6}, function(){});
                return;
            }

            var rememberUsername = document.getElementsByName("rememberUsername");
            var flag = rememberUsername[0].checked;

            //提交表单
            var loadingIndex=null;
            $.ajax({
                type:"POST",
                url:"doAjaxLogin",
                data:{"username":username,"password":password,"flag":flag},
                beforeSend:function(){
                    loadingIndex = layer.msg('登录中', {icon: 16});
                },
                success:function(result){
                    layer.close(loadingIndex);
                    if(result.success){
                        //后台验证用户的信息成功之后,跳转到index.jsp页面
                        window.location.href = "index";
                    }else{
                        layer.msg("用户的登录账号或密码不正确,请重新输入!", {time:2000, icon:2, shift:6}, function(){});
                    }
                }
            });
        }
    </script>
</body>
</html>


2.register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
    <link rel="stylesheet" href="./css/reg.css">

</head>
<body>
    <div class="reg">
        <div class="header">
            <h1>
                <a href="./login">登录</a> <a href="./register">注册</a>
            </h1>
        </div>
        <form action="" method="post" id="userForm">
            <table>
                <tr>
                    <td class="td1">用户名</td>
                    <td><input type="text" class="input1" name="username" id="username" placeholder="请输入用户名"></td>
                </tr>
                <tr>
                    <td class="td1">密码</td>
                    <td><input type="password" class="input1" name="password" id="password" placeholder="请输入密码"></td>
                </tr>
                <tr>
                    <td colspan="2">
                        <div class="btn-red">
                            <input type="button" value="注册" id="reg-btn" onclick="doRegister()">
                        </div>
                    </td>
                </tr>
            </table>
        </form>
    </div>

    <script src="jquery/jquery-2.1.1.min.js"></script>
    <script src="layer/layer.js"></script>

    <script>
        //1.登录单击事件
        function doRegister(){
            //账号和密码的非空校验(使用jQuery做的id选择器)
            var username = $("#username").val();
            var password = $("#password").val();
            var pwdReg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$/;

            //表单元素的value取值不会为null,当为空时是空字符串
            if(username == "" || password == ""){

                /*为了使弹出框的样式保持一致,不受浏览器的影响,可以使用组件layer
                * layer.msg(arg1,arg2,arg3)
                * arg1:提示信息
                * arg2: time为弹出框持续的时间,单位为ms
                *       icon为弹出框呈现的样式
                *       shift为弹出框出现的样式
                * arg3: 回调方法
                * 具体细节可以参考layer文档
                * */
                layer.msg("用户注册账号或密码为空,请输入", {time:2000, icon:5, shift:6}, function(){});
                return;
            }

            //验证密码是否符合要求
            if(!pwdReg.test(password)){
                layer.msg("密码必须由数字和字母组成,且长度在8-16位之间,请重新输入", {time:2000, icon:5, shift:6}, function(){});
                $("#password").reset();
                return;
            }

            //提交表单
            var loadingIndex=null;
            $.ajax({
                type:"POST",
                url:"doAjaxRegister",
                data:{"username":username,"password":password},
                beforeSend:function(){
                    loadingIndex = layer.msg('注册中', {icon: 16});
                },
                success:function(result){
                    layer.close(loadingIndex);
                    if(result.success){
                        layer.msg("注册成功!", {time:2000, icon:2, shift:6}, function(){});
                        //后台验证用户注册成功之后,跳转到login.jsp页面
                        window.location.href = "login";
                    }else{
                        layer.msg("该用户名已经被注册,请重新填写!", {time:2000, icon:2, shift:6}, function(){});
                        //清除已经填写的用户信息
                        $("#userForm")[0].reset();
                    }
                }
            });
        }

    </script>

</body>
</html>

五、完成前端与后台的交互

后台的目录结构如下所示

在这里插入图片描述
AjaxResult.java

package com.test.bean;

public class AjaxResult {

    //用于后台的逻辑判断
    private boolean success;
    //后台向前端返回的对象
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

}

1.登录与注册功能
(1)控制器类 DispatcherController.java,用于转发各种请求

package com.test.controller;

import com.test.bean.AjaxResult;
import com.test.bean.User;
import com.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Controller
public class DispatcherController {

    @Autowired
    private UserService userService;

    //1.跳转到登录页面
    @RequestMapping("login")
    public String login(){
        return "login";
    }

    //2.跳转到注册页面
    @RequestMapping("register")
    public String register(){
        return "register";
    }

    //2.登录成功后跳转到index.jsp页面
    @RequestMapping("index")
    public String index(){
        return "index";
    }

    //3.用Ajax完成登录操作,但不跳转页面
    @ResponseBody
    @RequestMapping("doAjaxLogin")
    public Object doAjaxLogin(User user, HttpSession session, HttpServletResponse response){

        //创建一个AjaxResult对象,用于保存各种
        AjaxResult ajaxResult = new AjaxResult();

        User dbUser = userService.queryForLogin(user);

        //登录成功
        if(dbUser != null){
            //将用户放入session中
            session.setAttribute("loginUser",dbUser.getUsername());

            //获取是否记住用户名单选框的状态
            if("true".equals(user.getFlag())){
                // 完成记住用户名的功能:
                Cookie cookie = new Cookie("username",user.getUsername());
                //设置有效路径:
                cookie.setPath("/ssmtest");
                // 设置有效时间:
                cookie.setMaxAge(60*60*24);// 保存24小时
                // 将cookie回写到浏览器:
                response.addCookie(cookie);
            }

            ajaxResult.setSuccess(true);
        }else{
            ajaxResult.setSuccess(false);
        }

        return ajaxResult;
    }

    //3.用Ajax完成注册操作,但不跳转页面
    @ResponseBody
    @RequestMapping("doAjaxRegister")
    public Object doAjaxRegister(User user){

        //创建一个AjaxResult对象,用于保存各种
        AjaxResult ajaxResult = new AjaxResult();

        User dbUser = userService.queryForLogin(user);

        if(dbUser != null){
            //该用户已经被注册
            ajaxResult.setSuccess(false);
        }else{
            try {
                userService.insertUser(user);

                ajaxResult.setSuccess(true);
            }catch (Exception e){
                e.printStackTrace();
                ajaxResult.setSuccess(false);
            }
        }
        return ajaxResult;
    }
}

(2).业务逻辑层(接口) UserService

package com.test.service;

import com.test.bean.User;

public interface UserService {

    //查询登录用户
    User queryForLogin(User user);

    //注册用户
    void insertUser(User user);
}

(3).业务逻辑实现 UserServiceImpl

package com.test.serviceImpl;

import com.test.bean.User;
import com.test.dao.UserDao;
import com.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public User queryForLogin(User user) {
        return userDao.queryForLogin(user);
    }

    @Override
    public void insertUser(User user) {
        userDao.insertUser(user);
    }
}

(4)数据访问层

package com.test.dao;

import com.test.bean.User;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

@Repository
public interface UserDao {

    //由于该查询语句较为简单,所以可以直接在@Select注解中写SQL语句
    @Select("select * from t_user where username = #{username} and password = #{password}")
    User queryForLogin(User user);
      
   //sql语句在mapper-user.xml文件中编写
    void insertUser(User user);
}

此时应该在mapper-user.xml文件中添加insertUser方法对应的SQL语句
mapper-user.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.dao.UserDao" >

    <!--用户注册-->
    <insert id="insertUser">
        insert INTO t_user (username, password)
        values (#{username},#{password})
    </insert>
    
</mapper>

此时进行测试
在这里插入图片描述
在这里插入图片描述
不出问题的话,注册应该也是没有什么问题的。

2.使用拦截器阻止未登陆的用户访问index.jsp页面
可能有人会想到如果直接在浏览器中输入http://localhost:8080/ssmtest/index,那么能不能直接访问到index.jsp页面?如果在用户访问该页面之前不进行登陆信息验证的话,那么是可以直接访问到的,这显然是不安全的。所以这里可以使用SpringMVC的组件拦截器(具体相关内容可以参考我之前整理的浅谈SpringMVC中的拦截器控件
(1)添加拦截器类LoginInteceptor.java

package com.test.web;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginInteceptor implements HandlerInterceptor{

    /*在拦截器拦截之前判断是否之后的操作
     * @return false:不再继续执行后面的操作
     *         true:继续执行后面的操作
     * */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //判断当前的用户是否已经登录
        HttpSession session = httpServletRequest.getSession();
        String loginUser = (String)session.getAttribute("loginUser");

        //用户未登陆,跳转到登录页面
        if(loginUser == null){
            String path = session.getServletContext().getContextPath();
            httpServletResponse.sendRedirect(path + "/login");
            return false;
        }else{
            return true;
        }

    }

    //在拦截器拦截完毕之后进行的操作
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    //在完成视图渲染之后进行的操作
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

(2)在springmvc-context.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:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!--
        1.context:include-filter:表示要包含的目标类,
             此处要扫描Controller,与spring-context.xml中的相反
        2.use-default-filters:当其值为false时,表示不使用默认的 Filter进行扫描,
             默认的扫描是全部都扫描
     -->
    <context:component-scan base-package="com.*" use-default-filters="false" >
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>


    <!--
       3.配置<mvc:default-servlet-handler/>的作用:
            会在Spring MVC上下文中定义一个DefaultServletHttpRequestHandler,
           它像一个检查员,对进入DispatcherServlet的URL进行筛查,
           如果发现是静态资源的请求, 就将该请求转由Web应用服务器默认的Servlet处理,
           如果不是静态资源的请求, 才由DispatcherServlet继续处理。
    -->
    <mvc:default-servlet-handler/>
    <!--4.解决@Controller标识的类的bean的注入和使用-->
    <mvc:annotation-driven/>


    <!-- 5.配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--6.配置拦截器-->
    <mvc:interceptors>
        <!--配置登录拦截器-->
        <mvc:interceptor>
            <!--拦截所有的请求-->
            <mvc:mapping path="/**"/>
            <!--排除要拦截的请求-->
            <mvc:exclude-mapping path="/login"/>
            <mvc:exclude-mapping path="/doAjaxLogin"/>
            <mvc:exclude-mapping path="/register"/>
            <mvc:exclude-mapping path="/doAjaxRegister"/>
            <mvc:exclude-mapping path="/login"/>
            <mvc:exclude-mapping path="/css/**"/>
            <mvc:exclude-mapping path="/jquery/**"/>
            <mvc:exclude-mapping path="/layer/**"/>
            <bean class="com.test.web.LoginInteceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

3.使用Cookie完成记住用户名的功能
在CookieUtils .java中创建findCookie方法,便于寻找用户名

package com.test.utils;

import javax.servlet.http.Cookie;

//在浏览器中回显用户名
public class CookieUtils {

    public static Cookie findCookie(Cookie[] cookies , String name){
        if(cookies == null){
            // 说明客户端没有携带Cookie:
            return null;
        }else{
            // 说明客户端携带Cookie:
            for (Cookie cookie : cookies) {
                if(name.equals(cookie.getName())){
                    return cookie;
                }
            }
            return null;
        }
    }
}

其余相关的操作在前面的代码已经给出

4.防止用户多处登录
(1).创建一个用户缓存类LoginCache.java,用于在session中保存用户的相关信息,以便于之后的验证

package com.test.utils;

import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

public class LoginCache {

    //使用单例模式
    private LoginCache(){}

    private static LoginCache instance = new LoginCache();

    public static LoginCache getInstance(){
        return instance;
    }

    // key值:登录用户登录名,value值:登录用户sessionId
    private Map<String,String> loginUserSession = new HashMap<String,String>();
    //key值:登录用户sessionId,value值:登录用户session对象
    private Map<String,HttpSession> loginSession = new HashMap<String,HttpSession>();

    /**
     * 通过登录名获取对应登录用户的sessionId
     * @param username
     * @return
     */
    public String getSessionIdByUsername(String username){
        return loginUserSession.get(username);
    }

    /**
     * 通过sessionId获取对应的session对象
     * @param sessionId
     * @return
     */
    public HttpSession getSessionBySessionId(String sessionId){
        return loginSession.get(sessionId);
    }

    /**
     * 存储登录名与对应的登录sessionID至缓存对象
     * @param username
     * @param sessionId
     */
    public void setSessionIdByUserName(String username,String sessionId){
        loginUserSession.put(username, sessionId);
    }

    /**
     * 存储sessionId与对应的session对象至缓存对象
     * @param sessionId
     * @param session
     */
    public void setSessionBySessionId(String sessionId,HttpSession session){
        loginSession.put(sessionId, session);
    }

}

(2)创建LoginSessionListener类,用户监听用户属性在session中的变化

package com.test.listener;

import com.test.utils.LoginCache;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class LoginSessionListener implements HttpSessionAttributeListener{

    private static final String LOGINUSER="loginUser";

    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        //获得session中添加的属性值的名称
        String attrName = httpSessionBindingEvent.getName();

        if(LOGINUSER.equals(attrName)){
            //获取添加的属性值,即用户登录名
            String attrValue = (String)httpSessionBindingEvent.getValue();
            //该次操作的session对象
            HttpSession session1 = httpSessionBindingEvent.getSession();
            //该次操作的session对象ID
            String sessionId = session1.getId();
            //从缓存对象里面,获得该用户登录名对应的sessionID值
            String sessionId2 = LoginCache.getInstance().getSessionIdByUsername(attrValue);
            //未获得结果,不需要清理前次登录用户会话信息
            if(null == sessionId2){

            }else{
                HttpSession session2 = LoginCache.getInstance().getSessionBySessionId(sessionId2);//获取前次该用户登录对应的session对象
                //清理前次登录用户会话存储信息,使得前次登录失效
                session2.invalidate();
            }

            //完成该次登录用户登录名、sessionID,session对象的缓存对象存储
            LoginCache.getInstance().setSessionIdByUserName(attrValue, sessionId);
            LoginCache.getInstance().setSessionBySessionId(sessionId, session1);
        }
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
}

(3)在web.xml文件中注册该监听器

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  <!--
      1.将Spring框架集成到项目中
      1).Spring环境构建时读取web应用的初始化参数contextConfigLocation,
            从classpath中读取配置文件spring/spring-*.xml
      2).Spring的初始化:ContextLoaderListener的作用是启动Web容器时, 自动装配
        spring-*.xml的配置信息(因为它实现了ServletContextListener
          这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。)
   -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:spring/spring-*.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>


  <!--
      2.集成SpringMVC框架
      1)SpringMVC环境构建时需要读取servlet初始化参数init-param,
           从classpath中读取配置文件spring/springmvc-context.xml
      2)load-on-startup:当值为0或者大于0时,表示容器在应用启动时就加载这个servlet,
           当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
          正数的值越小,启动该servlet的优先级越高。
   -->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/springmvc-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>   <!-- 拦截所有请求 -->
  </servlet-mapping>


  <!--
      3.使用spring提供的过滤器CharacterEncodingFilter解决乱码问题
        乱码问题有get方式提交和post方式提交2种情况
        1)解决get方式:在Tomcat的配置文件server.xml中的<Connector  connectionTimeout="20000"
          port="8080" protocol="HTTP/1.1" redirectPort="8443"/>添加URIEncoding="UTF-8"即可
        2)解决post方式:如下所示,增加过滤器
  -->
  <filter>
    <filter-name>encoding</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>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encoding</filter-name>
    <servlet-name>springmvc</servlet-name>
  </filter-mapping>

  <!--4.注册监听器-->
  <listener>
    <listener-class>com.test.listener.LoginSessionListener</listener-class>
  </listener>

</web-app>
        

这样一来,若同一个用户在两处登录同一个账号时,那么前一个登录的用户信息会被清除,并且由拦截器拦截到用户信息在session中为空之后,直接跳转到login.jsp页面,这样便实现了同一个用户不能多处登录。
好啦!到这里为止,一个基于SSM框架的登录注册案例便完成了。一些必要的注释我已经在代码中给出,由于文字不能完全地还原整个开发过程,所以给出的代码顺序会存在一些前后台不同步的情况,不过给出的代码应该是完整的。如果大家发现了错误可以直接提出来哦~~
该案例源码:ssmtest
提取码:86rm

  • 19
    点赞
  • 154
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值