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 编译…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
