Day4-登录页和jwt认证
前端登录页面实现
使用 Vue3+Vite4+VueRouter+ElementPlus+TailwindCss+AnimateCss 来实现。
创建前端项目
确保你拥有 nodejs 环境后进行下面操作!
进入 weblog 目录中创建前端项目
weblog 目录中创建前端项目在目录下执行:
npm create vue@latest执行过程中,会提示你命名新项目,我们命名为 weblog-vue3,以及是否需要开启一些诸如 TypeScript 和测试支持之类的可选功能,这里统一敲击回车键选择 No 即可。当你看到命令行中提示 Done , 表示你已经创建好了 weblog 前端项目。
添加依赖
# 进入项目文件夹
cd weblog-vue3
# 安装项目所需依赖
npm install
# 添加vue-router
npm install vue-router
# 添加TailwindCss
npm install -D tailwindcss postcss autoprefixer
# 添加Flowbite组件库
npm install flowbite
# 添加ElementPlus
npm install element-plus --save
# 添加ElementPlus图标库
npm install @element-plus/icons-vue
# 添加自动导入
npm install -D unplugin-vue-components unplugin-auto-import
# 添加AnimateCss
npm install animate.css --save进行配置
在添加了上述依赖后,首先打开 src/main.js 文件,将内容导入并添加到 Vue app 实例中, main.js 修改后如下:
alias 是一个用于定义路径别名的配置选项。当你的项目结构变得复杂时,路径别名可以帮助你简化 import 或 require 语句中的路径。通过 .. 来指定上一级目录,然后再定位具体路径下。考虑一个大型项目中,我们经常需要这样的引用,当文件层级很深,那么代码可能会像下面这样:
这种相对路径不易于管理和阅读。使用 alias,我们可以将路径简化为:
这样一来,不仅路径更短、更明确,而且移动文件时无需改动 import 路径。
unplugin-vue-components 和 unplugin-auto-import 这两款插件是实现 ElementPlus 组件按需导入的插件。
接下来我们将对这两项进行配置。在项目的根目录中,找到 Vite 的配置文件 vite.config.js,编辑它,添加 alias 配置并导入插件,代码如下:
接下来对引入的各个依赖进行配置:
vue-router
在 src 目录下,创建 /pages 文件夹,此文件夹中统一存放 页面 相关代码,然后 /pages 文件夹下,再创建两个文件夹, 分别是:
/admin: 存放管理后台相关页面;/frontend:存放前台展示相关页面,如首页、博客详情页等;
在 /frontend 文件夹下,创建 index.vue 首页文件,代码如下:
在 /src 目录下,新建 /router 路由文件夹,用于存放路由相关代码,并在此文件夹下,新建 index.js 文件, 代码如下:
上面代码中,我们将首页组件 index.vue 导入,并且通过定义 routes 数组来统一声明所有的路由,在数组中,我们创建了一个绑定到了根目录 / 的路由地址,指向了首页组件。
<router-view> 是 Vue Router 的一个核心组件,它是一个功能性组件(functional component)。其主要作用是根据当前的路由(URL)动态地渲染对应的组件,相当于是一个“占位符”,它会自动渲染与当前路径匹配的组件。
1、动态渲染组件:
<router-view>根据当前的 URL,自动渲染与当前路径匹配的组件。2、嵌套路由: 在有嵌套路由的情况下,
<router-view>可以用于渲染嵌套的子路由组件。
接下来,我们需要在 app.vue 中添加该组件,用于动态渲染路由对应的组件,再删除 app.vue 中的内容后,添加如下代码:
TailwindCss
之前的依赖安装命令会在项目中安装三个依赖,分别为:
tailwindcss:Tailwind CSS 框架本身。postcss:一个用于转换 CSS 的工具。autoprefixer:一个 PostCSS 插件,用于自动添加浏览器供应商前缀到 CSS 规则中,确保跨浏览器的兼容性。
同时我们还安装了 flowbite 组件库。Flowbite 是一个基于 Tailwind CSS 创建的组件库,旨在帮助开发者快速构建现代、响应式的 Web 应用界面。接下来我们需要进行一些配置以使这些内容生效。
在前端项目根目录执行如下命令, 此命令用于生成 tailwind.config.js 和 postcss.config.js 配置文件。:
生成后,打开 tailwind.config.js 文件,进行如下配置,添加所有模板文件路径,并且引入 flowbite 组件库:
清空 main.css 的初始内容,引入 Tailwind 样式:
构建首页框架与登录页
首页框架构建
清空 src/pages/frontend 文件夹下 index.vue 文件的内容,重写首页,主要是定义了响应式的导航栏,登录按钮加上了 $router.push('/login') 的点击跳转路由动作,该路由会在下面定义登录页时添加到 vue-router 的配置中:
登录页构建
在 src/pages/admin 文件夹下创建 login.vue 文件。登录页构建了一个左右分栏的页面样式,并添加了动画效果,代码如下:
上面样式可以根据自己需要进行调节美化,左右边栏的动画样式可以参考 AnimateCSS 网站进行修改, 左侧图片出自 IconFont ,可以使用其他图片进行替换,或者直接使用原图 developer.png (置于 src/assets 文件夹下):

接下来配置一下路由,修改 src\router\index.js 文件,添加上登录页的路由:
数据库连接和JWT登录鉴权
MyBatis Plus和P6Spy
MyBatis Plus (简称 MP) 是一款持久层框架,说白话就是一款操作数据库的框架。它是一个 MyBatis 的增强工具,就像 iPhone手机一般都有个 plus 版本一样,它在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。而 P6Spy 组件则用来实现完整的 SQL 打印,在日志中打印SQL语句,能够及时了解每个操作都具体执行了什么,同时通过打印执行耗时,可以发现一些慢SQL,提前做好优化,不过该组件不适合在生产环境中使用,所以需要在配置文件中进行区分。
建立数据库
搭建自己的 mysql 数据库 weblog (此处操作略)。再执行如下建表语句:
通过以上操作在 weblog 中建立了 t_user 表。
整合数据库依赖
在父项目 weblog-springboot 的 pom.xml 文件中,添加依赖:
然后,在 weblog-module-common 模块的 pom.xml 文件中,引入 MP 和 MySQL 依赖:
添加配置
修改 application-dev.yml 配置文件
详细解释一下各个配置的含义和作用:
spring.datasource.driver-class-name: 指定数据库驱动类的完整类名,这里使用的是 MySQL 数据库的驱动类。spring.datasource.url: 数据库连接的 URL,指向本地的 MySQL 数据库,端口是 3306,数据库名是weblog,同时配置了一系列参数,如使用 Unicode 编码、字符编码为 UTF-8、自动重连、不使用 SSL、对零时间进行转换等。spring.datasource.username: 数据库用户名,这里使用的是root。spring.datasource.password: 数据库密码,这里使用的是123456。
数据库链接池这块,我们使用的是 Spring Boot 默认的 HikariCP,它是一个高性能的连接池实现 , 同时,它号称是速度最快的连接池:
spring.datasource.hikari.minimum-idle: Hikari 连接池中最小空闲连接数。spring.datasource.hikari.maximum-pool-size: Hikari 连接池中允许的最大连接数。spring.datasource.hikari.auto-commit: 连接是否自动提交事务。spring.datasource.hikari.idle-timeout: 连接在连接池中闲置的最长时间,超过这个时间会被释放。spring.datasource.hikari.pool-name: 连接池的名字。spring.datasource.hikari.max-lifetime: 连接在连接池中的最大存活时间,超过这个时间会被强制关闭。spring.datasource.hikari.connection-timeout: 获取连接的超时时间。spring.datasource.hikari.connection-test-query: 用于测试连接是否可用的 SQL 查询,这里使用的是SELECT 1,表示执行这个查询来测试连接。
修改 application-prod.yml 配置文件
添加P6Spy配置
在 weblog-web 模块中的 resources 目录下添加 spy.properties 配置文件:
测试数据库连接
在 weblog-module-common 模块中创建 config 包和 domain 包,同时在 domain 包下创建 dos 和 mapper 包。在 config 包下,新建一个 MybatisPlusConfig 配置文件:
@Configuration: 此注解声明该类为配置类;@MapperScan: 扫描 MP 的mapper接口存放位置。PS: 数据库相关的代码,我们统一放置在
domain这个包中
在 dos 包下,新建 UserDO 类, 字段和数据库中的字段通过转驼峰的形式对应一一对应起来 ,MP 框架会默认通过这种规则将字段光联在一起:
在 mapper 包中新建 UserMapper 类,在该类中构建一个通过用户名在数据库中查找数据条目的方法:
在 weblog-web 模块中的单元测试类中,新增一个测试方法,代码如下:
接下来运行该方法,测试数据条目是否成功写入数据库中了,因为我们现在都是在测试环境中运行的,同时可以检查一下配置的 P6Spy 是否生效,看看日志中有没有打印出该条SQL语句。


Spring Security 与 JWT 鉴权
配置 Spring Security
Spring Security 是一个功能强大的安全框架,能够帮助您在应用程序中实现认证(Authentication)和授权(Authorization)功能。在整合 Spring Security 框架之前,我们需要新建一个 weblog-module-jwt 子模块(可以参考之前的教程创建),它主要用于放置 jwt 相关的功能代码。创建完成后修改子模块的 pom.xml 文件:
另外,还需在父项目 weblog-springboot 的 pom.xml 文件中添加 jwt 子模块 :
最后,在 weblog-module-admin 模块中,引入 weblog-module-jwt 模块和 Spring Security 依赖,因为认证、鉴权功能只有 admin 后台需要:
自定义配置
要更好地控制 Spring Security 的行为,可以创建一个自定义的 SecurityConfig 类,继承自 WebSecurityConfigurerAdapter。通过覆盖方法,我们可以配置认证、授权规则、自定义登录页面、注销等。
在 application-dev.yml中配置:
在 weblog-module-admin 模块中的 config 包下,创建一个 WebSecurityConfig 配置类,并进行如下自定义配置:
在上面 configure() 方法中,主要定义了如下功能:
禁用 CSRF 和表单登录
禁用 CSRF(跨站请求伪造)保护,因为在前后端分离的架构中,通常使用 JWT 进行身份验证,CSRF 保护不再必要。
禁用默认的表单登录,改用 JWT 进行身份验证。
应用 JWT 认证配置
将
JwtAuthenticationSecurityConfig中定义的 JWT 过滤器应用到安全配置中,以处理登录请求和生成 JWT。URL 访问控制
保护所有以
/admin/**开头的 URL,要求用户经过身份验证。允许其他所有请求不需要身份验证。配置 HTTP 基本认证入口点
使用自定义的
RestAuthenticationEntryPoint处理未认证用户的请求。会话管理策略
设置会话管理策略为无状态(
SessionCreationPolicy.STATELESS),因为在前后端分离的架构中,通常不使用服务器会话。添加 Token 认证过滤器
在
UsernamePasswordAuthenticationFilter之前添加TokenAuthenticationFilter,用于从请求中提取和验证 JWT。
实际上我们需要在 jwt 模块中实现如下主要类,来使自定义的 Spring Security 配置生效:
JwtAuthenticationSecurityConfig: 负责配置 JWT 认证的细节,包括成功和失败处理器、用户信息服务和密码编码器。TokenAuthenticationFilter: 负责从请求头中提取 JWT,验证其有效性,并将用户信息存入SecurityContextHolder。RestAuthenticationEntryPoint: 处理未认证用户的请求,返回适当的错误响应。
在 jwt 模块中创建如下包,接下进行JWT模块的逐步构建:

构建JWT认证
以下所有内容若无提示都是在
jwt子模块中进行的!
令牌工具类 JwtTokenHelper
JwtTokenHelper在 utils 包中封装一个 JwtTokenHelper 工具类:
通过运行该工具类的 main 方法,我们可以得到一个 base64 编码的密匙,将密匙存放到我们的 application.yml 配置中,同时设置 token 的签发人(可以自行填写):
密码加密类 PasswordEncoderConfig
PasswordEncoderConfig使用明文密码很容易受到工具,所以我们需要用密码加密算法来保护用户密码。
在 config 包中创建 PasswordEncoderConfig 配置类:
PasswordEncoder 接口是 Spring Security 提供的密码加密接口,它定义了密码加密和密码验证的方法。通过实现这个接口,您可以将密码加密为不可逆的哈希值,以及在验证密码时对比哈希值。
上述代码中,我们初始化了一个 PasswordEncoder 接口的具体实现类 BCryptPasswordEncoder。BCryptPasswordEncoder 是 Spring Security 提供的密码加密器的一种实现,使用 BCrypt 算法对密码进行加密。BCrypt 是一种安全且适合密码存储的哈希算法,它在进行哈希时会自动加入“盐”,增加密码的安全性。通过运行该工具类的 main 方法,我们就得到了加密后的用户密码,请复制得到的加密密码,接下来将会使用到。
用户详细服务类 UserDetailServiceImpl
UserDetailServiceImpl编辑 UserMapper 接口
在 service 包中创建 UserDetailServiceImpl 实现类:
该类是UserDetailsService 接口的实现。UserDetailsService 是 Spring Security 提供的接口,用于从应用程序的数据源(如数据库、LDAP、内存等)中加载用户信息。它是一个用于将用户详情加载到 Spring Security 的中心机制。UserDetailsService 主要负责两项工作:
加载用户信息: 从数据源中加载用户的用户名、密码和角色等信息。
创建 UserDetails 对象: 根据加载的用户信息,创建一个 Spring Security 所需的
UserDetails对象,包含用户名、密码、角色和权限等。
在上面代码中,我们实现了 UserDetailsService 接口,并重写了 loadUserByUsername() 方法,该方法用于根据用户名加载用户信息的逻辑 ,需要从数据库中查询。在数据库表t_user中执行如下语句添加用户(请根据自己的配置文件定义修改用户名,密码需要是上一步加密后生成的密码):
自定义认证过滤器 JwtAuthenticationFilter
JwtAuthenticationFilter在 exception 包下新建 UsernameOrPasswordNullException 类:
然后在 filter 包下新建 JwtAuthenticationFilter 类:
过滤器继承了 AbstractAuthenticationProcessingFilter,用于处理 JWT(JSON Web Token)的用户身份验证过程。在构造函数中,调用了父类 AbstractAuthenticationProcessingFilter 的构造函数,通过 AntPathRequestMatcher 指定了处理用户登录的访问地址。这意味着当请求路径匹配 /login 并且请求方法为 POST 时,该过滤器将被触发。
attemptAuthentication() 方法用于实现用户身份验证的具体逻辑。首先,我们解析了提交的 JSON 数据,并获取了用户名、密码,校验是否为空,若不为空,则将它们封装到 UsernamePasswordAuthenticationToken 中。最后,使用 getAuthenticationManager().authenticate() 来触发 Spring Security 的身份验证管理器执行实际的身份验证过程,然后返回身份验证结果。
自定义认证处理器(成功&失败)
在 Spring Security 中,AuthenticationFailureHandler 和 AuthenticationSuccessHandler 是用于处理身份验证失败和成功的接口。它们允许您在用户身份验证过程中自定义响应,以便更好地控制和定制用户体验。
在 model 包下新建LoginRspVO 类。此类是登录的响参类,VO (View Object) 表示和视图相关的实体类,rsp 是 response 的缩写,表示返参,对应的 req 是 request 的缩写,表示请求。代码如下:
同时为了在过滤器中方便的返回 JSON 参数,我们需要封装一个工具类 ResultUtil, 放置在 /utils 包下,代码如下:
编辑 weblog-module-common 模块中的 ResponseCodeEnum 枚举类,添加用户异常类型的响应码:
在 handler 包下创建 RestAuthenticationSuccessHandler 类,用于处理认证成功的数据返回:
该自定义的认证成功处理器首先从 authentication 对象中获取用户的 UserDetails 实例,这里是主要是获取用户的用户名,然后通过用户名生成 Token 令牌,最后返回数据。
再在 /handler 包下,创建 RestAuthenticationFailureHandler 认证失败处理器:
这个类实现了 AuthenticationFailureHandler 接口,主要用于处理用户认证失败的情况。
当用户登录失败时,onAuthenticationFailure 方法会被调用。这个方法接收三个参数:HttpServletRequest、HttpServletResponse 和 AuthenticationException。在方法内部,首先通过日志记录认证失败的详细信息。接着,根据异常的类型,返回不同的错误信息。如果异常是 UsernameOrPasswordNullException,则表示用户名或密码为空,使用 ResultUtil.fail 方法返回相应的错误信息。如果异常是 BadCredentialsException,则表示用户名或密码错误,同样使用 ResultUtil.fail 返回错误信息。最后,若认证失败的原因不属于上述两种情况,则返回一个通用的登录失败信息。
自定义token校检过滤器 TokenAuthenticationFilter
TokenAuthenticationFilter在建立过滤器之前,我们需要在 /handler 包下,新增 RestAuthenticationEntryPoint 处理器,它专门用来处理当用户未登录时,访问受保护的资源的情况:
当请求受保护的接口且请求头中未携带 token 时,我们收到的异常类型为 InsufficientAuthenticationException,请求的 HTTP 响应码为 401, 这就是未授权的异常类型。因此将这个类型的异常单独拎出来处理。覆写的方法主要实现当校验到 Token 异常时,进行捕获并返回不同的提示信息。
在 /filter 包下,创建 TokenAuthenticationFilter 过滤器,专用于校检 token 令牌:
这一步自定义了一个 Spring Security 过滤器 TokenAuthenticationFilter,它继承了 OncePerRequestFilter,确保每个请求只被过滤一次。在重写的 doFilterInternal() 方法中来定义过滤器处理逻辑,首先,从请求头中获取 key 为 Authorization 的值,判断是否以 Bearer 开头,若是,截取出 Token, 对其进行解析,并对可能抛出的异常做出不同的返参。最后,我们获取了用户详情信息,将用户信息存入 authentication,方便后续进行校验,同时将 authentication 存入 ThreadLocal 中,方便后面方便的获取用户信息,然后在处理完成并且没有出错之后放行请求。
你可能注意到在上面我们实现了处理器类 RestAuthenticationEntryPoint ,但上面 @Autowired 注解的是该类实现的接口 AuthenticationEntryPoint 。在 Spring 框架中,@Autowired 注解用于自动装配依赖项。当你在类中声明一个带有 @Autowired 注解的字段时,Spring 容器会在启动时自动查找与该字段类型匹配的 Bean,并将其注入到该字段中。在该项目中,因为我们之定义了一个 AuthenticationEntryPoint 类型的 Bean RestAuthenticationEntryPoint ,可以直接使用 AuthenticationEntryPoint 。但如果有多个实现,则需要使用 @Qualifier("restAuthenticationEntryPoint") 进行指定。
权限不足处理器 RestAccessDeniedHandler
RestAccessDeniedHandler在 /handler 包下,另外新增 RestAccessDeniedHandler 处理器,它主要用于处理当用户登录成功时,访问受保护的资源,但是权限不够的情况:
该处理器目前并不会用到,为了后续可能引入多角色时,预留该类方便拓展使用。
自定义 JWT 认证配置 JwtAuthenticationSecurityConfig
JwtAuthenticationSecurityConfig在 /config 包下新建 JwtAuthenticationSecurityConfig, 代码如下:
上述代码是一个 Spring Security 配置类,用于配置 JWT(JSON Web Token)的身份验证机制。它继承了 Spring Security 的 SecurityConfigurerAdapter 类,用于在 Spring Security 配置中添加自定义的认证过滤器和提供者。通过重写 configure() 方法,我们将之前写好过滤器、认证成功、失败处理器,以及加密算法整合到了 httpSecurity 中。
测试
使用设置的用户名和密码(明文)请求 \login 登录接口,得到token值。
添加新的测试API:
在不设置Authorization请求头时请求上面接口,设置Authorization请求头(值为Bearer + token值),以及输入错误的请求头后得到测试结果返回正常:



最后更新于