SSM个人博客项目

news2024/11/27 3:55:55

文章目录

  • 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 {
    @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/**");
    }
}

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 {
    @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
	}
}

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.coloradmin.cn/o/847653.html

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

相关文章

提取字符串标签中的文字js

DOM操作&#xff1a; const extractText (str) > {const parser new DOMParser();const doc parser.parseFromString(str, text/html);const textNodes doc.body.innerText.trim().split(\n);return textNodes; };const input <div>提取文字</div><p>…

合思动态:合思商城上新「中转联程」功能

引言&#xff1a;正如合思创始人兼 CEO马春荃强调&#xff0c;面对各个行业对财务数字化有差异化需求&#xff0c;费控企业需要深入扎根细分行业探索多元化版本和解决方案&#xff0c;并根据行业实时需求快速迭代。合思的使命就是云产品服务的创新&#xff0c;实现降本增效、合…

基于STM32设计的出租车计费系统

一、项目介绍 在城市交通中&#xff0c;出租车是一种常见的交通工具。为了方便乘客和司机之间的交易&#xff0c;出租车计费系统被广泛应用于出租车行业。系统能够自动计算乘客的费用&#xff0c;提供准确、方便的计费服务&#xff0c;并且能够记录乘客的行驶数据&#xff0c;…

IDEA 指定spring.profiles.active本地启动

spring.profiles.activedev spring.profiles.activepro

SpringBoot项目配置文件

文章目录 一、配置文件作用二、配置文件格式三、properties配置文件说明基本语法读取配置文件优缺点 四、yml配置文件说明基本语法使用yml连接数据库使用yml配置不同数据类型及nullyml配置的读取配置字符串的单双引号问题配置对象 两种格式配置文件对比properties格式配置文件示…

Keburnetes YAML配置文件管理

Kubernetes 支持 YAML 和 JSON 格式管理资源对象JSON 格式&#xff1a;主要用于 api 接口之间消息的传递YAML 格式&#xff1a;用于配置和管理&#xff0c;YAML 是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读 YAML 语法格式 大小写敏感使用缩进表示层…

Linux之AWK

目录 Linux之AWK 定义 语法格式 常用选项 awk 变量 内置变量 格式 案例 &#xff08;1&#xff09;编写一个文本名字叫做awkdemo&#xff0c;里面内容如下 &#xff08;2&#xff09;FS指定输入分隔符 &#xff08;3&#xff09;OFS指定输出分隔符 &#xff08;4&#xf…

EditText设置inputType=“textPassword“后hint 英文字体不对,而且不能换行

问题如题,我先上图看下遇到的问题 ( ps: 我只想唱一句 : 你这该死的安卓~~ 让我心在痛 累在流!!!) 很明显字体偏大,字间隔很宽,关键还不会换行, 看一下我们需要的效果: 看到只要把 android:inputType"textPassword" 这一行干掉 一切就是我们想要的,但是产…

sonar 错误 提示 Synchronize on a new “Object“ instead.使用Id 作为锁

文章目录 错误分析先看sonar 给解决方案最终解决方案 public void synchronizedMethod(Integer id) {synchronized (id) {// Code to be synchronized}}错误分析 Synchronize on a new "Object" instead. 不应该在原始包装器&#xff08;即整数、长整型、布尔值等&am…

HDFS中的Trash垃圾桶回收机制

Trash垃圾桶回收机制 文件系统垃圾桶背景功能概述Trash Checkpoint Trash功能开启关闭HDFS集群修改core-site.xml删除文件到trash删除文件跳过从trash中恢复文件清空trash 文件系统垃圾桶背景 回收站&#xff08;垃圾桶&#xff09;是windows操作系统里的一个系统文件夹&#…

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

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

华为认证 | 云计算HCIE3.0改版后有什么变化?

随着技术的不断进步和行业的发展&#xff0c;云计算的HCIE作为华为公司的顶级认证&#xff0c;也进行了版本的更新。 那改版后有哪些变化呢&#xff0c;今天给大家讲讲。 01 HCIE认证简介 HCIE认证是华为公司旗下的顶级专业认证&#xff0c;面向IT领域的高级专业人士。 它涵…

华为数通题库,新一波分享来袭(有答案哟)

1、以下关于OSPF的Router ID描述不正确的是&#xff08;&#xff09;。&#xff08;多选&#xff09; A. OSPF协议正常运行的前提条件是该路由器有Router ID B. Router ID必须是路由器某接口的IP地址 C. 必须通过手工配置方式来指定Router IDD. 在同一区域内Router I D必须相同…

MobiSys 2023 | 基于毫米波振动测量的无接触材料识别

注1:本文系“无线感知论文速递”系列之一,致力于简洁清晰完整地介绍、解读无线感知领域最新的顶会/顶刊论文(包括但不限于 Nature/Science及其子刊; MobiCom, Sigcom, MobiSys, NSDI, SenSys, Ubicomp; JSAC, 雷达学报 等)。本次介绍的论文是:《MobiSys 2023 | 基于毫米波振…

高忆管理:股票低开说明什么?股价跟什么有关?

股票的开盘价和收盘价是投资者重视的重要指标&#xff0c;它反映了股票价值的变化。那么股票低开阐明什么&#xff1f;股价跟什么有关&#xff1f;高忆管理也为大家预备了相关内容&#xff0c;以供参阅。 股票低开阐明什么&#xff1f; 股票低开是指股票的开盘价低于上一个交易…

SpringCloud(31):Nacos配置管理基础应用

1 Nacos配置管理模型 对于Nacos配置管理&#xff0c;通过Namespace、group、Data ID能够定位到一个配置集。 配置集(Data ID) 在系统中&#xff0c;一个配置文件通常就是一个配置集&#xff0c;一个配置集可以包含了系统的各种配置信息&#xff0c;例如&#xff0c;一个配置集…

C 语言的转义字符

转义字符也叫转移序列&#xff0c;包含如下&#xff1a; 转移序列 \0oo 和 \xhh 是 ASCII 码的特殊表示。 八进制数示例&#xff1a; 代码&#xff1a; #include<stdio.h> int main(void) {char beep\007;printf("%c\n",beep);return 0; }结果&#xff1a; …

IntelliJ IDEA Bookmark使用

1 增加 右键行号栏 2 查看 从favorite这里查看 参考IntelliJ IDEA 小技巧&#xff1a;Bookmark(书签)的使用_bookmark idea 使用_大唐冠军侯的博客-CSDN博客

刘汉清:从生活到画布,宠物成为灵感源泉

出生于中国镇江的艺术家刘汉清&#xff0c;其作品展现出他对日常生活的深入洞察力&#xff0c;以及对美的独特理解。他的作品通常没有视觉参考&#xff0c;而是通过对他周围环境的理解&#xff0c;尤其是他的宠物&#xff0c;来进行创作。 在刘汉清的创作过程中&#xff0c;他…

docker容器监控:Cadvisor +Prometheus+Grafana的安装部署

目录 Cadvisor PrometheusGrafana的安装部署 一、安装docker&#xff1a; 1、安装docker-ce 2、阿里云镜像加速器 3、下载组件镜像 4、创建自定义网络 二、部署Cadvisor 1、被监控主机上部署Cadvisor容器 2、访问cAdvisor页面 三、安装prometheus 1、部署Prometheus…