ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Layui登录注册模板全解析:从工程结构到认证逻辑

SpringBoot+Layui登录注册模板全解析:从工程结构到认证逻辑 简介基于SpringBootLayuiShiro搭建的登录注册模板面向需要快速实现用户认证功能的Java开发者。该项目以邮箱账号登录、邮箱验证码注册为核心流程引入Shiro作为安全框架完成密码加密与访问控制适合作为SpringBoot入门学习者理解安全认证整合的参考范例。压缩包共175个文件体积约1.35MB主要包含28个Java后端源码、33个JavaScript脚本、10个CSS样式及7个HTML页面同时提供YML配置、字体图标、图片素材和说明文档目录覆盖前端Layui页面、后端Controller/Service以及Shiro配置模块结构清晰便于直接导入项目对照学习。资源内还包含GIF动态演示可直观查看登录、注册、验证码校验等交互过程降低上手理解成本。目前已有988人学习下载既适合课程设计借鉴也能作为企业项目前期认证模块的快速搭建基础。1. 登录注册模板不是玩具是后端项目的起跑线任何一个带用户体系的 Web 项目无论最后做成电商、后台管理还是内容站第一块地基都是登录注册。拿到一个名为“基于SpringBootLayui的登录注册模板.zip”的压缩包本质上等于拿到了一套已经跑通的前后端联调范式SpringBoot 负责接口与鉴权Layui 负责页面与交互二者通过 JSON 对接不引入前端工程化构建链。这套模板最大的价值不是那几个页面而是告诉你怎么组织后端分层、怎么处理会话、怎么写统一返回体、怎么在前端弹窗报错。这个东西适合谁首先是 Java 后端刚起步的人需要一个不过度设计的范本其次是做毕业设计或公司内部系统的人想要一个能直接改的底子还有一些是接了老项目维护任务的人需要快速理解 SpringBoot 模板引擎时代的代码长什么样。接下来我会按实际开发顺序拆开这个模板先讲技术选型的来龙去脉再讲工程结构和配置然后分别过一遍后端与前端的关键代码最后落在验证部署和版本坑上。2. SpringBoot 与 Layui 组合的定位前后端不分离时代的最后一块舒适区2.1 为什么这个组合至今仍被大量模板采用SpringBoot 诞生已超过十年Layui 则更早二者都不是新东西但“SpringBoot Layui”这个组合在中文技术社区里一直有稳定的存量需求。你去看任何源码下载站、毕设项目站JavaWeb 分类下总量最大的仍然是这类组合。原因不复杂Layui 是一套纯浏览器端加载的 UI 框架引入方式是写一个layui.js和layui.css不需要 Node.js、不需要 Webpack也不存在跨域问题因为它和后端在同一个域名下跑。SpringBoot 则把原本繁琐的 SSM 配置收拢成自动配置写一个RestController就能返回 JSON。对比现在的 Vue SpringBoot 前后端分离方案Layui 模式的优势是“快”劣势是“边界模糊”——前端代码和后端代码在同一个工程里Java 代码里偶尔会飘着 JS 片段页面逻辑无法独立测试。对学习者和中小型内部系统来说快就是硬道理。SpringBoot 面试题里经常问的自动装配原理在这个组合里体现得最直观你引入spring-boot-starter-web后DispatcherServlet、Tomcat、Jackson全部自动就位你只写业务类即可。2.2 Layui 的核心使用方式layui.use 与模块化加载Layui 的使用方式不是写一堆script标签引入组件而是通过它的模块加载器按需拉取。常见做法是在 HTML 底部写这样一段初始化代码script src/lib/layui/layui.js/script script layui.use([form, layer, laydate, jquery], function(){ var form layui.form; var layer layui.layer; var $ layui.jquery; // 表单提交监听 form.on(submit(loginSubmit), function(data){ console.log(data.field); return false; // 阻止表单原生提交 }); }); /script这里说明几个参数layui.use第一参数是数组里面列当前页面要用到的模块名官方内置模块有form、layer、laydate、table、upload等回调函数里通过layui.form拿到模块实例这个过程叫“模块加载”。form.on(submit(loginSubmit))中loginSubmit是form上lay-filter属性的值不是 name 也不是 id这个细节经常被搞混。Layui 官网早已停止大版本更新但 2.8 版本仍然可用社区里也有针对 Vue 或原生 JS 的适配方案讨论。如果你的项目里有人问“layui 可以用 vue 吗”技术上的回答是Vue 是数据驱动视图Layui 是直接操作 DOM二者在表单联动场景下会互相干扰不建议混用模板项目里也不该出现这种架构。2.3 SpringBoot 版本选择模板默认 2.x别一上来追 3.x模板里的 SpringBoot 版本通常落在 2.7.x 这个区间。原因很简单3.x 要求 JDK 17 起步默认 Jakarta EE 命名空间javax.*改成jakarta.*很多老教程和老代码片段直接失效。如果你是在 idea 创建 SpringBoot 项目时选了 3.x再去抄模板里的代码会遇到HttpServletRequest导入不存在的报错那不是代码问题是版本差异。如果你的环境只装了 JDK 17 或 21又不想退回 1.8有两条路一是直接用 SpringBoot 3.x同时把模板里的javax.servlet包名全部换成jakarta.servlet改动量不大二是用 IDE 的 Project Structure 把项目 SDK 切到 8只要系统里装了 JDK 8 就能跑 2.x 的项目。模板类项目最忌讳在版本上标新立异我的习惯是“SpringBoot 2.7.13 JDK 8 Maven 3.6”这套组合历经大量生产环境验证兼容性风险最低。2.4 登录注册的功能边界模板里必须包含什么一个合格的登录注册模板功能上至少要覆盖三件事注册时的重复名校验、登录时的密码校验、访问受限资源时的拦截跳转。再往上是验证码、短信登录、OAuth2 第三方登录这些属于增强功能模板可以不含但代码结构上必须留出扩展位。下面用一个表格理清模板各层要承担的责任层次对应位置关键职责控制层com.example.demo.controller接收 HTTP 请求参数绑定不写业务业务层com.example.demo.service校验逻辑、密码加密、事务控制数据访问层com.example.demo.mapperSQL 操作MyBatis 或 JPA 二选一前端页面src/main/resources/static与templates用 Layui 渲染表单和反馈会话状态HttpSession 或 Redis存储登录态供拦截器读取看清这张表你拿到任何模板都能快速定位“哪里改逻辑、哪里改页面”。3. 工程结构与核心配置从打开 zip 到项目跑起来3.1 Maven 目录与配置文件定位解压 zip 后第一步不是双击 pom.xml而是先看目录层级。典型结构如下springboot-layui-login/ ├── pom.xml ├── sql/ │ └── login_demo.sql └── src/main/ ├── java/com/example/login/ │ ├── LoginApplication.java │ ├── controller/UserController.java │ ├── service/UserService.java │ ├── mapper/UserMapper.java │ └── config/WebConfig.java ├── resources/ │ ├── application.yml │ ├── mapper/UserMapper.xml │ ├── static/ │ │ ├── css/login.css │ │ └── js/layui.js │ └── templates/ │ ├── login.html │ └── register.html └── webapp/ (若使用JSP)application.yml是核心配置入口下面给出一个最常见的 MySQL 5.7 配置server: port: 8080 servlet: context-path: / spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/login_demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 thymeleaf: prefix: classpath:/templates/ suffix: .html cache: false mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.login.entity configuration: map-underscore-to-camel-case: true代码块里值得逐项解释server.port是后端端口改了之后 Layui 页面的请求地址也要同步改context-path如果设置成/api所有接口路径都要加前缀spring.datasource四项是数据库连接的基础serverTimezone必须要有否则新版 MySQL 驱动会报时区错误thymeleaf配置只在使用模板引擎时生效如果模板是用纯静态 HTML 加 AJAX 方式做的这部分可以删map-underscore-to-camel-case设成 true数据库nick_name就能自动映射到实体的nickName省掉一堆Results注解。如果把 MySQL 换掉比如换成 SQLServer你需要同时改driver-class-name和url并检查 SQL 文件里是否存在反引号——MySQL 用反引号SQLServer 不认识。模板一般默认 MySQL所以这个坑不要忽略。3.2 数据库脚本字段设计与初始化数据模板的sql目录下会有一个建表脚本核心表就一张设计非常克制CREATE DATABASE IF NOT EXISTS login_demo DEFAULT CHARACTER SET utf8mb4; USE login_demo; DROP TABLE IF EXISTS user; CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(64) NOT NULL COMMENT 登录名, password varchar(255) NOT NULL COMMENT 密码(BCrypt加密), nick_name varchar(64) DEFAULT NULL COMMENT 昵称, email varchar(128) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO user (username, password, nick_name) VALUES (admin, $2a$10$mE.qNFmAlQqLOlE1iTj6p.8vM3slP8FvF8iYwVpQcJkDdG1oLbBdS, 管理员);强调两个细节。第一password字段长度为 255不要改成 32因为 BCrypt 加密结果长度是 60 位这是模板正确性的关键点第二初始管理员密码不是明文而是 BCrypt 密文你无法通过看数据库知道它是什么模板通常在 README 里写了默认密码如果没写就删掉这条 INSERT重新注册一个。3.3 实体类与 MapperMyBatis 的 XML 映射实体类User.java的字段与数据库列一一对应这里不展开。重点看UserMapper.java和对应的 XMLpackage com.example.login.mapper; import com.example.login.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; Mapper public interface UserMapper { User findByUsername(Param(username) String username); int insert(User user); }XML 里对应的方法是?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.login.mapper.UserMapper select idfindByUsername resultTypeUser SELECT id, username, password, nick_name, email, create_time FROM user WHERE username #{username} /select insert idinsert parameterTypeUser useGeneratedKeystrue keyPropertyid INSERT INTO user (username, password, nick_name, email) VALUES (#{username}, #{password}, #{nickName}, #{email}) /insert /mappernamespace必须是 Mapper 接口的全限定名id必须等于接口方法名二者错一个运行时就报绑定异常。resultType写User的前提是type-aliases-package配置正确并且map-underscore-to-camel-case已开启否则nick_name映射不到nickName上。这里提一个 MyBatis 与 SpringBoot 的兼容性注意点如果你用的是 SpringBoot 3.x需要引入mybatis-spring-boot-starter的 3.0 版本2.x 版本的 starter 在 SpringBoot 3.x 下会因为包名变更而启动失败。模板里如果是mybatis-spring-boot-starter 2.x配套 SpringBoot 2.x切勿混装。4. 后端认证逻辑Session、拦截器与密码加密4.1 注册接口参数校验与 BCrypt 加密落地注册接口是模板里逻辑最完整的一块它要同时处理格式校验、查重和加密存储。看下面这段UserService代码package com.example.login.service; import com.example.login.common.Result; import com.example.login.entity.User; import com.example.login.mapper.UserMapper; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import javax.annotation.Resource; Service public class UserService { Resource private UserMapper userMapper; private BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); public Result? register(String username, String password, String email) { if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) { return Result.error(400, 用户名和密码不能为空); } if (username.length() 3 || username.length() 20) { return Result.error(400, 用户名长度需在3到20个字符之间); } if (userMapper.findByUsername(username) ! null) { return Result.error(400, 用户名已存在); } User user new User(); user.setUsername(username); user.setPassword(encoder.encode(password)); user.setEmail(email); userMapper.insert(user); return Result.success(注册成功); } public ResultUser login(String username, String password) { User user userMapper.findByUsername(username); if (user null || !encoder.matches(password, user.getPassword())) { return Result.error(401, 用户名或密码错误); } return Result.success(user); } }这段代码里最关键的一行是encoder.encode(password)。为什么不直接存明文因为一旦数据库泄露所有用户密码直接暴露且用户很可能在其他平台复用了同一个密码这就是撞库攻击的根源。BCrypt 算法自带盐值同样的明文每次加密结果不同破解成本高是 Spring Security 内置的默认实现不需要额外引入完整的安全框架只引spring-security-crypto包即可。matches(password, user.getPassword())是校验过程它从密文中解析盐值并重新计算比对注意参数顺序不能反第一参是明文第二参是库里的密文。如果你从网上粘了一段自定义 MD5 加密趁早换掉MD5 早已不适合存储密码。注册里查重这步还有并发问题两个请求同时查到用户名不存在同时 insert后提交的会爆唯一键冲突。模板通常不做这层处理但你在理解时要心里有数生产方案是捕获DuplicateKeyException转为友好提示或者用数据库唯一索引兜底。4.2 登录接口与统一返回体模板的隐形规范登录注册接口的返回值模板几乎都采用统一的 JSON 结构常见的是code msg data三字段。这也是前后端不分离项目里最容易统一风格的地方后端不管成功失败都返回这个结构前端用code 200判断成败{ code: 200, msg: 登录成功, data: { id: 1, username: admin, nickName: 管理员 } }配合一个Result工具类来生成package com.example.login.common; public class ResultT { private int code; private String msg; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.code 200; r.msg success; r.data data; return r; } public static T ResultT error(int code, String msg) { ResultT r new Result(); r.code code; r.msg msg; return r; } // getter/setter 省略 }注意data的类型是泛型失败时它为 null前端拿不到也不报错。这个返回体是整个前后端联调的契约如果拆成前后端分离架构它就是接口文档的雏形。SpringBoot 默认用 Jackson 序列化对象为 JSONLocalDateTime类型字段需要额外加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)否则返回一串带 T 的 ISO 格式字符串Layui 表格渲染会不好看。登录成功后的会话保持方式是模板的另一个分水岭有的用 HttpSession有的用 JWT。模板项目几乎都选 Session因为简单且同域部署天然支持。对应代码是request.getSession().setAttribute(loginUser, user)后续请求通过拦截器检查这个属性是否存在。4.3 拦截器保护需要登录才能访问的页面登录状态检查不能靠每个 Controller 里 copy 一段 if而是用 Spring MVC 的拦截器统一处理。模板中的WebConfig是这样配置的package com.example.login.config; import com.example.login.interceptor.LoginInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; Configuration public class WebConfig implements WebMvcConfigurer { Resource private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) .excludePathPatterns( /login, /doLogin, /register, /doRegister, /css/**, /js/**, /images/**, /lib/** ); } }addPathPatterns(/**)匹配所有路径excludePathPatterns排除掉登录注册接口和静态资源。这里最容易踩的坑是漏掉静态资源目录Layui 的 js、css 文件全在/lib下如果不排除登录页都加载不出样式因为资源请求被拦截重定向了。LoginInterceptor的实现很简短package com.example.login.interceptor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); if (session.getAttribute(loginUser) ! null) { return true; } // 判断是否为 AJAX 请求 String xRequestedWith request.getHeader(X-Requested-With); if (XMLHttpRequest.equals(xRequestedWith)) { response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录或会话已过期\}); } else { response.sendRedirect(/login); } return false; } }这个拦截器区分了 AJAX 请求和普通页面跳转。如果用户在登录状态下打开了一个页面Session 超时后再点页面里的按钮Layui 的 AJAX 请求会收到 401 的 JSON而不是一个 302 重定向页面——后者会导致前端解析 HTML 报语法错误。Layui 的$.ajax默认不会跟随重定向到 JSON 接口这个细节是在实际联调中才会暴露的坑。5. Layui 页面与接口对接登录表单、注册表单与反馈交互5.1 登录页form.on 是核心入口登录页的 HTML 结构本身不复杂一个form加两个输入框加一个按钮关键是lay-filter值前后端要一致。看这段连 HTML 带 JS 的完整片段!DOCTYPE html html langzh-CN head meta charsetUTF-8 title用户登录/title link relstylesheet href/lib/layui/css/layui.css style body { background: #f2f2f2; } .login-box { width: 380px; margin: 120px auto; background: #fff; padding: 30px; border-radius: 6px; } /style /head body div classlogin-box h2 styletext-align:center;margin-bottom:20px;用户登录/h2 form classlayui-form lay-filterloginForm div classlayui-form-item input typetext nameusername required lay-verifyrequired placeholder用户名 autocompleteusername classlayui-input /div div classlayui-form-item input typepassword namepassword required lay-verifyrequired placeholder密码 autocompletecurrent-password classlayui-input /div div classlayui-form-item button classlayui-btn layui-btn-fluid lay-submit lay-filterloginSubmit登录/button /div /form /div script src/lib/layui/layui.js/script script layui.use([form, layer, jquery], function(){ var form layui.form; var layer layui.layer; var $ layui.jquery; form.on(submit(loginSubmit), function(data){ $.ajax({ url: /doLogin, type: POST, contentType: application/json, data: JSON.stringify(data.field), success: function(res){ if (res.code 200) { layer.msg(登录成功, {icon: 1, time: 1000}, function(){ window.location.href /index; }); } else { layer.msg(res.msg, {icon: 2}); } }, error: function(){ layer.msg(网络请求失败, {icon: 2}); } }); return false; }); }); /script /body /html代码逻辑说明lay-submit和lay-filterloginSubmit搭配click 事件才不会走按钮原生行为而是进入form.on(submit(loginSubmit))回调data.field是 Layui 自动收集的表单数据对象key 对应输入框的name属性JSON.stringify(data.field)把对象转 JSON 字符串后端用RequestBody接收。这里有一个容易踩的坑如果你用contentType: application/json后端必须用RequestBody接收如果后端 Controller 写的是RequestParam会直接报 415 或参数缺失。一致的选择是“全 JSON 或全表单编码”。上面的代码选了 JSON那么后端 Controller 就要写成这样PostMapping(/doLogin) public ResultUser doLogin(RequestBody LoginParam param, HttpServletRequest request) { ResultUser result userService.login(param.getUsername(), param.getPassword()); if (result.getCode() 200) { request.getSession().setAttribute(loginUser, result.getData()); } return result; }LoginParam是一个内部类或独立类字段名username、password必须与前端data.field的 key 完全一致Jackson 才能完成映射。如果你在拦截器里已经写了会话处理这里只存 Session 即可。5.2 注册页lay验证规则与重复密码校验注册页在登录页基础上增加字段同时要处理“确认密码”一致性校验。Layui 自带lay-verify规则也支持自定义规则layui.use([form, layer, jquery], function(){ var form layui.form; var layer layui.layer; var $ layui.jquery; form.verify({ pass: [ /^[\S]{6,12}$/, 密码必须为6到12位且不能出现空格 ], confirmPass: function(value){ var pwd $(#password).val(); if (value ! pwd) { return 两次输入的密码不一致; } } }); form.on(submit(regSubmit), function(data){ $.ajax({ url: /doRegister, type: POST, contentType: application/json, data: JSON.stringify(data.field), success: function(res){ if (res.code 200) { layer.alert(注册成功请登录, {icon: 1}, function(index){ layer.close(index); window.location.href /login; }); } else { layer.msg(res.msg, {icon: 2}); } } }); return false; }); });自定义规则confirmPass在函数内部拿到当前输入值和#password的 DOM 值做比对不一致就返回字符串Layui 会把这个字符串当作错误提示展示在表单项下方。form.verify的三种写法要分清正则直接写在[/.../, 提示]里函数用function(value){ return 错误信息; }如果需要读取其他字段函数内用 jQuery 取值即可。注册页里如果包含出生日期或入职日期这类字段可以引出laydate组件——热搜词里有“laydate 最大日期当前日期”这是 laydate 最常见的自定义需求因为默认允许选未来日期而用户生日不能晚于今天。正确写法是在渲染时指定max参数laydate.render({ elem: #birthday, max: 0, // 0 表示当前日期为最大值 trigger: click });max: 0是指“到今天为止”如果写max: 2024-01-01就是固定写死。动态最大日期的另一种做法是用new Date()生成字符串但 laydate 传0是官方支持的最省事写法。5.3 全局 AJAX 统一处理哪种方案更适合模板登录注册页每个页面都写一遍 AJAX 错误处理代码很冗余。模板里常见的进阶做法是在页面中引入一个公共 JS 文件重写 Ajax 的 success 回调// common.js layui.use([layer, jquery], function(){ var $ layui.jquery; var layer layui.layer; $.ajaxSetup({ dataType: json, complete: function(xhr) { if (xhr.status 401) { layer.msg(登录已过期请重新登录, {icon: 2}, function(){ window.location.href /login; }); } } }); });这里监听 401 状态码专门处理拦截器中将未登录请求标记为 401 的场景。这个文件在登录页不要引入因为在未登录状态下访问登录页不算异常注册页可以引入注册接口本身不需要登录态。6. 部署验证与常见陷阱模板跑通了还能怎么演进6.1 启动与验证的最小步骤拿到模板后按照“改配置 → 建库 → 起服务”三步走验证接口是否联通。先看pom.xml里有没有spring-boot-maven-plugin再确认依赖是否下载完整然后执行mvn clean package -DskipTests java -jar target/login-demo-0.0.1-SNAPSHOT.jar启动成功后用 curl 先验证后端接口不要急着开浏览器。这一步能快速区分“后端 bug”和“前端 bug”curl -X POST http://localhost:8080/doRegister \ -H Content-Type: application/json \ -d {username:test01,password:123456,email:testexample.com} curl -X POST http://localhost:8080/doLogin \ -H Content-Type: application/json \ -d {username:test01,password:123456}注意相同路径下接口地址是否与前端代码里写的/doRegister完全一致context-path是否已改。两条命令返回的 JSON 里code都应为 200。如果你在 Windows 环境下curl 命令里 JSON 字符串要用双引号包整体、内部字段用单引号或者直接转义否则会报参数格式错误。端口被占用是模板项目启动最常见的错误Port 8080 was already in use。查看并释放端口的命令是netstat -ano | findstr :8080 taskkill /F /PID 进程号6.2 SpringBoot 版本过高导致的兼容性问题如果你创建项目时用了最新版 SpringBoot但不是模板自带的版本会遇到几个典型报错。第一个是javax.servlet不存在进入 SpringBoot 3.x 后所有涉及 Servlet API 的类都要改jakarta.servlet拦截器、Controller、过滤器四处都要动第二个是 MyBatis 版本不匹配控制台提示Invalid value type for attribute factoryBeanObjectType去把mybatis-spring-boot-starter升到 3.0 即可第三个是 JDK 版本不兼容导致编译失败SpringBoot 2.x 配合 JDK 17 会报cannot access class sun.misc之类的错误最省事的解决方案是把项目的 Java level 调回 8。6.3 从模板到生产系统验证码与权限模型的演进登录注册模板的尽头是真实项目首要增强点是加图形验证码。常见做法是引入kaptcha或hutool-captcha生成图片把验证码文本存入 Session登录时先校验验证码再校验密码。这只是防御脚本刷接口更严谨的做法是加登录失败次数限制连续失败 5 次锁定账号 15 分钟这个逻辑可以用一个ConcurrentHashMapString, AtomicInteger做内存版本也可以用 Redis 做分布式版本。权限模型方面模板通常只有登录与未登录的区别。后续演进路线是引入角色字段把user表加一个role列然后在拦截器中判断角色是否可以访问/admin/**路径。更完整的方案是接 Spring Security但那是另一个量级的复杂度只做内部后台的话拦截器加角色判断足够撑到几百用户量级。6.4 一个实用的 Session 超时处理技巧模板的登录态存在 Session 里默认超时时间是 30 分钟这个值在application.yml中可以调整server: servlet: session: timeout: 60m如果设置为0或负数Session 永不过期适合纯内网演示环境但生产环境不要这么干。为了在前端体验上感知超时最简单的方案是前端每隔一段时间发一个轻量请求刷新 Session 或探测在线状态这个请求路径可以排除在拦截器之外比如/heartbeat。定时调用的 JS 代码用setInterval15 分钟发一次即可。这个方法成本很低但能避免“写着写着突然 401 跳回登录页”的糟糕体验。至此一个基于 SpringBoot 与 Layui 的登录注册模板已经被完整拆解过一遍先理解组合存在的理由再看配置与数据库设计然后分别过一遍后端会话逻辑与前端表单交互最后落到启动验证与版本兼容性处理上。后续翻看其他同类模板时重点观察它如何处理统一返回体、如何做 Session 持久化、是否做好了静态资源放行——这三个点决定了这套代码是教学演示级还是可用工程级。本文还有配套的精品资源点击获取
返回列表