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

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

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

 

导入依赖

  <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:不包括、排除

四、在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"/>

    <mvc:annotation-driven/>

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

    <!--配置静态资源放行-->
    <!--<mvc:resources mapping="/js/**" location="/WEB-INF/js/"/>-->
    <mvc:resources mapping="/upload/**" location="/upload/"/>

    <!--文件上传组件
    id属性的值必须为multipartResolver
    -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传文件的默认编码格式-->
        <property name="defaultEncoding" value="UTF-8"/>
        <!--在这里控制上传文件的大小  不推荐
        这时候没办法给前端返回提示信息,推荐在controller中配置-->
        <!--<property name="maxUploadSize" value="10240000000"></property>-->
    </bean>

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

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

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

  • 在数据库中创建user表

  •  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包

创建接口UserService

package com.augus.service;

import com.augus.pojo.User;

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

创建impl包,创建UserServiceImpl实现类

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.5.新建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,代码如下
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="login" method="post">
       <table style="margin: auto">
           <tr>
               <td>
                   <p style="color: red;margin: auto">${requestScope.message}</p>
               </td>
           </tr>
           <tr>
              <td>用户名:</td>
               <td><input type="text" name="username" required></td>
           </tr>
           <tr>
               <td>密码:</td>
               <td><input type="text" name="password" required></td>
           </tr>
           <tr>
               <td colspan="2" align="center"><input type="submit" value="登陆"></td>
           </tr>
       </table>
    </form>
</body>
</html>
  • 创建main.html,代码如下
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>欢迎来到主页</title>
</head>
<body>
    <h1>欢迎您:${sessionScope.loginUser.username}</h1>
    <a href="logout">退出登录</a>
</body>
</html>

七、启动Tomcat重新部署项目

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

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

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

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

posted @ 2022-10-02 13:51  酒剑仙*  阅读(232)  评论(0编辑  收藏  举报