通过 Spring MVC 拦截器(Interceptor)来实现一个用户登录权限验证的案例。只有登录后的用户才能访问系统主页,如果没有登录就直接访问主页,则拦截器会将请求拦截并跳转到登录页面,同时在登录页面中给出提示信息。若用户登陆时,用户名或密码错误,则登录页也会显示相应的提示信息。已登录的用户在系统主页点击“退出登录”时,跳转会登录页面,流程图如下。

Spring MVC拦截器实现用户登录权限验证_xml

一、 新建一个名为“ssm02”的 Web 工程模块,并将与 Spring MVC 相关的依赖包引入到工程中。

Spring MVC拦截器实现用户登录权限验证_spring_02

 

Spring MVC拦截器实现用户登录权限验证_mvc_03

导入依赖

<dependencies>
<!-- spring核心容器包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>

<!-- spring切面 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.18</version>
</dependency>

<!--aop联盟包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>

<!-- 德鲁伊连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>

<!--MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>

<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.20</version>
</dependency>

<!-- MySQL事务包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.18</version>
</dependency>

<!-- spring-orm映射依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.18</version>
</dependency>

<!-- Apache Commons日志包 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

<!-- log4j2 日志 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.1</version>
<scope>test</scope>
</dependency>

<!-- lombok包 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>

<!-- spring test测试支持包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.18</version>
<scope>test</scope>
</dependency>

<!-- junit5单元测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>

<!-- springMVC支持包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>

<!-- JSON支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>

<!-- mybatis核心 jar包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<!-- mybatis-spring整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>

<!--jsp 和Servlet 可选-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring5 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.1.0.M2</version>
</dependency>

</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>

二、更改web.xml 中文件内容,配置 Spring MVC 的 DispatcherServlet、请求和响应编码过滤器等信息,配置内容如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--spring核心配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--
ContextLoaderListener监听器
(1)ContextLoaderListener监听器的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。
(2)在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--请求和响应的字符串过滤器-->
<!--请求和响应的字符串过滤器-->
<!--请求和响应的字符串过滤器-->
<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>
<!--设置响应的编码,这里我们可以省略-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置springMvc前端控制器,对浏览器发送的请求统一处理-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置DispatcherServlet的一个初始化参数:spring mvc配置文件的位置和名称-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--设置springmvc的核心控制器所能处理的请求路径 /所匹配的请求可以是 /login 或者 .html 或者.js或者css请求路径
但是/不能匹配.jsp请求的路径的请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

三、补充项目结构,准备好MVC模式下的主要目录

注意:要通过 Mark Directory as 将补充的目录进行设置,选择的参数说明:

  • Sources Root:告诉idea这个文件夹及其子文件夹中包含源代码,是需要编译构建的一部分
  • Test Sources Root:测试源文件夹允许您将与测试相关的代码与生产代码分开。通常,源和测试源的编译结果被放置在不同的文件夹中。
  • Resources Root:用于应用程序中的资源文件(图像、各种配置XML和属性文件等)。
  • 在构建过程中,资源文件夹的所有内容都复制到输出文件夹中,如下所示。
  • 类似于源,您可以指定生成资源。您还可以指定输出文件夹中的文件夹,您的资源应该复制到
  • Test Resources Root:测试的资源文件
  • Exclued:不包括、排除

Spring MVC拦截器实现用户登录权限验证_spring_04

四、在resource目录下准备配置文件

4.1.创建 log4j2.xml 内容如下

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>

4.2.创建 jdbc.properties 内容如下

jdbc_driver=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=test
jdbc_password=123456

4.3.创建 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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!--加载外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

<!--扫描service层-->
<context:component-scan base-package="com.augus.service"></context:component-scan>

<!--配置德鲁伊连接池-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc_username}"></property>
<property name="password" value="${jdbc_password}"></property>
<property name="url" value="${jdbc_url}"></property>
<property name="driverClassName" value="${jdbc_driver}"></property>
</bean>

<!--
SqlSessionFactoryBean能在Spring IoC容器中以SqlSessionFactory的类型保存并被获取。就是继承了FactoryBean这个接口了,这是个支持泛型的接口:
当实现了这个接口的Bean在配置为被Spring接管时,存入IoC容器中的实例类型将会是实例化泛型的那个类型,从IoC容器中获取时也是实例化泛型的那个类型,
这种情况下,Spring 将会在应用启动时为你创建SqlSessionFactory对象,然后将它以 SqlSessionFactory为名来存储。当把这个bean注入到Spring中去了以后,IoC容器中的其他类型就可以拿到SqlSession实例了,就可以进行相关的SQL执行任务了。
-->
<bean id="sessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--获取上面的数据源-->
<property name="dataSource" ref="druidDataSource"></property>
<property name="typeAliasesPackage" value="com.augus.pojo"></property>
</bean>

<!--
配置MapperScanner 扫描mapper.xml接口
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--配置SQLSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sessionFactoryBean"></property>
<!--配置mapper扫描-->
<property name="basePackage" value="com.augus.mapper"></property>
</bean>

<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"></property>
</bean>

<!--开启事务注解-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>
</beans>

4.4.创建 springmvc.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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.augus.controller"></context:component-scan>
<!-- 配置 Thymeleaf 视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>

<!--当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签-->
<mvc:annotation-driven></mvc:annotation-driven>

<!--将静态资源交给Tomcat默认的servlet处理-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>

<!--配置拦截器-->
<mvc:interceptors>
<!--拦截器-->
<mvc:interceptor>
<!--配置拦截器拦截的请求路径-->
<mvc:mapping path="/**"/>
<!--定义在这里,表示拦截器只对指定路径的请求进行拦截-->
<bean id="interceptor" class="com.augus.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>

五、在java下创建包com.augusu,在分别创建四个包

Spring MVC拦截器实现用户登录权限验证_xml_05

5.1.新建pojo包,在下面创建User实体类

  • 在数据库中创建user表

Spring MVC拦截器实现用户登录权限验证_mvc_06

  • SQL如下:
CREATE TABLE `user` (
`uid` int(255) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uid`)
)
  • 实体类:User
package com.augus.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class User implements Serializable {
private Integer uid;
private String username;
private String password;
}

5.2.新建mapper包

在下面创建UserMapper接口,代码如下:

package com.augus.mapper;

import com.augus.pojo.User;
import org.apache.ibatis.annotations.Param;

public interface UserMapper {
User findByUserName(@Param("name") String username);
}

在下面创建UserMapper.xml,从表中查询数据

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.augus.mapper.UserMapper">
<!--User findUser(String uname, String password);-->
<select id="findByUserName" resultType="user">
select * from user where username = #{name}
</select>
</mapper>

5.3.新建service包

创建接口Uservice

package com.augus.service;

import com.augus.pojo.User;

public interface UserService {
User findByUserName(String uname);
}

创建impl包,创建UserviceImpl实现类

package com.augus.service.impl;

import com.augus.mapper.UserMapper;
import com.augus.pojo.User;
import com.augus.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;

//根据用户名查询用户信息
@Override
public User findByUserName(String uname) {
return userMapper.findByUserName(uname);
}
}

5.4.新建interceptor包

创建拦截器 LoginInterceptor,代码如下:

package com.augus.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

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

public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行前
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/*设置请求和响应的乱码 */
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");

// 获取请求的URL
/*
* indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
* 如果没有找到匹配的字符串则返回 -1。*/
String url = request.getRequestURI();
// login.jsp或登录请求放行,不拦截
if (url.indexOf("/toLogin") >= 0 || url.indexOf("/login") >= 0) {
return true;
}
// 获取 session
HttpSession session = request.getSession();
Object obj = session.getAttribute("loginUser");

//不等于null表示是已经登录了
if (obj != null)
return true;

// 没有登录且不是登录页面,转发到登录页面,并给出提示错误信息
request.setAttribute("msg", "还没登录,请先登录!");

/*request.getRequestDispatcher("/toLogin").forward(request, response);
* 1、属于转发,也是服务器跳转,相当于方法调用,在执行当前文件的过程中转向执行目标文件,两个文件(当前文件和目标文件)属于同一次请求,前后页共用一个request,可以通过此来传递一些数据或者session信息,request.setAttribute()和request.getAttribute()。
* 2、在前后两次执行后,地址栏不变,仍是当前文件的地址。
* 3、不能转向到本web应用之外的页面和网站,所以转向的速度要快。
* 4、URL中所包含的“/”表示应用程序(项目)的路径。
* */
request.getRequestDispatcher("/toLogin").forward(request, response);
//response.sendRedirect("login");
return false;
}
}

5.4.新建controller包

在项目创建LoginController,代码如下

package com.augus.controller;

import com.augus.pojo.User;
import com.augus.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

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

@Controller
public class LoginController {
@Autowired
private UserService userService;

@RequestMapping("toLogin")
public String userLogin(){
return "login";
}

@RequestMapping("/login")
public String login(User user, HttpServletRequest request){
//获取根据用户名 查询到的user对象
User user2 = userService.findByUserName(user.getUsername());
if(user2 != null && user2.getPassword().equals(user.getPassword())){
//如果byUserName不为空,输入的密码和数据库中保存的密码保持一致,则表示登录成功,就写出产品信息
HttpSession session = request.getSession();//将用户信息放到session域中
//将当前登录的user2对象放到session域中
session.setAttribute("loginUser", user2);
return "redirect:/main";
}
request.setAttribute("msg","账户或者密码错误!!!");
return "login";
}

/**
* 跳转到主页面
*/
@RequestMapping("/main")
public String toMain() {
return "main";
}

//退出
@RequestMapping("/logout")
public String Logout(User user, HttpServletRequest request) {
//session 失效
request.getSession().invalidate();
return "login";
}
}

六、在WEB-INF下面创建前端html文件

  • 创建login.html,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hi</title>
</head>
<body>
<form th:action="login" method="post">
<table style="margin: auto">
<tr>
<td th:if="${not #strings.isEmpty(msg)}" colspan="2" align="center">
<p style="color: red;margin: auto" th:text="${msg}"></p>
</td>
</tr>
<tr>
<td>用户名:</td>
<td><input type="text" name="username" required><br></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password" required><br></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="登陆">
<input type="reset" value="重置">
</td>
</tr>
</table>
</form>
</body>
</body>
</html>
  • 创建main.html,代码如下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>系统主页</title>
</head>
<body>
<h1 th:text="'欢迎您:'+${session.loginUser.getUsername()}" th:if="${not #strings.isEmpty(session.loginUser)}"></h1>
<a th:href="logout">退出登录</a>
</body>
</html>

七、启动Tomcat重新部署项目

1.访问http://localhost:8080/ssm02_war_exploded/mian,效果如下:

Spring MVC拦截器实现用户登录权限验证_spring_07

从上图可以看出,当用户没有登录而直接访问系统主页面(mian.html)时请求将被登录拦截器拦截,返回到登录页面,并提示信息。如果用户在用户名框中输入“zhangsan”,在密码框中输入“123456”,单击“登录”按钮后浏览器的显示结果如下图

Spring MVC拦截器实现用户登录权限验证_spring_08

当单击“退出”链接后,系统将从主页面返回到登录页面。

Spring MVC拦截器实现用户登录权限验证_xml_09

如果输入的用户名或密码错误,浏览器的显示结果如下图所示:

Spring MVC拦截器实现用户登录权限验证_spring_10