springboot + thymeleaf +interceptor实现用户登录验证

一、Thymeleaf

我们以前开发,前端需要动态展示数据,用的的最多的就是jsp,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。

jsp支持非常强大的功能,包括能写Java代码,但是呢,我们现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war,像第二,我们用的还是嵌入式的Tomcat,所以呢,他现在默认是不支持jsp的。

那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办呢?

SpringBoot推荐你可以来使用模板引擎:

模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有用的比较多的freemarker,包括SpringBoot给我们推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想都是一样的,什么样一个思想呢我们来看一下这张图:
在这里插入图片描述
模板引擎的作用就是我们来写一个页面模板,比如有些值呢,是动态的,我们写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。

然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。

只不过呢,就是说不同模板引擎之间,他们可能这个语法有点不一样。SpringBoot给我们推荐的Thymeleaf模板引擎,这模板引擎呢,是一个高级语言的模板引擎,他的这个语法更简单。而且呢,功能更强大。

首先,我们来看在SpringBoot里边怎么用。
可以参考一下:
Thymeleaf 官网:https://www.thymeleaf.org/

Thymeleaf 在Github 的主页:https://github.com/thymeleaf/thymeleaf

Spring官方文档:找到我们对应的版本

https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter

找到对应的pom依赖:可以适当点进源码看下本来的包!

我们首先得按照SpringBoot的自动配置原理看一下我们这个Thymeleaf的自动配置规则,在按照那个规则,我们进行使用。

我们去找一下Thymeleaf的自动配置类:ThymeleafProperties
在这里插入图片描述
我们可以在其中看到默认的前缀和后缀!

我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。

使用thymeleaf什么都不需要配置,只需要将他放在指定的文件夹下即可!

二、环境准备

  • 首先创建一个springboot项目,并引入thymeleaf依赖。贴一下pom.xml
<!--引入thymeleaf-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在这里插入图片描述
静态资源存放目录如上图。

三、使用Thymeleaf模板更改页面

1.在html标签添加命名空间:
在这里插入图片描述
2.修改css目录访问路径
在这里插入图片描述
springboot项目默认classpath目录路径由四个:分别是:

1.“classpath:/META-INF/resources/”,
2.“classpath:/resources/”,
3.“classpath:/static/”,
4. "classpath:/public/"

可以参考一下上一篇文章:springboot静态资源加载规则
所以我们这里引入CSS文件可以省略static目录直接从css目录开始引入。

这里要注意:如果css前面不写 / 的话,使用localhost:8080是可以访问到默认index.html的并且也是有样式的,但是经过servlet后台重新转发后将无法使用css样式,引入路径会被修改。

现在我们启动项目看一下:
在这里插入图片描述
是可以访问的,因为springboot项目启动后会默认找classpath目录下面的index.html页面,所以我们一般吧index.html作为首页。
在这里插入图片描述
可以看到,使用localhost:8080localhost:8080/index.html的效果是一样的。

四、修改mvc视图配置

我们一般在访问网站的时候,如果访问了错误的路径,是不是,一般都直接跳转到首页了。这里需要自己去定义一个视图转发器,就可以实现这种效果。
我们新建一个config目录,并创建一个MyWebConfig类,实现WebMvcConfigurer接口。
在这里插入图片描述

package com.example.demo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 自定义配置中心
 * create by c-pown on 2020-07-16
 */
@Configuration
@Slf4j
public class MyWebConfig implements WebMvcConfigurer {
    /**
     * 自定义视图转发器
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        log.info("进入视图转发");
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/login").setViewName("index");
    }
}
  • 这里自定义的配置文件,一定要加上@Configuration注解,这样springboot在启动的时候就会加载我们的配置项,装配到系统里。
  • 重写 addViewControllers方法,并自定义转发路径。就可以在项目访问其他路径的时候转发到index首页。
    启动一下:
    在这里插入图片描述
    发现在项目启动时已经加载到我们的配置类。

这时候发现访问http://localhost:8080/login路径也是可以跳转到受首页的。在这里插入图片描述

四、用户登录校验

index.html代码:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<meta name="description" content="">
		<meta name="author" content="">
		<title>Signin Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<!--此处路径必须带有  / 否则仓后台跳转过来会默认增加一级目录导致 css访问失效-->
		<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
		<!-- Custom styles for this template -->
		<link th:href="@{/css/signin.css}" rel="stylesheet">
	</head>

	<body class="text-center">
		<form class="form-signin" th:action="@{/user/login}">
			<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>

			<!--@{ not strings.notEmpty(msg)} 可以校验msg是否为空  不为空才展示-->
			<p style="color: red" th:text="${msg}" th:if="@{ not strings.notEmpty(msg)}"></p>

			<label class="sr-only">Username</label>
			<input type="text" class="form-control" placeholder="Username" name="userName" required="" autofocus="">
			<label class="sr-only">Password</label>
			<input type="password" class="form-control" placeholder="Password" name="password" required="">
			<div class="checkbox mb-3">
				<label>
          <input type="checkbox" value="remember-me"> Remember me
        </label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
			<a class="btn btn-sm">中文</a>
			<a class="btn btn-sm">English</a>
		</form>

	</body>

</html>
  1. th:action="@{user/login} 为我们请求的接口地址,thymeleaf默认为我们添加了 localhost:8080项目访问地址。所以可以直接写接口地址即可。
<form class="form-signin" th:action="@{/user/login}">
  1. 此处可以展示登录报错信息,如果校验失败返回错误信息以展示。
<p style="color: red" th:text="${msg}" th:if="@{ not strings.notEmpty(msg)}"></p>

后端代码:

package com.example.demo.controller;

import com.alibaba.druid.util.StringUtils;
import com.example.demo.model.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.HashMap;
import java.util.Map;

/**
 * 用户登录controller
 * create by c-pown on 2020-07-16
 */
@Controller
@Slf4j
@RequestMapping("user")
public class LoginUserController {

    private static Map<String,Object> userDb = new HashMap<>();

    /**
     * 模拟数据库 有一个用户张三 密码是:123456
     */
    static {
        userDb.put("张三",new SysUser("张三","123456"));
    }

    /**
     * /登录接口
     */
    @RequestMapping("/login")
    public String LoginUser(@RequestParam("userName")String userName,@RequestParam("password") String password, Model model){
        log.info("user login ...");
        //如果张三不存在  提醒用户去注册
        if(userDb.get(userName) == null){
            log.info("user login error :用户不存在,请先注册用户");
            model.addAttribute("msg","用户不存在,请先注册用户");
            return "index";
        }else{
            SysUser user = (SysUser)userDb.get(userName);
            if(!StringUtils.equals(password,user.getPassword())){
                log.info("user login error :密码错误");
                model.addAttribute("msg","密码错误");
                return "index";
            }else{
                log.info("user login success");
                return "dashboard";
            }
        }
    }
}
  • 这里使用Map模拟一下数据库 (在后面我会陆续集成数据库等其他资源)
 private static Map<String,Object> userDb = new HashMap<>();
 /**
     * 模拟数据库 有一个用户张三 密码是:123456
     */
    static {
        userDb.put("张三",new SysUser("张三","123456"));
    }
  • 我们接受前端请求,如果用户存在,就跳转到列表dashboard页面,如果不存在就返回首页,并提醒用户注册,
    如果密码错误,则返回首页提醒密码错误。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    使用{张三,123456}登录成功。
    **注意:**我们做登录页面以及一些安全性较高的页面,可以修改请求方式为post;
<form class="form-signin" th:action="@{/user/login}" method="post">

五、访问权限校验

上面我们写了,用户登录校验,但是还有个缺陷,那就是我们在后台转发dashboard页面,是无论如何都能转发成功的,一般我们在实际项目中都是登录过后,处于登录状态才能够跳转。
这里我们可以使用session + Interceptor 拦截器,来对用户是否登录做校验,同时判断是否放行该请求。

<1> 我们先修改controller代码,如果用户登录成功,则往session里面添加用户session:

package com.example.demo.controller;

import com.alibaba.druid.util.StringUtils;
import com.example.demo.model.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

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

/**
 * 用户登录controller
 * create by c-pown on 2020-07-16
 */
@Controller
@Slf4j
@RequestMapping("user")
public class LoginUserController {

    private static Map<String,Object> userDb = new HashMap<>();

    /**
     * 模拟数据库 有一个用户张三 密码是:123456
     */
    static {
        userDb.put("张三",new SysUser("张三","123456"));
    }

    /**
     * /登录接口
     */
    @RequestMapping(value = "/login")
    public String LoginUser(@RequestParam("userName")String userName, @RequestParam("password") String password, Model model, HttpSession session){
        log.info("user login ...");
        //如果张三不存在  提醒用户去注册
        if(userDb.get(userName) == null){
            log.info("user login error :用户不存在,请先注册用户");
            model.addAttribute("msg","用户不存在,请先注册用户");
            return "index";
        }else{
            SysUser user = (SysUser)userDb.get(userName);
            if(!StringUtils.equals(password,user.getPassword())){
                log.info("user login error :密码错误");
                model.addAttribute("msg","密码错误");
                return "index";
            }else{
                //登录成功  添加session
                session.setAttribute("loginUser",user);
                log.info("user login success 添加用户到 session");
                return "dashboard";
            }
        }
    }
}
  • 登陆成功将SysUser对象放入session。

<2> 再定义一个拦截器 MyInterceptor

package com.example.demo.config;

import com.example.demo.model.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

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

/**
 * 自定义请求拦截器
 * create by c-pown on 2020-07-17
 */
@Slf4j
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 这里可以对请求之前进行拦截
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("拦截到请求");
        //判断session里面有没有用户
        SysUser loginUser = (SysUser)request.getSession().getAttribute("loginUser");
        //如果没有 转发到首页登录
        if(loginUser == null ){
            log.info("没有登录,请到首页登录");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }
        log.info("session User 登录用户:"+loginUser);
        //如果用户不等于 空 放行
        return true;
    }
}
  • 需要实现HandlerInterceptor 接口,并重写preHandle方法。
  • 如果有请求被拦截到,就判断session里面是否有SysUser对象,如果没有就跳转到首页index.

<3> 将拦截器加载到配置MyConfig

package com.example.demo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 自定义配置中心
 * create by c-pown on 2020-07-16
 */
@Configuration
@Slf4j
public class MyWebConfig implements WebMvcConfigurer {
    /**
     * 自定义视图转发器
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        log.info("进入视图转发");
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/login").setViewName("index");
    }

    /**
     * 加载自定义拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截所有请求  除了转发到首页的请求
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/index","/index.html","/user/login","/login","/css/*","/img/*","/js/*");
    }
}
  • registry.addInterceptor(new MyInterceptor())将自定义拦截器放入。
  • addPathPatterns("/**")默认拦截所有请求。
  • excludePathPatterns("/index","/index.html","/user/login","/login","/css/*","/img/*","/js/*")排除首页登录页,以及静态资源。

重启项目,试一下:
在这里插入图片描述

  • 现在访问任何路径,只要没有登录都可以跳转到首页,因为我们拦截了所有请求。

在这里插入图片描述

  • 使用{张三,123456}登录是可以的。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200717140039640.png

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值