湖北网站建设公司排名,活动策划ppt模板免费,wordpress4.9.4 安装,网页网站动作效果做的比较棒相比于之前使用Servlet来完成的博客系统#xff0c;SpringBoot版本的博客系统功能更完善#xff0c;使用到的技术更接近企业级#xff0c;快来看看吧~
目录
1.项目介绍
2.数据库准备
3.实体化类
4.返回格式
5.登录和注册功能
6.登出#xff08;注销#xff09;功能…相比于之前使用Servlet来完成的博客系统SpringBoot版本的博客系统功能更完善使用到的技术更接近企业级快来看看吧~
目录
1.项目介绍
2.数据库准备
3.实体化类
4.返回格式
5.登录和注册功能
6.登出注销功能
7.判定是否登录
8.添加、修改、查询、删除文章
9.查询文章列表、分页、展示阅读次数
查询文章列表
展示阅读次数
分页功能
10.拦截器
11.统一异常处理和统一数据返回格式
统一异常处理
统一数据返回格式
12.加盐处理 1.项目介绍
本项目集成了用户注册、用户登录、用户登出注销、验证登录状态、添加文章、查询文章、修改文章、展示阅读次数、文章列表查询、删除文章、分页功能。
其中使用了统一功能处理返回的数据对密码进行了加盐加密操作使用拦截器完成验证用户登录状态。
并且使用分层思想
2.数据库准备
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;-- 使用数据数据
use mycnblog;-- 创建表[用户表]
drop table if exists userinfo;
create table userinfo(id int primary key auto_increment,username varchar(100) not null,password varchar(65) not null,photo varchar(500) default ,createtime timestamp default current_timestamp,updatetime timestamp default current_timestamp,state int default 1
) default charset utf8mb4;-- 创建文章表
drop table if exists articleinfo;
create table articleinfo(id int primary key auto_increment,title varchar(100) not null,content text not null,createtime timestamp default current_timestamp,updatetime timestamp default current_timestamp,uid int not null,rcount int not null default 1,state int default 1
)default charset utf8mb4;-- 添加一个用户信息
INSERT INTO mycnblog.userinfo (id, username, password, photo, createtime, updatetime, state) VALUES
(1, admin, admin, , 2024-2-12 17:10:48, 2024-2-12 17:10:48, 1);-- 文章添加测试数据
insert into articleinfo(title,content,uid)values(Java,Java正文,1);
3.实体化类 在createtime和updatetime之前加上时间格式化注释JsonFormat可以统一时间格式。
4.返回格式
在和前端进行交互的时候我们新建一个类来完成对所有返回的结果的规定。
包括了返回的状态码code返回的信息msg返回的数据data。并且重载success和fail方法。
每个controller返回给前端的数据就是一个AjaxResult success()或者AjaxResult fail()
Data
public class AjaxResult implements Serializable {private Integer code;private String msg;private Object data;public static AjaxResult success(Object data) {AjaxResult ajaxResult new AjaxResult();ajaxResult.setCode(200);ajaxResult.setMsg();ajaxResult.setData(data);return ajaxResult;}public static AjaxResult success(Object data, String msg) {AjaxResult ajaxResult new AjaxResult();ajaxResult.setCode(200);ajaxResult.setMsg(msg);ajaxResult.setData(data);return ajaxResult;}public static AjaxResult fail(Integer code, String msg) {AjaxResult ajaxResult new AjaxResult();ajaxResult.setCode(code);ajaxResult.setMsg(msg);ajaxResult.setData();return ajaxResult;}public static AjaxResult fail(Integer code, String msg, Object data) {AjaxResult ajaxResult new AjaxResult();ajaxResult.setCode(code);ajaxResult.setMsg(msg);ajaxResult.setData(data);return ajaxResult;}
} 5.登录和注册功能 insert idreginsert into userinfo(username, password) value (#{username},#{password})/insertselect idlogin resultTypecom.example.spring_myblogsystem.entity.UserInfoselect * from userinfowhere username #{username}/select
Mapper
public interface UserMapper {int reg(UserInfo userInfo);UserInfo login(Param(username) String username);
}
Service
public class UserService {Autowiredprivate UserMapper userMapper;public int reg(UserInfo userInfo) {return userMapper.reg(userInfo);}public UserInfo login(String username) {return userMapper.login(username);}
} RestController
RequestMapping(/user)
public class UserController {Autowiredprivate UserService userService;RequestMapping(/reg)public AjaxResult reg(UserInfo userInfo) {if (userInfo null || !StringUtils.hasLength(userInfo.getUsername()) || !StringUtils.hasLength(userInfo.getUsername())) {return AjaxResult.fail(-1, 参数非法);}userInfo.setPassword(PasswordTooles.encrypt(userInfo.getPassword()));int result userService.reg(userInfo);return AjaxResult.success(result);}RequestMapping(/login)public AjaxResult login(String username, String password, HttpServletRequest request) {if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return AjaxResult.fail(-1, 参数非法);}UserInfo userInfo userService.login(username);if (userInfo null || userInfo.getId() 0) {return AjaxResult.fail(-2, 用户名或密码错误);}if (PasswordTooles.decrypt(password, userInfo.getPassword())) {return AjaxResult.fail(-2, 用户名或密码错误);}HttpSession session request.getSession();session.setAttribute(ApplicationVariale.SESSION_USERINFO_KEY,userInfo);return AjaxResult.success(1);}
在登录过程中用到了session来判定登录状态所以在传形参的时候不光是要传用户名和密码还需要把request也一起传输过去用来设置当前session。因为session有多个地方需要使用所以把session单独拿出来定义
public class ApplicationVariale {public static final String SESSION_USERINFO_KEY SESSION_KEY_USERINFO;}
6.登出注销功能
只需要使用removeAttribute来移除session就可以实现注销功能。 RequestMapping(/logout)public AjaxResult logout(HttpServletRequest request) {HttpSession session request.getSession(false);session.removeAttribute(ApplicationVariale.SESSION_USERINFO_KEY);return AjaxResult.success(1);}
7.判定是否登录
在某些页面需要判定用户是否登录了比如在博客正文的页面就需要根据用户登录情况来显示不同的按钮。未登录则显示登录按钮已经登录了则显示博客主页按钮。 RequestMapping(/islogin)public AjaxResult isLogin(HttpServletRequest request) {if (UserSessionTools.getLoginUser(request) null) {return AjaxResult.success(0);}return AjaxResult.success(1);}
并且因为判定登录功能可能会多次使用到所以把相关的功能写到common中方便调用。传递的参数是request用来判定当前session的情况。
public class UserSessionTools {public static UserInfo getLoginUser(HttpServletRequest request) {HttpSession session request.getSession(false);if (session ! null session.getAttribute(ApplicationVariale.SESSION_USERINFO_KEY) ! null) {return (UserInfo) session.getAttribute(ApplicationVariale.SESSION_USERINFO_KEY);}return null;}
}
8.添加、修改、查询、删除文章 insert idaddinsert into articleinfo(title,content,uid)values(#{title},#{content},#{uid})/insertupdate idupdateupdate articleinfo set title#{title},content#{content},updatetime#{updatetime}where id#{id} and uid#{uid}/updateselect idgetDetailByIdAndUid resultTypecom.example.spring_myblogsystem.entity.ArticleInfoselect * from articleinfo where id#{id} and uid#{uid}/selectdelete iddeldelete from articleinfo where id #{id} and uid #{uid}/delete
Mapper
public interface ArticleMapper {int add(ArticleInfo articleInfo);ArticleInfo getDetailByIdAndUid(Param(id)Integer id,Param(uid)Integer uid);int update(ArticleInfo articleInfo);int del(Param(id) Integer id, Param(uid) Integer uid);
Service
public class ArticleService {Autowiredprivate ArticleMapper articleMapper;public int add(ArticleInfo articleInfo){return articleMapper.add(articleInfo);}public ArticleInfo getDetailByIdAndUid(Integer id,Integer uid){return articleMapper.getDetailByIdAndUid(id,uid);}public int update(ArticleInfo articleInfo){return articleMapper.update(articleInfo);}public int del(Integer id, Integer uid) {return articleMapper.del(id, uid);}
RestController
RequestMapping(/art)
public class ArticleController {Autowiredprivate ArticleService articleService;RequestMapping(/add)public AjaxResult add(ArticleInfo articleInfo, HttpServletRequest request){if (articleInfo null ||!StringUtils.hasLength(articleInfo.getTitle()) ||!StringUtils.hasLength(articleInfo.getContent())){return AjaxResult.fail(-1,参数异常);}UserInfo userInfo UserSessionTools.getLoginUser(request);articleInfo.setUid(userInfo.getId());int result articleService.add(articleInfo);return AjaxResult.success(result);}RequestMapping(/getdetailbyid)public AjaxResult getDetailByIdAndUid(Integer id,HttpServletRequest request){if(id null || id 0){return AjaxResult.fail(-1,参数非法);}UserInfo userInfo UserSessionTools.getLoginUser(request);return AjaxResult.success(articleService.getDetailByIdAndUid(id, userInfo.getId()));}RequestMapping(/update)public AjaxResult update(ArticleInfo articleInfo,HttpServletRequest request){if(articleInfo null || articleInfo.getId() 0 || !StringUtils.hasLength(articleInfo.getContent()) || !StringUtils.hasLength(articleInfo.getTitle())){return AjaxResult.fail(-1,参数有误);}UserInfo userInfo UserSessionTools.getLoginUser(request);articleInfo.setUid(userInfo.getId());articleInfo.setUpdatetime(userInfo.getUpdatetime());return AjaxResult.success(articleService.update(articleInfo));}RequestMapping(/del)public AjaxResult del(Integer id, HttpServletRequest request) {if (id null || id 0) {return AjaxResult.fail(-1, 参数错误);}UserInfo userInfo UserSessionTools.getLoginUser(request);int result articleService.del(id, userInfo.getId());return AjaxResult.success(result);}
可以看到几乎所有的方法都是先对传入的参数进行非空校验再对session的情况进行判定。实现都很简单。
在getDetailByIdAndUid和del中一次性传入了文章id和uid。这是为了校验当前查看文章的用户是文章的作者只有这种情况才能够对文章进行后续的修改和删除。
9.查询文章列表、分页、展示阅读次数
相比于前面的增删查改操作来说这个部分要复杂很多。
查询文章列表 select idgetListByUid resultTypecom.example.spring_myblogsystem.entity.ArticleInfoselect * from articleinfowhere uid #{uid}order by id desc;/select
ListArticleInfo getListByUid(Param(uid) Integer id);
public ListArticleInfo getListByUid(Integer id) {return articleMapper.getListByUid(id);} RequestMapping(/mylist)public AjaxResult mylist(HttpServletRequest request) {UserInfo userInfo UserSessionTools.getLoginUser(request);ListArticleInfo list articleService.getListByUid(userInfo.getId());//todo将文章正文截取成文章摘要for (ArticleInfo item : list) {String content StringTools.subLength(item.getContent(), 200);item.setContent(content);}return AjaxResult.success(list);}
展示阅读次数
定义一个先rcount变量记录文章的阅读次数。先做查询操作等前面页面刷新时rcount自增1并且存储到数据库中。 update idaddRcountupdate articleinfoset rcountrcount 1where id #{id}/update public Integer getCount() {return articleMapper.getCount();} RequestMapping(/getcount)public AjaxResult getCount() {return AjaxResult.success(articleService.getCount());}
分页功能 分页功能也就是实现这四个功能。
我们先定义几个变量 pageIndex记录页面当前的页码从1开始pageSize记录每页最大条数pageCount记录总页数offset数据库中从第几条开始查询 首页功能很好实现规定当前页码是1offset只需要是0开始就行了。
当设置pageSize为2时总页数和offset都可以被算出来。同时需要再数据库中查询两次一次是根据相关的参数查询文章另一次是查询一共有多少条文章。 select idgetListByPage resultTypecom.example.spring_myblogsystem.entity.ArticleInfoselect * from articleinfoorder by id desclimit #{pageSize} offset #{offset}/select select idgetCount resultTypejava.lang.Integerselect count(id)from articleinfo/select
例如当前是第一页查询的文章为从第0条开始查询当前页码为1offset为0。
如果到了第二页查询的文章从第2条开始查询当前页码为1offset为2。
也就是说offset有如下公式offset pageIndex - 1× pageSize RequestMapping(/getlistbypage)public AjaxResult getListByPage(Integer pageSize, Integer pageIndex) {if (pageSize null || pageSize 0) {pageSize 2;}if (pageIndex null || pageIndex 1) {pageIndex 1;}int offset (pageIndex - 1) * pageSize;ListArticleInfo list articleService.getListByPage(pageSize, offset);list.stream().parallel().forEach((item - {item.setContent(StringTools.subLength(item.getContent(), 150));}));return AjaxResult.success(list);}
同时用一个list来存储相关的文章通过list.stream.forEach来遍历并且可以使用parallel()多线程处理效率更快。
处理完了首页、上一页和下一页末页需要用到getCount获取到一共有多少条文章然后再根据这个数字来判定总页数通过getCount/pageSize就可以得到。得到总页数后再传回数据库查询得到末页的文章。
10.拦截器
虽然前面已经对session做了判定判断用户是否登录但是我们用一个统一的拦截器来完成对代码的过滤等等。
比如在登入一个页面时如果要求用户密码、权限等的验证就可以用自定义的拦截器进行密码验证和权限限制。对符合的登入者才跳转到正确页面。这样如果有新增权限的话不用在controller里修改任何代码直接在interceptor里修改就行了。
Spring中的拦截器是通过动态代理的方式实现和环绕通知的方式实现的并且是作用域controller之前先把相关的请求预处理完再进行程序的正常流程。
Configuration
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(ApplicationVariale.SESSION_USERINFO_KEY) ! null) {return true;}response.sendRedirect(/login.html);return false;}
}
需要实现一个LoginInterceptor并且implements HandlerInterceptor
重写preHandle方法来判定用户是否登录的功能并且把需要登录才能访问的内容添加到MyConfig中。 首先添加所有的路径然后把不需要的路径排除掉就是拦截器拦截的内容。简单的理解为用户需要登录才能够访问的内容都被拦截器拦截了不需要登录就能够访问的则不添加到拦截器中。 11.统一异常处理和统一数据返回格式
统一异常处理
对于异常处理来说最简单直接的方式就是使用 try catch 代码块来捕获系统异常。但是这种处理方式需要我们编写大量的代码而且异常信息不易于统一维护增加了开发工作量甚至可能还会出现异常没有被捕获的情况。为了能够高效的处理好各种系统异常我们需要在项目中统一集中处理我们的异常。
RestControllerAdvice
public class MyExceptionAdvice {ExceptionHandler(Exception.class)public AjaxResult doException(Exception e) {return AjaxResult.fail(-1, e.getMessage());}
}
ControllerAdvice 表示控制器通知类ExceptionHandler 是异常处理器两个结合表示当出现异常的时候执行某个通知 也就是执行某个方法事件。
统一数据返回格式
在所有的controller中我们返回给前端的都是一个AjaxResult对象也就是包含了code、msg、data三个属性的变量。但是如果某个controller中返回的是错误的假设返回的是1那么就会出现错误。我们可以规定统一返回的一种格式有助于帮助前后端程序员沟通。
//统一返回数据格式的封装
//当返回的数据不是AjaxResult的时候转换成AjaxResult
ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {Autowiredprivate ObjectMapper objectMapper;Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}SneakyThrowsOverridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof AjaxResult) {return body;}if (body instanceof String) {return objectMapper.writeValueAsString(AjaxResult.success(body));}return null;}
}
统⼀的数据返回格式可以使用 ControllerAdvice ResponseBodyAdvice 的方式实现。
12.加盐处理
用户输入的密码到数据库中时非常不安全很容易被破解。所以当我们储存到数据库中时最好存储的是加密的密码。所以我们在服务器中需要完成对密码的加密。通过加盐的方式来处理。
密码如何加盐加密http://t.csdnimg.cn/9q7lU
public class PasswordTooles {public static String encrypt(String password) {//产生盐值String salt UUID.randomUUID().toString().replace(-, );//使用盐值明文密码得到加密的密码String finalPassword DigestUtils.md5DigestAsHex((salt password).getBytes());//将盐值和加密的密码共同返回String dbPassword salt $ finalPassword;return dbPassword;}//验证加盐加密密码public static boolean decrypt(String password, String dbPassword) {boolean result false;if (StringUtils.hasLength(password) StringUtils.hasLength(dbPassword) dbPassword.length() 65 dbPassword.contains($)) {String[] passwordArr dbPassword.split(\\$);String salt passwordArr[0];String checkPassword encrypt(password);if (dbPassword.equals(checkPassword)) {result true;}}return result;}
}