本项目是一个前后端分离的个人博客系统,实现的主要功能有用户注册、用户登录、找回密码、验证码、文章的发布和删除、定时发布文章功能、草稿箱功能、文章列表分页功能、用户信息修改包括上传头像。利用SpingAOP实现了统一的登录验证、异常处理、统一返回格式。
从Maven仓库引入SprinAOP依赖和第三方Hutool依赖
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.16version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
数据库一共有6张表,分别是用户表、文章表、文章草稿表、定时发布文章表、密保问题表
实体类分别对应数据表
用户类
@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;
}
使用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 {
@Resource
private ObjectMapper mapper;
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@SneakyThrows
@Override
public 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:200
data: "自定义的返回数据"
message: "自定义的返回消息"
}
错误执行的响应
{
code:-1
data: ""
message: "自定义的返回消息"
}
统一的异常处理,服务器发送异常后我们可以通过增强类做统一处理后给前端返回响应,可以添加一些预计会发送的异常分别进行处理。
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public Object handler(Exception e) {
return Response.fail("服务器异常",500);
}
}
定义登录拦截器,可以避免大量登录验证的代码冗余,让指定的接口统一验证。
/**
* 自定义登录拦截器
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public 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 {
@Override
public 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/**");
}
}
使用第三方工具Hutool来绘制验证码,把生成的验证码保存到session中,通过响应把图片传递个前端
@GetMapping("/getVerify")
public Response getVerify(HttpSession session,HttpServletResponse response) {
VerificationCodeUtil.getCode(session,response);
return Response.success("ok");
}
通过密码加盐来保证用户密码的一定安全性
#
号分割再把生成的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("注销成功");
}
登录成功后跳转到博客列表页面,需要获取当前用户的所有博客信息,和当前用户的信息。
用户登录后通过用户的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);
}
博客列表有对应的查看文章响应,修改文章,删除文章。
发布文章校验标题和正文是否为空,校验完毕后给数据表添加信息。
约定前后端交互:
请求:
{
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 {
@Autowired
private 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 {
@Autowired
private TimedArticleService timedArticleService;
@Autowired
private ArticleService articleService;
// 每5秒执行一次
@Scheduled(fixedRate = 5000)
@Transactional
public 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
}
}
文章草稿包存草稿箱和发布文章类似,点击保存草稿后就把文章保存到草稿箱当中,并更新数据表信息,可以从草稿箱中发布博客,也可以修改草稿箱里的草稿文章或者删除草稿。
需要注意的是修改草稿,点击修改草稿后,通过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("保存失败");
}
前端传递头像,后端校验一下文件格式,生成唯一的文件名,把头像路径更新到对应的用户数据库。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("修改失败");
}
修改密码比较简单,用户登录后,输入原密码和新密码,后端通过解密方式验证。验证通过即可修改密码。
@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同问题一起比对验证,端验证正确后,再给用户输入新密码,输入新密码后再次提交。修改密码前再次验证一下密保问题,如果验证通过即可修改密码。