行业知识
博客系统(SSM)
前���页面http://t.csdnimg.cn/zwKyG以上是之前写过的博客前端页面的内容,下面是通过SSM实现的后端内容。
目录
一.准备工作
1.1数据准备
1.2修改配置版本文件
1.3配置数据库
二.项目公共模块
2.1实体类
2.2操作数据库部分
三.功能开发
3.1博客列表
获取博客列表
修改日期格式
3.2博客详情
3.3实现登录(使用令牌)
账号密码登录
强制登录
3.4实现显示用户信息
3.5实现用户退出
3.6实现发博客
3.7实现删除、编辑博客
编辑按钮
编辑博客
编辑
删除博客
3.8加密(盐值)
一.准备工作
1.1数据准备
建表SQL
-- 建表SQLcreate database if not exists java_blog_spring charset utf8mb4;
-- ⽤⼾表DROP TABLE IF EXISTS java_blog_spring.user;CREATE TABLE java_blog_spring.user( `id` INT NOT NULL AUTO_INCREMENT, `user_name` VARCHAR ( 128 ) NOT NULL, `password` VARCHAR ( 128 ) NOT NULL, `github_url` VARCHAR ( 128 ) NULL, `delete_flag` TINYINT ( 4 ) NULL DEFAULT 0, `create_time` DATETIME DEFAULT now(), `update_time` DATETIME DEFAULT now(), PRIMARY KEY ( id ),UNIQUE INDEX user_name_UNIQUE ( user_name ASC )) ENGINE = INNODB DEFAULT CHARACTERSET = utf8mb4 COMMENT = '⽤⼾表';-- 博客表drop table if exists java_blog_spring.blog;CREATE TABLE java_blog_spring.blog ( `id` INT NOT NULL AUTO_INCREMENT, `title` VARCHAR(200) NULL, `content` TEXT NULL, `user_id` INT(11) NULL, `delete_flag` TINYINT(4) NULL DEFAULT 0, `create_time` DATETIME DEFAULT now(), `update_time` DATETIME DEFAULT now(), PRIMARY KEY (id))ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';-- 新增⽤⼾信息insert into java_blog_spring.user (user_name, password,github_url)values("zhangsan","123456","https://gitee.com/Roylele-java-j");insert into java_blog_spring.user (user_name, password,github_url)values("lisi","123456","https://gitee.com/Roylele-java-j");insert into java_blog_spring.blog(title,content,user_id) values("第一篇博客","111我是博客正文我是博客正文我是博客正文",1);insert into java_blog_spring.blog(title,content,user_id) values("第一篇博客","222我是博客正文我是博客正文我是博客正文",2); 1.2创建项目
以JDK17创建,改成JDK8的版本
修改pom文件
1.2修改配置版本文件
1.3配置数据库
配置application.yml文件
#数据库连接配置spring: datasource: url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Drivermybatis: configuration: map-underscore-to-camel-case: true #配置驼峰⾃动转换 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句 mapper-locations: classpath:mapper/**Mapper.xml# 设置⽇志⽂件的⽂件名logging: file: name: logger/spring-blog.log
二.项目公共模块
项目分为控制层(Controller), 服务层(Service), 持久层(Mapper). 各层之间的调用关系如下:
2.1实体类
@Datapublic class UserInfo { private Integer id; private String userName; private String password; private String githubUrl; private Byte deleteFlag; private Date createTime; private Date updateTime;} @Datapublic class BlogInfo { private Integer id; private String title; private String content; private Integer userId; private Integer deleteFlag; private Date createTime; private Date updateTime;} 2.2操作数据库部分
涉及到数据库的部分:
1.用户登录,校验用户名和密码是否正确
SQL:根据用户名,查询用户信息
2.博客列表页
SQL:根据ID名,查询用户信息
SQL:获取博客列表
3.博客详情页··
SQL:根据博客ID,获取博客详情
SQL:根据博客ID,编辑博客
SQL:根据博客ID,删除博客
4.博客添加/修改
SQL:根据输入内容,添加博客
对数据库的操作
@Mapperpublic interface UserInfoMapper {// 根据用户名,查询用户信息 @Select("select * from user where user_name=#{userName} and delete_flag=0") UserInfo queryByName(String userName);// 根据用户ID,查询用用户信息 @Select("select * from user where id=#{id} and delete_flag=0") UserInfo queryById(Integer id);} @Mapperpublic interface BlogInfoMapper {// 获取博客列表 @Select("select * from blog where delete_flag=0") List queryBlogList();// 根据博客ID,获取博客详情 @Select("select * from blog where id=#{id} and delete_flag=0") BlogInfo queryById(Integer id); @Update("update blog set title=#{title},content=#{content} where id=#{id}")// 根据博客ID,编辑博客 Integer update(BlogInfo blogInfo); @Update("update blog set delete_flag=1 where id=#{id}")// 根据博客ID,删除博客 Integer deleteBlog(Integer id);// 根据输入内容,添加博客 @Insert("insert into blog(title,content,user_id) values(#{title},#{content},#{userId})") Integer insertBlog(BlogInfo blogInfo);} 测试操作数据库功能
@SpringBootTestclass UserInfoMapperTest { @Autowired private UserInfoMapper userInfoMapper; @Test void queryByName() { System.out.println(userInfoMapper.queryByName("zhangsan")); } @Test void queryById() { System.out.println(userInfoMapper.queryById(2)); }} @SpringBootTestclass BlogInfoMapperTest { @Autowired private BlogInfoMapper blogInfoMapper; @Test void queryBlogList() { System.out.println(blogInfoMapper.queryBlogList()); } @Test void queryById() { System.out.println(blogInfoMapper.queryById(1)); } @Test void update() { BlogInfo blogInfo=new BlogInfo(); blogInfo.setTitle("test11111"); blogInfo.setContent("好想躺平"); blogInfo.setId(3); blogInfoMapper.update(blogInfo); } @Test void deleteBlog() { blogInfoMapper.deleteBlog(3); } @Test void insertBlog() { BlogInfo blogInfo=new BlogInfo(); blogInfo.setTitle("test"); blogInfo.setContent("好好学习,天天向上"); blogInfo.setUserId(1); blogInfoMapper.insertBlog(blogInfo); }} 定义返回结果的类型Result
//定义统一的返回结果@Datapublic class Result { private int code;//200-成功 -1失败 private String errorMsg;// private T data; public static Result success(T data){ Result result=new Result(); result.setCode(Constants.RESULT_SUCCESS); result.setData(data); return result; } public static Result fail(String errorMsg,T data){ Result result=new Result(); result.setCode(Constants.RESULT_FAIL); result.setErrorMsg(errorMsg); result.setData(data); return result; }} 把常量对应放在一个类Constants中:
public class Constants { public static final Integer RESULT_SUCCESS=200; public static final Integer RESULT_FAIL=-1;} 使用AOP的方式完成统一返回结果处理:
@ControllerAdvice//AOP统一返回结果public class ResponseAdvice implements ResponseBodyAdvice { @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 Result){ return body; } //返回结果是String类型需要单独处理 if (body instanceof String){ ObjectMapper objectMapper=new ObjectMapper(); return objectMapper.writeValueAsString(Result.success(body)); } return Result.success(body); }} 统一异常处理:
//统一异常处理 拦截异常@ResponseBody//返回的是对象,而不是页面@ControllerAdvicepublic class ErrorAdvice { @ExceptionHandler//捕获到异常的注释 public Result errorHandler(Exception e){ Result result=new Result(); // 写法一 // result.setErrorMsg(e.getMessage()); // 写法二 result.setErrorMsg("内部发生错误,请联系管理员"); result.setCode(Constants.RESULT_FAIL); return result; }} 三.功能开发
3.1博客列表
获取博客列表
约定前后端交互接口:
请求:blog/getList
响应:''code'':200 , ''msg'':" " data:
@RequestMapping("/blog")@RestControllerpublic class BlogController { @Autowired private BlogService blogService; //获取博客列表 @RequestMapping("/getList") public List getBlogList(){ return blogService.getBlogList(); }} @Servicepublic class BlogService { @Autowired private BlogInfoMapper blogInfoMapper; public List getBlogList() { return blogInfoMapper.queryBlogList(); }} @Mapperpublic interface BlogInfoMapper {// 获取博客列表 @Select("select * from blog where delete_flag=0") List queryBlogList(); 前端交互部分:
getBlogList(); function getBlogList(){ $.ajax({ type:"get", url:"/blog/getList", success:function(result){ if(result.code==200 && result.data!=null){ var blogList=result.data; //拼接字符串 var finalHtml=""; //对每一条博客,拼接成一个p for(var blog of blogList){ finalHtml +=''; finalHtml +=''+blog.title+''; finalHtml +=''+blog.createTime+''; finalHtml +=''+blog.content+''; finalHtml +='查看全文>>'; finalHtml +=' '; } $(".right").html(finalHtml); } } }); } 此时查看页面:
修改日期格式
对日期格式进行修改:使用SimpleDataFormat
创建一个日期工具类
//日期工具类public class DateUtils { //日期转换成2024-02-28 16;02这种格式 public static String formatDate(Date date){ SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm"); return simpleDateFormat.format(date); }} 对BlogInfo中的 private Date createTime;
自己重新定义想要的get方法
@Datapublic class BlogInfo { private Integer id; private String title; private String content; private Integer userId; private Integer deleteFlag; private Date createTime; private Date updateTime; //自定义Date的get方法 public String getCreateTime() { return DateUtils.formatDate(createTime); }} 3.2博客详情
通过点击博客列表页的产看全文进入到详细的博客详情页。
后端代码:
约定前后端交互接口:
请求:/blog/getBlogDetail?blogId=1
BlogController类中添加BlogService类中添加
//获取博客详情 @RequestMapping("/getBlogDetail") public BlogInfo getBlogDetail(Integer blogId){ return blogService.getBlogDetail(blogId); } BlogService类中添加
public BlogInfo getBlogDetail(Integer id) { return blogInfoMapper.queryById(id); } 测试后端代码:
前端代码:
blog_detail.html
//获取博客详情 $.ajax({ type:"get", url:"/blog/getBlogDetail"+location.search, success:function(result){ if(result.code==200 && result.data!=null){ var blog=result.data; $(".title").text(blog.title); $(".date").text(blog.creatTime); $(".detail").text(blog.content); } } }); 3.3实现登录(使用令牌)
账号密码登录
以前使用的方式:
1.根据用户名和密码,验证密码是否正确
2.如果密码正确,存储session
3.后续访问时,携带cookie(携带sessionId)
现在使用token的方式:
1.根据用户名和密码,验证密码是否正确
2.如果密码正确,后端生成token,并返回给前端(可以放在cookie中,也可以放在本地存储中)
3.后续访问,携带token,后端校验token的合法性(通常放在http请求的header中)
约定前后端交互接口:
/user/login
参数username=xxx&&password=xxx
创建JwtUtils工具类
//JWT工具类public class JwtUtils { private static final String secretString="yvc+h4mLEjn0+7s1XQNUp8q71z5kuYgn2JnAvLXK8Hg="; private static final long expiration=30*60*1000; private static final Key key= Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));//签名的编码方式 public static String genToken(Map claim){ String token= Jwts.builder() .setClaims(claim)//设置载荷 .setExpiration(new Date(System.currentTimeMillis()+expiration))//生成过期时间 .signWith(key)//设置签名 .compact();//生成token return token; }} UserController中添加
@RequestMapping("/user")@RestControllerpublic class UserController { @Autowired private UserService userService; @RequestMapping("/login") public Result login(String username, String password){ //1.参数校验 //2.密码校验 //3.生成token并返回 if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){ return Result.fail("用户名或密码为空"); } //获取数据库中的密码 UserInfo userInfo=userService.queryByName(username); if(userInfo==null ||userInfo.getId()