SSM个人博客项目

文章目录

  • SSM个人博客系统实现
    • 项目介绍
  • 一、准备工作
    • 0. 创建项目添加对应依赖
    • 1. 数据库设计
    • 2. 定时实体类
  • 二、功能实现
    • 1.统一功能处理
      • 统一返回格式
      • 统一异常处理
      • 定义登录拦截器
    • 2. 注册登录实现
      • 生成获取验证码
      • 密码加盐实现
      • 注册功能
      • 登录功能
      • 注销功能
    • 3.登录用户博客列表
      • 获取登录用户信息
      • 获取登录用户文章列表
    • 4.文章相关操作
      • 发布文章
      • 删除文章
      • 修改文章
      • 定时发布文章
      • 分页获取所有用户的文章
      • 文章详情
    • 5.文章草稿箱实现
      • 从草稿箱发布文章
      • 修改草稿
    • 6.个人信息修改
      • 头像修改
      • 基本信息修改
    • 7. 其它密码相关功能实现
      • 修改密码
      • 设置密保问题
      • 找回密码


SSM个人博客系统实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

项目介绍

本项目是一个前后端分离的个人博客系统,实现的主要功能有用户注册、用户登录、找回密码、验证码、文章的发布和删除、定时发布文章功能、草稿箱功能、文章列表分页功能、用户信息修改包括上传头像。利用SpingAOP实现了统一的登录验证、异常处理、统一返回格式。

一、准备工作

0. 创建项目添加对应依赖

在这里插入图片描述

从Maven仓库引入SprinAOP依赖和第三方Hutool依赖

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

1. 数据库设计

数据库一共有6张表,分别是用户表、文章表、文章草稿表、定时发布文章表、密保问题表

在这里插入图片描述

2. 定时实体类

实体类分别对应数据表

用户类

@Data
public class User {private int id;private String username;private String password;private String netName;private String photo;private String git;private Integer articleCount;private Date createTime;private Date updateTime;// 用户是否第一次登录private int state;
}

文章类

@Data
public class Article {private Integer id;private String title;private String content;private Date createTime;private Date updateTime;private Integer userId;private Integer visits;
}

文章草稿

@Data
public class Drafts {private Integer id;private String title;private String content;private Date createTime;private Date updateTime;private Integer userId;
}

定时发布的文章

@Data
public class TimingArticle {private Integer id;private String title;private String content;private Date postTime;private Integer userId;
}

密保问题

@Data
public class QuestionPassword {private Integer id;private String question1;private String question2;private String question3;private String answer1;private String answer2;private String answer3;private Integer userId;
}

二、功能实现

1.统一功能处理

使用SpringAOP可以实现统一功能的处理,统一的功能处理可以避免代码的冗余。

统一返回格式

先定义一个响应类,重载一些方法,success表示执行成功(正确的查询数据、登录成功等),fail表示执行失败(密码错误、参数非法的),重载可以非常灵活的让我们给前端返回数据。

@Getter
@Setter
public class Response {private int code;private String message;private Object data;public static Response success(Object data) {Response response = new Response();response.code = 200;response.message = "";response.data = data;return response;}public static Response success(String message) {Response response = new Response();response.message = message;response.code = 200;return response;}public static Response success(Object data,String message) {Response response = new Response();response.message = message;response.code = 200;response.data = data;return response;}public static Response fail(String message) {Response response = new Response();response.code = -1;response.message = message;return response;}public static Response fail(String message,int code) {Response response = new Response();response.code = code;response.message = message;return response;}
}

实现统一响应格式实现,统一的返回格式有利于后端统一标准规范,降低前后端的沟通成本。定义ResponseAdvice类实现ResponseBodyAdvice接口,并用@ControllerAdvice注解修饰表示该类为一个增强类

@ControllerAdvice
@ResponseBody
public class ResponseAdvice implements ResponseBodyAdvice {@Resourceprivate ObjectMapper mapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {// 如果是定义的返回格式就直接返回if (body instanceof Response) {return body;}if (body instanceof String) {// 转换成json根式的返回格式return mapper.writeValueAsString(Response.success(body));}return Response.success(body);}
}

最后的响应返回格式,后面的所有响应都是如此

正确执行的响应

{code:200data: "自定义的返回数据"message: "自定义的返回消息"
}

错误执行的响应

{code:-1data: ""message: "自定义的返回消息"
}

统一异常处理

统一的异常处理,服务器发送异常后我们可以通过增强类做统一处理后给前端返回响应,可以添加一些预计会发送的异常分别进行处理。

@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {@ExceptionHandler(Exception.class)public Object handler(Exception e) {return Response.fail("服务器异常",500);}
}

定义登录拦截器

定义登录拦截器,可以避免大量登录验证的代码冗余,让指定的接口统一验证。

/*** 自定义登录拦截器*/
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if (session != null && session.getAttribute(Constant.SESSION) != null) {response.setStatus(200);return true;}// 重定向到登录页面response.sendRedirect("/login.html");return false;}
}

添加自定义拦截器,如果某些接口被拦截器拦截就需要经过拦截器验证后才能去执行对应的Controller方法,也就是需要登录后才能使用。

@Configuration
public class AppConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()) // 添加自定义拦截器.addPathPatterns("/**") //拦截所有接口.excludePathPatterns("/user/login") //排除的接口.excludePathPatterns("/user/reg").excludePathPatterns("/user/getVerify").excludePathPatterns("/user/byIdUser").excludePathPatterns("/questionPass/getQuestion").excludePathPatterns("/questionPass/findPass").excludePathPatterns("/questionPass/setNewPass").excludePathPatterns("/article/byIdArticle").excludePathPatterns("/article/pagingGetArticle").excludePathPatterns("/login.html").excludePathPatterns("/blog_detailed.html").excludePathPatterns("/blog_list.html").excludePathPatterns("/find_pass.html").excludePathPatterns("/register.html").excludePathPatterns("/img/**").excludePathPatterns("/js/**").excludePathPatterns("/photo/**").excludePathPatterns("/css/**");}
}

2. 注册登录实现

生成获取验证码

使用第三方工具Hutool来绘制验证码,把生成的验证码保存到session中,通过响应把图片传递个前端

@GetMapping("/getVerify")
public Response getVerify(HttpSession session,HttpServletResponse response) {VerificationCodeUtil.getCode(session,response);return Response.success("ok");
}

密码加盐实现

通过密码加盐来保证用户密码的一定安全性

  • 加盐的实现思路为:用户注册把生成一个UUID拼接上用户前端传递的明文密码进行MD5,以#号分割再把生成的UUID拼接到#后面,生成最终密码存入数据库。
  • 解密思路:把数据库加密的代码查询出来,以#把UUID分割出来,把用户输入的明文密码以同样的方式拼接上分割出来的UUID,加盐后再和数据库查询的密码进行比对
public class PasswordUtil {/*** 密码加盐* @param password 明文密码* @return*/public static String passwordAddSalt(String password) {String uuid = UUID.randomUUID().toString().replace("-","");String finalPass = (DigestUtils.md5DigestAsHex((uuid+password).getBytes())+"#"+uuid);return finalPass;}/*** 验证密码* @param password 明文密码* @param finalPass 加密密码* @return*/public static boolean check(String password,String finalPass) {String uuid = finalPass.split("#")[1];String pass = (DigestUtils.md5DigestAsHex((uuid+password).getBytes())+"#"+uuid);return pass.equals(finalPass);}
}

注册功能

在这里插入图片描述

约定前后端交互

请求:

{username : "用户名",password : "密码",confirmPass :"确认密码",verifyCode : "验证码"
}

响应:

{"code":200,"message":"注册成功","data":null
}

先简单的数据校验,密码加盐,再生成随机的网名,注意网名和用户名要区分,考虑到用户名已经存在问题。

@PostMapping("/reg")
public Response reg(String username,String password,String confirmPass,HttpSession session,String verifyCode) {if (verifyCode == null || "".equals(verifyCode.trim()) || !VerificationCodeUtil.check(session,verifyCode)) {return Response.fail("验证码错误");}if (username == null || password == null || confirmPass == null || "".equals(username.trim())|| "".equals(password.trim()) || "".equals(confirmPass.trim())) {return Response.fail("用户名密码非法");}if (!password.equals(confirmPass)) {return Response.fail("两次密码输入不一致");}User user = userService.byNameUser(username);if (user != null) {return Response.fail("用户已经被注册");}// 密码加盐String finalPass = PasswordUtil.passwordAddSalt(password);String netName = "用户"+System.currentTimeMillis()%1000000;int ret = userService.reg(username,finalPass,netName);if (ret == 1) {return Response.success("注册成功");}return Response.fail("用户名密码非法");
}

登录功能

在这里插入图片描述

约定前后端交互

请求:

{username:"用户名",password:"密码",inputVerify="验证码"
}

响应:

{"code":200,"message":"登录成功/用户名密码错误/验证码错误","data":null
}

登录成功后把用户信息保存session会话,前端跳转到博客列表页

@PostMapping("/login")
public Response login(String username, String password,String inputVerify ,HttpSession httpSession,HttpServletRequest request) {if (inputVerify == null || "".equals(inputVerify.trim()) || !VerificationCodeUtil.check(httpSession,inputVerify)) {return Response.fail("验证码错误");}if (username == null || password == null || "".equals(username.trim()) || "".equals(password.trim())) {return Response.fail("用户名密码错误");}User user = userService.byNameUser(username);if (user != null && PasswordUtil.check(password,user.getPassword())) {// 把密码置为空user.setPassword("");user.setUsername("");HttpSession session = request.getSession(true);session.setAttribute(Constant.SESSION,user);return Response.success("登录成功");}return Response.fail("用户名密码错误");
}

注销功能

删除session会话即可

@PostMapping("/logout")
public Response login(HttpServletRequest request) {User user = LogInUserInfo.getUserInfo(request);if (user == null) {return Response.fail("当前未登录");}request.removeAttribute(Constant.SESSION);return Response.success("注销成功");
}

3.登录用户博客列表

登录成功后跳转到博客列表页面,需要获取当前用户的所有博客信息,和当前用户的信息。
在这里插入图片描述

获取登录用户信息

用户登录后通过用户的id来查询到用户的信息,查询到后要把用户名和密码一些敏感信息给影响。

请求:

"http://127.0.0.1:7070/usre/getUserInfo"

响应:

{"code":200,"message":"","data":{"id":1,"username":"","password":"","netName":"用户11326","photo":"../img/logo.jpg","git":"https://gitee.com/he-hanyu","articleCount":0,"createTime":null,"updateTime":null,"state":0}
}

主意通过用户的state字段判断用户是否第一次登录,如果是第一次登录就在前端提示用户设置密保问题。
在这里插入图片描述

@GetMapping("/getUserInfo")
public Response getUserInfo(HttpServletRequest request) {User user = LogInUserInfo.getUserInfo(request);User myUser = userService.byIdUser(user.getId());myUser.setPassword("");myUser.setUsername("");// 判断是否是第一次登录if (myUser.getState() == 1) {//如果是第一登录就修改状态userService.updateState(user.getId());}return Response.success(myUser);
}

获取登录用户文章列表

请求:

"http://127.0.0.1:7070/user/getUserArticle"

响应:

{"code":200,"message":"","data":[{"id":1,"title":"测试","content":"#Hh宇的个人博客","createTime":"2023-08-06","updateTime":null,"userId":1,"visits":0}]
}

通过用户id来查看当前用户的博客,显示博客是博客列表要注意把文章内荣进行一个截取

@GetMapping("/getUserArticle")
public Response getUserArticle(HttpServletRequest request) {User user = LogInUserInfo.getUserInfo(request);List<Article> articleList = articleService.getUserArticle(user.getId());for (Article article : articleList) {if (article.getContent().length() > 100) {article.setContent(article.getContent().substring(0,100)+"......");}}return Response.success(articleList);
}

在这里插入图片描述

博客列表有对应的查看文章响应,修改文章,删除文章。

4.文章相关操作

在这里插入图片描述

发布文章

发布文章校验标题和正文是否为空,校验完毕后给数据表添加信息。

约定前后端交互:

请求:

{title: "文章标题",content: "文章正文"
}

响应:

{"code":200,"message":"发布成功","data":null
}
@PostMapping("/addArticle")
public Response addArticle(String title,String content,HttpServletRequest request) {if (title == null || content == null || "".equals(title.trim()) || "".equals(content.trim())) {return Response.fail("发布失败");}User user = LogInUserInfo.getUserInfo(request);int row = articleService.addArticle(title,content,user.getId());if (row == 1) {userService.articleCountAuto(user.getId(),1);return Response.success("发布成功");}return Response.fail("发布失败");
}

删除文章

点击登录用户文章列表后通过博客id来删除文章,文章id由前端在生成链接时拼接在querystr中。点击删除链接,就会获取到文章Id给后端发送删除请求。且删除时验证该文章是否属于当前登录用户。
在这里插入图片描述

请求:

POST http://127.0.0.1:7070/article/idDeleteArticle
{articleId : 文章Id
}
@PostMapping("/idDeleteArticle")
public Response idDeleteArticle(Integer articleId,HttpServletRequest request) {if (articleId == null || articleId <= 0) {return Response.fail("删除失败");}User user = LogInUserInfo.getUserInfo(request);int ret = articleService.idDeleteArticle(articleId,user.getId());if (ret == 1) {userService.articleCountAuto(user.getId(),-1);return Response.success("删除成功");}return Response.fail("删除失败");
}

修改文章

点击修改文章后,会在url里拼接上一个文章id进入博客编辑页面,再次点击发布博客后会判断url中的querystr中是否有文章Id如果有说明是修改博客。
在这里插入图片描述
在这里插入图片描述

获取博客请求也就是通过querystr中的id查询博客:

GET http://127.0.0.1:7070/article/byIdArticle?articleId=2 

修改博客请求:

POST http://127.0.0.1:7070/article/updateArticle HTTP/1.1
{title: "修改后的标题",content: "修改后的正文"
}

响应:

{"code":200,"message":"修改成功","data":null
}
@PostMapping("/updateArticle")
public Response updateArticle(String title,String content,Integer articleId,HttpServletRequest request) {if (title == null || content == null || "".equals(title.trim()) || "".equals(content.trim())|| articleId == null || articleId <= 0) {return Response.fail("内容非法");}User user = LogInUserInfo.getUserInfo(request);int ret = articleService.updateArticle(title,content,articleId,user.getId());if (ret == 1) {return Response.success("修改成功");}return Response.fail("修改失败");
}

定时发布文章

在这里插入图片描述

定时发布文章前端给后端传递格式化的时间,后端再装换成和数据库对应的Date时间存,把待发布文章存入数据库,通过Spring的定时任务,每5秒扫描一下定时任务数据表,如果当前的大于发布时间就从数据库中获取到文章信息并进行发布,且删除对应的文章信息。

请求:

{title: "文章标题",content: "文章正文",postTime : "2023-08-06 16:28:26"
}

响应:

{"code":200,"message":"定时博客任务发布成功","data":null
}

需要通过正则判断前端传递的时间格式是否正确,且发布时间没有大于预期值。

@RestController
@RequestMapping("/timed")
public class TimedArticleController {@Autowiredprivate TimedArticleService timedArticleService;@SneakyThrows@PostMapping("/timingPost")public Response addTimedPost(String title, String content, String postTime, HttpServletRequest request) {if (title == null || content == null || postTime == null ||"".equals(title.trim()) || "".equals(content.trim()) || "".equals(postTime.trim())) {return Response.fail("内容非法");}// 校验时间格式是否正确if (DateTimeUtil.isValidDateTimeFormat(postTime)) {System.out.println(postTime);// 获取当前时间String time = DateUtil.now();// 判断当前时间和发布时间是否合法if (DateTimeUtil.getTimeDifferenceDay(time,postTime) > 7) {return Response.fail("发布时间不合法,请输入小于7天的发布时间");}SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = format.parse(postTime);User user = LogInUserInfo.getUserInfo(request);int ret = timedArticleService.addTimedPost(title,content,date,user.getId());if (ret == 1) {return Response.success("定时博客任务发布成功");}}return Response.fail("发布失败");}
}

在SpingBoot的启动类上添加注解开启定时任务,

@SpringBootApplication
@EnableScheduling
public class BlogApplication {public static void main(String[] args) {SpringApplication.run(BlogApplication.class, args);}}

再创建一个定时任务类每5秒扫描一下数据库

@Component
public class ScheduledRelease {@Autowiredprivate TimedArticleService timedArticleService;@Autowiredprivate ArticleService articleService;// 每5秒执行一次@Scheduled(fixedRate = 5000)@Transactionalpublic void scheduledTak() {Date date = new Date();List<TimingArticle> timingArticles = timedArticleService.getPostArticle(date);for(TimingArticle article : timingArticles) {articleService.addArticle(article.getTitle(),article.getContent(),article.getUserId());// 发布后立马删除记录timedArticleService.idDelete(article.getId());}}
}

需要注意的是查询时间对比sql要先把时间转换为时间戳

<select id="getPostArticle" resultMap="TimingArticleInfo">select * from timed_article where unix_timestamp(#{time}) > unix_timestamp(post_time);</select>

分页获取所有用户的文章

所有用户的文章数量比较多,为了减小服务器压力,所以采用分页。一页显示5篇文章。每次进入博客列表页,后端会先查询数据库中的文章数量通过公式计算出一共有多少页,再把最大页数和当前页的文章传递给前端,前端默认获取第一页的文章。
分页公式为文章总数*1.0/每页显示数量向上取整算出页数,前端传递的(页数-1)*每页显示数量求出从数据表中哪一条数据开始查询。

请求:

GET http://127.0.0.1:7070/article/pagingGetArticle?pageNumber=1&size=5 HTTP/1.1

响应:

{
"code":200,
"message":"",
"data":{"pageCount":2,"articles":[{"id":6,"title":"再来一篇文章","content":"#Hh宇的个人博客","createTime":"2023-08-	08","updateTime":null,"userId":2,"visits":0},]}
}

后端代码

/*** 分页获取所有用户博客* @param pageNumber 页数* @param size 每页显示多少篇文章* @return*/@GetMapping("/pagingGetArticle")public Response pagingGetArticle(Integer pageNumber,Integer size) {if (pageNumber < 1) {pageNumber = 1;}// 获取所有文章数量int articleCount = articleService.getArticleCount();// 计算出每页size个最多有多少页int pageCount = (int)(Math.ceil(articleCount*1.0/size));// 公式计算步长int offset = (pageNumber-1)*size;List<Article> articles = articleService.pagingGetArticle(size,offset);System.out.println(articles);HashMap<String,Object> result = new HashMap<>();result.put("pageCount",pageCount);result.put("articles",articles);return Response.success(result);}

在这里插入图片描述

文章详情

点击查看全文即可查看文章详情,文章详情里展示了文章标题、文章发布时间、文章的浏览量和文章正文。左边显示的是当前文章的作者信息。先通过querystr中的的文章id查询到文章,再同过文章里返回的当前用户id,来查询到当前文章作者对应的信息。

在这里插入图片描述

请求:

GET http://120.25.124.200:7070/article/byIdArticle?articleId=5 HTTP/1.1

响应:

{
"code":200,
"message":"",
"data":{"id":5,"title":"文章分页","content":"#Hh宇的个人博客","createTime":"2023-08-08","updateTime":null,"userId":2,"visits":0}
}

5.文章草稿箱实现

在这里插入图片描述

文章草稿包存草稿箱和发布文章类似,点击保存草稿后就把文章保存到草稿箱当中,并更新数据表信息,可以从草稿箱中发布博客,也可以修改草稿箱里的草稿文章或者删除草稿。

需要注意的是修改草稿,点击修改草稿后,通过url中的querystr来查询到对应的草稿

GET http://127.0.0.1:7070/blog_editor.html?draftId=1 HTTP/1.1

响应:

{"code":200,"message":"","data":{"id":1,"title":"这是一篇草稿","content":"#Hh宇的个人博客","createTime":null,"updateTime":null,"userId":1}
}

从草稿箱发布文章

因为发布文章和草稿共用的是一个页面,所以发布文章的时候,需要通过url中的querystr的id来判断是普通发布文章还是从草稿箱中发布文章。从草稿箱发布完文章后,就删除草稿数据报中的文章。
在这里插入图片描述

请求:

POST http://127.0.0.1:7070/articleDraft/postArticle HTTP/1.1
{title:"草稿标题"content:"草稿正文",draftId : 1
}

响应:

{"code":200,"message":"发布成功","data":null
}
@PostMapping("/postArticle")
public Response postArticle(String title,String content,Integer draftId,HttpServletRequest request) {if (title == null || content == null || "".equals(title.trim()) || "".equals(content.trim()) || draftId <= 0) {return Response.fail("发布失败");}User user = LogInUserInfo.getUserInfo(request);int ret = articleService.addArticle(title,content,user.getId());// 发布成功就删除草稿if (ret == 1) {int row = articleDraftService.idDeleteDrafts(draftId,user.getId());if (row == 1) {return  Response.success("发布成功");}}return Response.fail("发布失败");
}

修改草稿

如果是修改草稿,也就是点击编辑草稿后再次点击保存草稿,此时就是修改草稿了,而不是保存草稿,同样是在前端通过querystr区分。

请求:

POST http://127.0.0.1:7070/articleDraft/updateDraft HTTP/1.1
{draftId : 2,title : "标题",content : "正文"
}

响应:

{"code":200,"message":"保存成功","data":null
}
/*** 修改草稿* @param title* @param content* @param draftId* @return*/
@PostMapping("/updateDraft")
public Response updateDraft(String title,String content,Integer draftId,HttpServletRequest request) {if (title == null || content == null || "".equals(title.trim()) || "".equals(content.trim())|| draftId == null ||draftId < 1) {return Response.fail("内容非法");}User user = LogInUserInfo.getUserInfo(request);int ret = articleDraftService.updateDraft(title,content,draftId,user.getId());if (ret == 1) {return Response.success("保存成功");}return Response.success("保存失败");
}

6.个人信息修改

在这里插入图片描述

头像修改

前端传递头像,后端校验一下文件格式,生成唯一的文件名,把头像路径更新到对应的用户数据库。SpingBoot对文件上传大小有默认限制,我们只需处理对应的异常即可。

@SneakyThrows
@PostMapping("/updatePhoto")
public Response updatePhoto(@RequestPart("photo")MultipartFile imgFile, HttpServletRequest request,HttpServletResponse response) {// 设置重定向response.setStatus(302);response.sendRedirect("/user_blog_list.html");if (imgFile != null && !imgFile.isEmpty()) {String fileName = imgFile.getOriginalFilename();if (fileName.contains(".jpg") || fileName.contains(".png")) {// 1.生成唯一文件名String newFileName = UUID.randomUUID().toString().replaceAll("-","")+fileName.substring(fileName.length()-4);// 路径,文件名File file = new File(photoPath,newFileName);//保存文件imgFile.transferTo(file);User user = LogInUserInfo.getUserInfo(request);int ret = userService.updatePhoto(user.getId(),Constant.PHOTO_UPLOAD_PATH+newFileName);if (ret == 1) {return Response.success("修改成功");}}}return Response.fail("修改失败");
}

基本信息修改

修改网名和gitee连接

请求:

POST http://127.0.0.1:7070/user/updateUserInfo HTTP/1.1
{netName:"网名","gitee": "gitee链接"
}

响应:

{"code":200,"message":"修改成功","data":null
}
@PostMapping("/updateUserInfo")
public Response updateUserInfo(String netName,String git,HttpServletRequest request) {if (netName == null || git == null || "".equals(netName.trim()) || "".equals(git.trim())|| (!git.contains("https://"))) {return Response.fail("参数非法或git链接非法");}User user = LogInUserInfo.getUserInfo(request);int ret = userService.updateUserInfo(netName,git,user.getId());if (ret == 1) {return Response.success("修改成功");}return Response.fail("修改失败");
}

7. 其它密码相关功能实现

在这里插入图片描述

修改密码

修改密码比较简单,用户登录后,输入原密码和新密码,后端通过解密方式验证。验证通过即可修改密码。

@PostMapping("/updatePassword")
public Response updatePassword(String password,String newPassword,String confirmPassword,HttpServletRequest request) {if (password ==null || newPassword == null || confirmPassword == null ||"".equals(password.trim()) || "".equals(newPassword.trim()) || "".equals(confirmPassword.trim())) {return Response.fail("修改失败");}User user = LogInUserInfo.getUserInfo(request);User myUser = userService.byIdUser(user.getId());if (PasswordUtil.check(password,myUser.getPassword())) {String finalPass = PasswordUtil.passwordAddSalt(newPassword);int ret = userService.updatePassword(finalPass,user.getId());if (ret == 1) {HttpSession session = request.getSession(false);session.removeAttribute(Constant.SESSION);return Response.success("修改成功");}}return Response.fail("修改失败,密码错误");
}

设置密保问题

在这里插入图片描述

在用户第一登录的时候提示用户设置密保问题,通过密保问题即可找回密码。

给定3个问题和指定的问题选项,让用户输入答案,和用户密码进行设置密码问题。再对答案进行md5加密存入数据库。

请求:

POST http://127.0.0.1:7070/questionPass/addQuestionPassword HTTP/1.1
{password:hhy,question1=你最相信的人的姓名是,question2=你的出生地是,question3=你最喜欢吃的水果是,answer1=某某,answer2=湖南,answer3=葡萄
}

响应:

{"code":200,"message":"密保问题设置成功","data":null
}

找回密码

通过用户设置的密保问题来找回密码,用户输入查找的用户名来获取对应的密保问题,再输入答案后,对答案进行md5同问题一起比对验证,端验证正确后,再给用户输入新密码,输入新密码后再次提交。修改密码前再次验证一下密保问题,如果验证通过即可修改密码。
在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/29046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习笔记之优化算法(十)梯度下降法铺垫:总体介绍

机器学习笔记之优化算法——梯度下降法铺垫&#xff1a;总体介绍 引言回顾&#xff1a;线搜索方法线搜索方法的方向 P k \mathcal P_k Pk​线搜索方法的步长 α k \alpha_k αk​ 梯度下降方法整体介绍 引言 从本节开始&#xff0c;将介绍梯度下降法 ( Gradient Descent,GD ) …

Git stash命令

Git stash Git stash用来暂存当前正在进行的工作&#xff0c; 将工作区还没加入索引库的内容压入本地的Git栈中&#xff0c;在需要应用的时候再弹出来。比如想pull 最新代码&#xff0c;又不想加新commit&#xff1b;或者为了修复一个紧急的bug&#xff0c;先stash&#xff0c…

Vue_02:详细语法以及代码示例 + 知识点练习 + 综合案例(第二期)

2023年8月4日15:25:01 Vue_02_note 在Vue中&#xff0c;非相应式数据&#xff0c;直接往实例上面挂载就可以了。 01_Vue 指令修饰符 什么是指令修饰符呢&#xff1f; 答&#xff1a; 通过 " . " 指明一些指令后缀&#xff0c;不同 后缀 封装了不同的处理操作 —…

基于差分进化优化随机森林的多分类预测,基于DA-RF的分类预测

目录 背影 摘要 随机森林的基本定义 随机森林实现的步骤 差分进化算法原理 差分算法主要参数 差分算法流程图 差分算法优化测试函数代码 完整代码包含数据下载链接: https://download.csdn.net/download/abc991835105/88179071 基于差分进化算法改进的bp神经网络的心理状况评估…

SpringCloud Gateway获取请求响应body大小

前提 本文获取请求、响应body大小方法的前提 : 网关只做转发逻辑&#xff0c;不修改请求、相应的body内容。 SpringCloud Gateway内部的机制类似下图&#xff0c;HttpServer&#xff08;也就是NettyServer&#xff09;接收外部的请求&#xff0c;在Gateway内部请求将会通过Htt…

RISC-V基础之函数调用(四)非叶函数调用(包含实例)

叶函数是指不调用其他函数&#xff0c;也不改变任何非易失性寄存器的函数2。叶函数通常是一些简单的操作&#xff0c;如数学运算或逻辑判断。叶函数的特点是可以通过模拟返回来展开&#xff0c;即不需要保存或恢复寄存器的状态。 非叶函数是指调用其他函数或改变非易失性寄存器…

电力巡检无人机助力迎峰度夏,保障夏季电力供应

夏季是电力需求量较高的时期&#xff0c;随着高温天气的来临&#xff0c;风扇、空调和冰箱等电器的使用量也大大增加&#xff0c;从而迎来夏季用电高峰期&#xff0c;电网用电负荷不断攀升。为了保障夏季电网供电稳定&#xff0c;供电公司会加强对电力设施设备的巡检&#xff0…

ATF(TF-A)安全通告汇总

目录计划如下&#xff0c;相关内容补充中&#xff0c;待完成后进行超链接&#xff0c;敬请期待&#xff0c;欢迎您的关注 1、Advisory TFV-1 (CVE-2016-10319) 2、Advisory TFV-2 (CVE-2017-7564) 3、Advisory TFV-3 (CVE-2017-7563) 4、Advisory TFV-4 (CVE-2017-9607) 5、Ad…

❤ vue3中的RouteRecordRaw

❤ vue3中的RouteRecordRaw RouteRecordRaw 是 Vue Router 4.x 中新增的类型&#xff0c;用于定义路由配置。它是一个 TypeScript 类型 import { RouteRecordRaw } from vue-routerconst routes: Array<RouteRecordRaw> [{path: /,name: Home,component: () > impo…

opencv基础-34 图像平滑处理-2D 卷积 cv2.filter2D()

2D卷积是一种图像处理和计算机视觉中常用的操作&#xff0c;用于在图像上应用滤波器或卷积核&#xff0c;从而对图像进行特征提取、平滑处理或边缘检测等操作。 在2D卷积中&#xff0c;图像和卷积核都是二维的矩阵或数组。卷积操作将卷积核在图像上滑动&#xff0c;对每个局部区…

瑞数系列及顶像二次验证LOGS

瑞数商标局药监局专利局及顶像二次验证 日期&#xff1a;20230808 瑞数信息安全是一个专注于信息安全领域的公司&#xff0c;致力于为企业和个人提供全面的信息安全解决方案。他们的主要业务包括网络安全、数据安全、应用安全、云安全等方面的服务和产品。瑞数信息安全拥有一支…

Python爬虫之JsonPath详解

简介 JsonPath是一种信息抽取类库&#xff0c;是从JSON文档中抽取指定信息的工具 文档 官方文档 JSONPath - XPath for JSON 语法规则 $ 根节点 现行节点 .or[] 取子节点 n/a 取父节点&#xff0c;Jsonpath未支持 .. 就是不管位置&#xff0c;选择所有符…

【资料分享】全志科技T507工业核心板硬件说明书(一)

目 录 前言 1硬件资源 1.1CPU 1.2ROM 1.3RAM 1.4时钟系统 1.5电源 1.6LED

现在转行搞嵌入式找工作难不难啊?

对于应届生来说&#xff0c;嵌入式开发的经验不会有太多&#xff0c;所以要求也不会太高。 嵌入式开发常用的是C语言&#xff0c;所以需要你有扎实的功底&#xff0c;这一点很重要&#xff0c;数据结构算法&#xff0c;指针&#xff0c;函数&#xff0c;网络编程 有了上面的基…

web题型

0X01 命令执行 漏洞原理 没有对用户输入的内容进行一定过滤直接传给shell_exec、system一类函数执行 看一个具体例子 cmd1|cmd2:无论cmd1是否执行成功&#xff0c;cmd2将被执行 cmd1;cmd2:无论cmd1是否执行成功&#xff0c;cmd2将被执行 cmd1&cmd2:无论cmd1是否执行成…

源码分析——ConcurrentHashMap源码+底层数据结构分析

文章目录 1. ConcurrentHashMap 1.71. 存储结构2. 初始化3. put4. 扩容 rehash5. get 2. ConcurrentHashMap 1.81. 存储结构2. 初始化 initTable3. put4. get 3. 总结 1. ConcurrentHashMap 1.7 1. 存储结构 Java 7 中 ConcurrentHashMap 的存储结构如上图&#xff0c;Concurr…

winform控件 datagridview分页功能

主要实现页面跳转、动态改变每页显示行数、返回首末页、上下页功能&#xff0c;效果图如下&#xff1a; 主代码如下&#xff1a; namespace Paging {public partial class Form1 : Form{public Form1(){InitializeComponent();}private int currentPageCount;//记录当前页行数…

一个竖杠在python中代表什么,python中一竖代表什么

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;一个竖杠在python中代表什么&#xff0c;python中一竖代表什么&#xff0c;今天让我们一起来看看吧&#xff01; 维基百科页面是错误的&#xff0c;我已经更正了。|和&不是布尔运算符&#xff0c;即使它们是急切运算…

GODOT游戏引擎简介,包含与unity性能对比测试,以及选型建议

GODOT&#xff0c;是一个免费开源的3D引擎。本文以unity作对比&#xff0c;简述两者区别和选型建议。由于是很久以前写的ppt&#xff0c;技术原因视频和部分章节丢失了。建议当做业务参考。 GODOT目前为止遇到3个比较重大的基于&#xff0c;第一个是oprea的合作奖&#xff0c;…

13-把矩阵看作是对系统的描述

探索矩阵乘法&#xff1a;更深刻的理解与应用视角 &#x1f9e9;&#x1f50d; 引言 &#x1f4d6; 在我们进一步探讨矩阵乘法之前&#xff0c;让我们从不同的角度来理解什么是矩阵&#xff0c;以及如何将矩阵视为一个系统。我们之前已经介绍了矩阵的基本概念和运算&#xff…