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.rhkb.cn/news/82201.html

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

相关文章

clickhouse断电重启故障解决方案

业务场景 公司的一个日志系统用到了clickhouse。一线运维反映说有个生产环境因为异常断电造成服务器重启。在执行日志系统的启动脚本时&#xff0c;一直报clickhouse启动不起来&#xff0c;日志系统无法使用。 问题排查 通过阅读启动脚本代码&#xff0c;以及启动日志系统&a…

Android 项目导入高德SDK初次上手

文章目录 一、前置知识&#xff1a;二、学习目标三、学习资料四、操作过程1、创建空项目2、高德 SDK 环境接入2.1 获取高德 key2.2下载 SDK 并导入2.2.1、下载SDK 文件2.2.2、SDK 导入项目2.2.3、清单文件配置2.2.4、隐私权限 3、显示地图 一、前置知识&#xff1a; 1、Java 基…

关于在c++中使用数组名作为函数参数,或者使用数组名的地址作为函数参数问题的一些研究

前言 使用数组名作为函数参数&#xff0c;或者使用数组名的地址作为函数参数&#xff0c;常常出现于对于字符串的读入问题之中。 常有以下两种写法&#xff1a; 这是使用数组名作为函数参数 #include<cstdio> char s[100]; int main() {scanf("%s",s); }在…

【如何构建自己的基于Arduino的Scara 机器人】

【如何构建自己的基于Arduino的Scara 机器人】 1. 概述2. Scara机器人3D模型3. 3D打印机器人零件4. 组装机器人5. SCARA机器人电路图6. 完成装配7. SCARA机器人的工作原理8. 对 SCARA 机器人进行编程 – Arduino 和处理代码9. 总结在本教程中,我们将学习如何构建基于 Arduino …

导出LLaMA ChatGlm2等LLM模型为onnx

通过onnx模型可以在支持onnx推理的推理引擎上进行推理&#xff0c;从而可以将LLM部署在更加广泛的平台上面。此外还可以具有避免pytorch依赖&#xff0c;获得更好的性能等优势。 这篇博客&#xff08;大模型LLaMa及周边项目&#xff08;二&#xff09; - 知乎&#xff09;进行…

leetcode 399-除法求值

法一&#xff1a;并查集 分析示例1&#xff1a; a / b 2.0 a/ b 2.0 a/b2.0&#xff0c;说明 a 2 b a2b a2b&#xff0c; a a a和 b b b在同一个集合中 b / c 3.0 b/c3.0 b/c3.0&#xff0c;说明 b 3 c b3c b3c&#xff0c; b b b和 c c c在同一个集合中 求 a / c a/…

微服务——ES实现自动补全

效果展示 在搜索框根据拼音首字母进行提示 拼音分词器 和IK中文分词器一样的用法&#xff0c;按照下面的顺序执行。 # 进入容器内部 docker exec -it elasticsearch /bin/bash# 在线下载并安装 ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch…

【C++】红黑树的原理与实现

文章目录 一、引言 二、红黑树的概念与性质 2、1 红黑树的概念 2、2 红黑树的性质 三、红黑树的定义与实现 3、1 红黑树的定义 3、2 插入新节点 3、2、1 默认插入红色节点 3、3 插入情况分类 3、3、1 情况一&#xff08;根据颜色向上调整&#xff09; 3、3、2 情况二&#xff0…

AIGC技术到底是什么?为什么这么火热?

AIGC技术到底是什么&#xff1f;为什么这么火热&#xff1f; ALCG技术到底是什么&#xff1f;AIGC技术的发展史AIGC技术特点AIGC技术主要用途ALGC技术未来发展 ALCG技术到底是什么&#xff1f; AIGC&#xff08;Artificial Intelligence in Game Creation&#xff09;技术是指…

OpenLayers实战,OpenLayers实现气象台风飓风运动轨迹运动动画,可调台风旋转速度和运动速度,静态图片旋转动画

专栏目录: OpenLayers实战进阶专栏目录 前言 本章使用OpenLayers实现气象中常用的台风或者飓风运动轨迹动画,支持调整台风图标旋转速度和运动速度。 不同的台风可以设置不同的运动速度和旋转速度,也可以通过变量控制图片不旋转。 本章图片使用静态png图片,并非gif动态图。…

中间件多版本冲突的4种解决方案和我们的选择

背景 在小小的公司里面&#xff0c;挖呀挖呀挖。最近又挖到坑里去了。一个稳定运行多年的应用&#xff0c;需要在里面支持多个版本的中间件客户端&#xff1b;而多个版本的客户端在一个应用里运行时会有同名类冲突的矛盾。在经过询问chatGPT&#xff0c;百度&#xff0c;googl…

在Python中定义Main函数

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 许多编程语言都有一个特殊的函数&#xff0c;当操作系统开始运行程序时会自动执行该函数。 这个函数通常被命名为main()&#xff0c;并且依据语言标准具有特定的返回类型和参数。 另一方面&#xff0c;Python解释器从文件…

SQL-每日一题【1179. 重新格式化部门表】

题目 部门表 Department&#xff1a; 编写一个 SQL 查询来重新格式化表&#xff0c;使得新的表中有一个部门 id 列和一些对应 每个月 的收入&#xff08;revenue&#xff09;列。 查询结果格式如下面的示例所示&#xff1a; 解题思路 1.题目要求我们重新格式化表&#xff0c;…

Sentieon | 应用教程: 关于读段组的建议

介绍 本文档描述了使用Sentieon Genomics软件时&#xff0c;推荐使用RGID字段以最小化潜在问题的用法。 本文档能帮助您确定设置所使用的bam文件中RG标签的不同字段的最佳实践方法。 RG字段及其用法的详细描述 RG字段的详细描述 SAM格式规范http://samtools.github.io/hts-…

Android Studio实现刮刮卡效果

代码和刮刮乐图片参考网络 实现效果 MainActivity import android.app.Activity; import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentV…

教雅川学缠论06-中枢

本系列文章之前讲的内容都只有上升和下降两类趋势&#xff0c;并没有提及盘整&#xff0c;在缠论中&#xff0c;中枢这个新词汇用来定义盘整&#xff0c;中枢&#xff1a; 1.至少由5条线段&#xff08;或笔&#xff09;组成 2.中枢是有方向的&#xff0c;中枢左右两侧外面的线&…

【locust】使用locust + boomer实现对接口的压测

目录 背景 环境安装 脚本编写 master slave节点&#xff08;golang/boomer&#xff09; 问题 资料获取方法 背景 很早之前&#xff0c;考虑单机执行能力&#xff0c;使用locust做过公司短信网关的压测工作&#xff0c;后来发现了一个golang版本的locust&#xff0c;性能…

替换开源LDAP,某科技企业用宁盾目录统一身份,为业务敏捷提供支撑

客户介绍 某高科技企业成立于2015年&#xff0c;是一家深耕于大物流领域的人工智能公司&#xff0c;迄今为止已为全球16个国家和地区&#xff0c;120余家客户打造智能化升级体验&#xff0c;场景覆盖海陆空铁、工厂等货运物流领域。 该公司使用开源LDAP面临的挑战 挑战1 开源…

01《Detecting Software Attacks on Embedded IoT Devices》随笔

2023.08.05 今天读的是一篇博士论文 论文传送门&#xff1a;Detecting Software Attacks on Embedded IoT Devices 看了很长时间&#xff0c;发现有一百多页&#xff0c;没看完&#xff0c;没看到怎么实现的。 摘要 联网设备的增加使得嵌入式设备成为各种网络攻击的诱人目标&…

c#设计模式-创建型模式 之 工厂模式

前言&#xff1a; 工厂模式&#xff08;Factory Pattern&#xff09;是一种常用的对象创建型设计模式。该模式的主要思想是提供一个创建对象的接口&#xff08;也可以是抽象类、静态方法等&#xff09;&#xff0c;将实际创建对象的工作推迟到子类中进行。这样一来&#xff0c…