Spring Boot快速搭建一个简易商城项目【完成登录功能且优化】

完成登录且优化:
未优化做简单的判断:

全部异常抓捕
优化:返回的是json的格式

BusinessException:所有的错误放到这个容器中,全局异常从这个类中调用

BusinessException:
package com.lya.lyaspshop.exception;import com.lya.lyaspshop.resp.JsonResponseStatus; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor;@EqualsAndHashCode(callSuper = true)//自动生成equals和hashCode方法 @AllArgsConstructor//自动生成全参构造函数 @NoArgsConstructor//自动生成无参构造函数 @Data//自动生成getters、setters、toString public class BusinessException extends RuntimeException {// 所有的错误放到这个容器中,全局异常从这个类中调用private JsonResponseStatus jsonResponseStatus;}

GlobalExceptionHandler
package com.lya.lyaspshop.exception;import com.lya.lyaspshop.resp.JsonResponseBody; import com.lya.lyaspshop.resp.JsonResponseStatus; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice//用于声明这个类是一个全局异常处理器 @Slf4j public class GlobalExceptionHandler {// 已知错误@ExceptionHandler(BusinessException.class)//声明这是一个异常处理方法public JsonResponseBody<?> exceptionBusinessException(BusinessException e) {JsonResponseStatus status = e.getJsonResponseStatus();log.info(status.getMsg());//使用日志打印异常的消息。return JsonResponseBody.other(status);}// 未知错误@ExceptionHandler(Throwable.class)public JsonResponseBody<?> exceptionThrowable(Throwable e) {log.info(e.getMessage());//使用日志打印异常的消息。return JsonResponseBody.other(JsonResponseStatus.UN_KNOWN);} }
这里为啥要写这两个类:
理解:编写
GlobalExceptionHandler类可以集中处理应用程序中的各种异常,提高代码的可维护性,同时简化了代码


jsr303

//这里体现了为啥要建这个类:1.降低代码耦合度:VO 实体类可以将数据从数据库实体类中解耦 2.用户进行权限校验等操作,业务逻辑与数据访问层分离开来,提高代码的可读性和可维护性 如果我直接在数据库的实体类中去

<!-- jsr303 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>
实体:使用注解
@NotBlank

抛一个异常:

报错信息:

这个是时候错误已经该变:


连接日志查看:
遇到一个问题:这里就是异常就是使用的303自己带的异常,不要写其他的

前后台的加密过程:
从前台发送请求来:
引入加密js

<script src="http://www.gongjuji.net/Content/files/jquery.md5.js" type="text/javascript"></script>

加密成功:

加密后数据库的原密码肯定是不对的了。这我们使用debug给截取到密码存入数据库中。
集成redis

<!-- redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
RedisServiceImpl
package com.lya.lyaspshop.service.impl;import com.lya.lyaspshop.pojo.User; import com.lya.lyaspshop.service.IRedisService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service public class RedisServiceImpl implements IRedisService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 往redis设置@Overridepublic void setUserToRedis(String token, User user) {redisTemplate.opsForValue().set("user:" + token, user, 7200, TimeUnit.SECONDS);}@Overridepublic User getUserByToken(String token) {return (User) redisTemplate.opsForValue().get("user:" + token);}}
IRedisService
package com.lya.lyaspshop.service;import com.lya.lyaspshop.pojo.User;public interface IRedisService {/*** 将登陆User对象保存到Redis中,并以Token为键*/void setUserToRedis(String token, User user);/*** 根据token令牌获取redis中存储的user对象*/User getUserByToken(String token);}

redisService.setUserToRedis(token, one);
加入cookie
使用CookieUtils类:
package com.lya.lyaspshop.utils;import lombok.extern.slf4j.Slf4j;import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder;@Slf4j public class CookieUtils {/*** @Description: 得到Cookie的值, 不编码*/public static String getCookieValue(HttpServletRequest request, String cookieName) {return getCookieValue(request, cookieName, false);}/*** @Description: 得到Cookie的值*/public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {if (isDecoder) {retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");} else {retValue = cookieList[i].getValue();}break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** @Description: 得到Cookie的值*/public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue) {setCookie(request, response, cookieName, cookieValue, -1);}/*** @param request* @param response* @param cookieName* @param cookieValue* @param cookieMaxage* @Description: 设置Cookie的值 在指定时间内生效,但不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage) {setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);}/*** @Description: 设置Cookie的值 不设置生效时间,但编码* 在服务器被创建,返回给客户端,并且保存客户端* 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中* 如果没有设置,会默认把cookie保存在浏览器的内存中* 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, boolean isEncode) {setCookie(request, response, cookieName, cookieValue, -1, isEncode);}/*** @Description: 设置Cookie的值 在指定时间内生效, 编码参数*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, boolean isEncode) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);}/*** @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, String encodeString) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);}/*** @Description: 删除Cookie带cookie域名*/public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,String cookieName) {doSetCookie(request, response, cookieName, null, -1, false);}/*** @Description: 设置Cookie的值,并使其在指定时间内生效*/private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {try {if (cookieValue == null) {cookieValue = "";} else if (isEncode) {cookieValue = URLEncoder.encode(cookieValue, "utf-8");}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);log.info("========== domainName: {} ==========", domainName);if (!"localhost".equals(domainName)) {cookie.setDomain(domainName);}}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** @Description: 设置Cookie的值,并使其在指定时间内生效*/private static void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, String encodeString) {try {if (cookieValue == null) {cookieValue = "";} else {cookieValue = URLEncoder.encode(cookieValue, encodeString);}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);log.info("========== domainName: {} ==========", domainName);if (!"localhost".equals(domainName)) {cookie.setDomain(domainName);}}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** @Description: 得到cookie的域名*/private static String getDomainName(HttpServletRequest request) {String domainName = null;String serverName = request.getRequestURL().toString();if (serverName == null || serverName.equals("")) {domainName = "";} else {serverName = serverName.toLowerCase();serverName = serverName.substring(7);final int end = serverName.indexOf("/");serverName = serverName.substring(0, end);if (serverName.indexOf(":") > 0) {String[] ary = serverName.split("\\:");serverName = ary[0];}final String[] domains = serverName.split("\\.");int len = domains.length;if (len > 3 && !isIp(serverName)) {// www.xxx.com.cndomainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];} else if (len <= 3 && len > 1) {// xxx.com or xxx.cndomainName = "." + domains[len - 2] + "." + domains[len - 1];} else {domainName = serverName;}}return domainName;}public static String trimSpaces(String IP) {//去掉IP字符串前后所有的空格while (IP.startsWith(" ")) {IP = IP.substring(1, IP.length()).trim();}while (IP.endsWith(" ")) {IP = IP.substring(0, IP.length() - 1).trim();}return IP;}public static boolean isIp(String IP) {//判断是否是一个IPboolean b = false;IP = trimSpaces(IP);if (IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) {String s[] = IP.split("\\.");if (Integer.parseInt(s[0]) < 255)if (Integer.parseInt(s[1]) < 255)if (Integer.parseInt(s[2]) < 255)if (Integer.parseInt(s[3]) < 255)b = true;}return b;}}

自定义注解
根据@isNoblank去写:
这三行代码必须写的:
boolean require() default false;String expr() default "";String message() default "";
IsMobile
package com.lya.lyaspshop.core;import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target;import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME;@Documented @Constraint(validatedBy = {IsMobileConstraintValidator.class}) @Target({FIELD}) @Retention(RUNTIME) public @interface IsMobile {boolean require() default false;String expr() default "";String message() default "";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
IsMobileConstraintValidator
package com.lya.lyaspshop.core;import com.baomidou.mybatisplus.core.toolkit.StringUtils;import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext;/*** @author CloudJun*/ public class IsMobileConstraintValidator implements ConstraintValidator<IsMobile, String> {private boolean require;private String expr;@Overridepublic void initialize(IsMobile isMobile) {expr = isMobile.expr();require = isMobile.require();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (!require) return true;if (StringUtils.isEmpty(value)) return false;return value.matches(expr);}}


优化:定义一个常量类(使用的定值往这里调用就行)
package com.lya.lyaspshop.core;public abstract class Constants {// 常类public static final String EXPR_MOBILE = "(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}";public static final String EXPR_PASSWORD = "[a-zA-Z0-9]{32}";public static final String USER_TOKEN_PREFIX = "user:";}

相关文章:
Spring Boot快速搭建一个简易商城项目【完成登录功能且优化】
完成登录且优化: 未优化做简单的判断: 全部异常抓捕 优化:返回的是json的格式 BusinessException:所有的错误放到这个容器中,全局异常从这个类中调用 BusinessException: package com.lya.lyaspshop.exce…...
KG+LLM(一)KnowGPT: Black-Box Knowledge Injection for Large Language Models
论文链接:2023.12-https://arxiv.org/pdf/2312.06185.pdf 1.Background & Motivation 目前生成式的语言模型,如ChatGPT等在通用领域获得了巨大的成功,但在专业领域,由于缺乏相关事实性知识,LLM往往会产生不准确的…...
使用anaconda创建爬虫spyder工程
1.由于每个工程使用的环境都可能不一样,因此一个好的习惯就是不同的工程都创建属于自己的环境,在anaconda中默认的环境是base,我们现在来创建一个名为spyder的环境,专门用于爬虫工程: //括号中名字,代表当…...
网络通信(7)-TCP协议解析
目录 一、定义 二、主要特点 三、报文格式 四、工作方式...
win32 WM_MENUSELECT消息学习
之前写了一些win32的程序,处理菜单单击都是处理WM_COMMAND消息,通过 LOWORD(wParam) 获取菜单ID,判断单击的是哪个菜单项; 还有一些其他菜单消息; 当在菜单项中移动光标或鼠标,程序会收到许多WM_MENUSELEC…...
Java学习苦旅(十六)——List
本篇博客将详细讲解Java中的List。 文章目录 预备知识——初识泛型泛型的引入泛型小结 预备知识——包装类基本数据类型和包装类直接对应关系装包与拆包 ArrayList简介ArrayList使用ArrayList的构造ArrayList常见操作ArrayList遍历 结尾 预备知识——初识泛型 泛型的引入 我…...
python爬虫实现获取招聘信息
使用的python版本: 3.12.1 selenium版本:4.8.0 urllib版本:1.26.18 from selenium import webdriver from selenium.webdriver import ActionChains import timeimport re import xlwt import urllib.parsedef get_html(url):chrome_drive…...
模块电源(七):LDO 应用
1、Typical application circuit LDO 典型应用电路如下图所示: 2、High Output Current Positive Voltage Regulator 通过 PNP 型三极管 Tr1 ,可以提高 LDO 的输出电流能力,电路如下图所示: 设流过 R1 的电流为 ,当 …...
Redis命令---Hash(哈希)篇 (超全)
目录 1.Redis Hmset 命令 - 同时将多个 field-value (域-值)对设置到哈希表 key 中。简介语法可用版本: > 2.0.0返回值: 如果命令执行成功,返回 OK 。 示例 2.Redis Hmget 命令 - 获取所有给定字段的值简介语法可用版本: > 2.0.0返回值: 一个包含多个给定字段…...
Objects are not valid as a React child (found: object with keys {name}).
在jsx中可以嵌套表达式,将表达式作为内容的一部分,但是要注意,普通对象不能作为子元素;但是数组,react元素对象是可以的 如下:不能将stu这个对象作为子元素放 function App() {const myCal imgStyleconst…...
nodejs业务分层如何写后端接口
这里展示的是在node express 项目中的操作 ,数据库使用的是MongoDB,前期关于express和MongoDB的文章可访问: Nodejs后端express框架 server后端接口操作:通过路由匹配——>调用对应的 Controller——>进行 Service调用——&…...
Windows 7 虚拟机的安装以及解决安装VMVMware tools问题
1.Windows 7 虚拟机的安装以及解决安装VMVMware tools问题 参考:Windows 7 虚拟机的安装以及解决安装VMVMware tools问题 注意:下载官方补丁:Microsoft Update Catalog在智慧联想浏览器中打不开,要在火狐中才能打开下载。 2.win7如…...
[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-9PID控制器
本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-9PID控制器) P —— Proportional I —— Integral D —— Derivative 当前误差/过去误差/误差的变化趋势 K p ⋅ e K_{\mathrm{p}}\cdot e Kp⋅e:比…...
SSM养老院综合服务系统----计算机毕业设计
项目介绍 该项目为后台管理项目,分为管理员与护工两种角色; 管理员角色包含以下功能: 管理员登录,个人资料密码管理,用户管理,床位类型管理,床位管理,护工管理,老人管理,咨询登记管理,预约登记管理,老人健康信息管理,费用管理等功能。 护…...
广州求职招聘(找工作)去哪里找比较好
在广州找工作,可以选择“吉鹿力招聘网”这个平台。它是一个号称直接和boss聊的互联网招聘神器,同时,“吉鹿力招聘网”作岗位比较齐全,企业用户也多,比较全面。在“吉鹿力招聘网”历即可投递岗位。 广州找工作上 吉鹿力…...
ARM NEON 指令
NEON指令 按照操作数类型可以分为正常指令、宽指令、窄指令、饱和指令、长指令。 正常指令:生成大小相同且类型通常与操作数向量相同到结果向量。长指令:对双字向量操作数执行运算,生产四字向量到结果。所生成的元素一般是操作数元素宽度到…...
Open3D 最小二乘拟合平面——拉格朗日乘子法
目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接Open3D 最小二乘拟合平面——拉格朗日乘子法。爬虫自重。 一、算法原理 设拟合出的平面方程为: a x + b y +...
Rockchip平台Android应用预安装功能(基于Android13)
Rockchip平台Android应用预安装功能(基于Android13) 1. 预安装应用类型 Android上的应用预安装功能,主要是指配置产品时,根据厂商要求,将事先准备好的第三方应用预置进Android系统。预安装分为以下几种类型: 安装不可卸载应用安…...
vue项目表单使用正则过滤ip、手机号
import useFormValidate from /hooks/useFormValidatesetup(props, { emit }) {const { validateName, validateIPAndPort } useFormValidate()const state reactive({workFaceInfo: props.info?.id ? props.info : {},sysTypeData: props.sysType,formRules: {name: [{req…...
vscode编译调试sln工程
使用msvc工具链 vscode配置调用visual studio的编译和调试环境_vscode用vs-CSDN博客 将vscode打造无敌的IDE(14) tasks.json和launch.json配置详解,随心所欲添加自动化任务_tasks.json详解-CSDN博客 通过命令行使用 Microsoft C 工具集 | Microsoft Learn 编译…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
02-性能方案设计
需求分析与测试设计 根据具体的性能测试需求,确定测试类型,以及压测的模块(web/mysql/redis/系统整体)前期要与相关人员充分沟通,初步确定压测方案及具体的性能指标QA完成性能测试设计后,需产出测试方案文档发送邮件到项目组&…...
前端打包工具简单介绍
前端打包工具简单介绍 一、Webpack 架构与插件机制 1. Webpack 架构核心组成 Entry(入口) 指定应用的起点文件,比如 src/index.js。 Module(模块) Webpack 把项目当作模块图,模块可以是 JS、CSS、图片等…...
TMC2226超静音步进电机驱动控制模块
目前已经使用TMC2226量产超过20K,发现在静音方面做的还是很不错。 一、TMC2226管脚定义说明 二、原理图及下载地址 一、TMC2226管脚定义说明 引脚编号类型功能OB11电机线圈 B 输出 1BRB2线圈 B 的检测电阻连接端。将检测电阻靠近该引脚连接到地。使用内部检测电阻时,将此引…...
