宜昌城市建设学校网站,云南大学做行测的网站,wordpress网站怎么样,手机app设计软件文章目录项目优点项目创建创建相应的目录#xff0c;文件#xff0c;表#xff0c;导入前端资源实现common工具类实现拦截器验证用户登录实现统一数据返回格式实现加盐加密类实现encrypt方法实现decrypt方法实现SessionUtil类实现注册页面实现前端代码实现后端代码实现登录页…
文章目录项目优点项目创建创建相应的目录文件表导入前端资源实现common工具类实现拦截器验证用户登录实现统一数据返回格式实现加盐加密类实现encrypt方法实现decrypt方法实现SessionUtil类实现注册页面实现前端代码实现后端代码实现登录页面实现前端代码实现后端代码实现个人主页实现退出登录功能实现前端代码实现后端代码初始化个人信息包括个人文章列表的个人信息和完成删除功能实现前端代码实现后端代码实现详情页blog_content.html实现前端代码实现后端代码实现博客修改功能blog_update.html实现前端代码实现后端代码实现博客编辑页实现前端代码实现后端代码实现我的草稿箱draft_list.html实现前端代码实现后端代码实现博客主页blog_list.html实现前端代码实现后端代码将session持久化到redis其他扩展功能前言这个博客系统前端分为8个页面分别是注册页登录页编辑页修改页个人主页博客正文页草稿列表页博客列表页项目优点
框架使用ssmSpringBoot SpringMVC MyBatis密码用户登录用的密码是使用加盐算法处理然后存储到数据库用户登录状态持久化将session持久化到redis功能升级在博客列表页实现一个分页功能使用拦截器进行用户的登录验证统一数据返回格式统一异常处理
项目创建
创建一个SpringBoot项目添加需要的依赖
dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.springframework.session/groupIdartifactIdspring-session-data-redis/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.3.0/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdscoperuntime/scopeoptionaltrue/optional/dependencydependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdscoperuntime/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependencies创建相应的目录文件表导入前端资源
首先在数据库建表这里直接提供sql语句
-- 创建数据库
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(32) not null,photo varchar(500) default ,createtime datetime default now(),updatetime datetime default now(),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 datetime default now(),updatetime datetime default now(),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, , 2021-12-06 17:10:48, 2021-12-06 17:10:48, 1);-- 文章添加测试数据
insert into articleinfo(title,content,uid,state)values(喜讯,今天是正月初六,9,2);
实体层model:创建实体类UserInfo,ArticleInfo控制层controller创建控制器UserController和ArticleInfo服务层servlce创建服务类UserService和ArticleService持久层(mapper)创建接口UserMapper和ArticleMapper并创建对应的xml文件在yml文件里配置好工具层common统一返回类ResponseAdvice,AjaxResult等
注意创建完相应的类就得将需要加的注解加上去这里就不一一展示了 创建完后的目录结构
yml配置文件的内容
# 配置数据库的连接字符串
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncodingutf8username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis:mapper-locations: classpath:mybatis/**Mapper.xmlconfiguration: # 配置打印 MyBatis 执行的 SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置打印 MyBatis 执行的 SQL
logging:level:com:example:demo: debug将前端的东西导入static
实现common工具类 由于工具类后面我们基本都会用到所以这里先实现一下
实现拦截器验证用户登录
步骤1创建一个Constant类定义session的key步骤2创建一个普通类实现HandlerInterceptor接口重写preHandle步骤3创建一个普通类实现 WebMvcConfigurer接口重写addInterceptors
public class Constant {//登录信息存储到session中的key值public static final String SESSION_USERINFO_KEY session_userinfo_key;
}
Component
public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断登录业务HttpSession session request.getSession(false);//会根据请求发送来的sessionId去服务器找对应的会话if(session.getAttribute(Constant.SESSION_USERINFO_KEY)!null) {//根据key拿到valuereturn true;}response.setStatus(401);return false;}
}
Configuration
public class AppConfig implements WebMvcConfigurer {//不拦截的urlListString excludes new ArrayList() {{add(/**/*.html);add(/js/**);add(/editor.md/**);add(/css/**);add(/img/**); // 放行 static/img 下的所有文件}};AutowiredLoginInterceptor loginInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置拦截器InterceptorRegistration registration registry.addInterceptor(loginInterceptor);registration.addPathPatterns(/**);registration.excludePathPatterns(excludes);}
}实现统一数据返回格式
步骤1创建一个普通的类实现业务成功返回的方法和业务失败返回的方法 步骤2创建一个普通的类实现ResponseAdvice接口重写supports方法和beforeBodyWrite方法 代码
public class AjaxResult {/*** 业务执行成功返回的方法* param data* return*/public static HashMapString,Object success(Object data) {HashMapString,Object result new HashMap();result.put(code,200);result.put(msg,);result.put(data,data);return result;}/*** 业务执行成功时返回的代码* param data* param msg* return*/public static HashMapString,Object success(Object data,String msg) {HashMapString,Object result new HashMap();result.put(code,200);result.put(msg,msg);result.put(data,data);return result;}/*** 业务执行失败返回的方法* param code* param data* param msg* return*/public static HashMapString,Object fail(int code,Object data,String msg) {HashMapString,Object result new HashMap();result.put(code,code);result.put(msg,msg);result.put(data,data);return result;}/*** 业务执行失败返回的方法* param code* param msg* return*/public static HashMapString,Object fail(int code,String msg) {HashMapString,Object result new HashMap();result.put(code,code);result.put(msg,msg);result.put(data,);return result;}
}public class ResponseAdvice implements ResponseBodyAdvice {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 HashMap) {return body;}//如果body是字符串类型需要特殊处理if(body instanceof String) {ObjectMapper objectMapper new ObjectMapper();return objectMapper.writeValueAsString(AjaxResult.success(body));}return AjaxResult.success(body);}
}
实现加盐加密类
创建一个普通类实现两个方法encrypt方法和decrypt方法。
encrypt方法根据用户输入的密码进行加盐加密返回最终加密的密码decrypt方法根据用户输入的密码和数据库存的加密的密码进行验证
public class SecurityUtil {/*** 对password进行加盐加密* param password* return*/public static String encrypt(String password) {}/*** 验证password和数据库拿出来的finalPassword进行验证* param password* param finalPassword* return*/public static String decrypt(String password,String finalPassword) {}
}实现encrypt方法
加盐思路用UUID类生成一个32长度的字符串作为盐值然后将盐值password进行md5加密生成一个32长度的字符串然后盐值这个有md5加密的字符串就是最终的结果 /*** 对password进行加盐加密* param password* return*/public static String encrypt(String password) {//每次生成内容不同但是长度固定为32的字符串String salt UUID.randomUUID().toString().replace(-,);//盐值password进行md5加密String finalPassword DigestUtils.md5DigestAsHex((saltpassword).getBytes());//返回盐值密码总共64位存到数据库中return saltfinalPassword;}实现decrypt方法
这个方法用来解密验证密码 思路这个方法有两个参数password:待验证的密码finalPassword最终正确的密码在数据库查询的密码从finalPassword中提取出盐值然后盐值password进行md5加密生成一个字符串然后盐值字符串和finalPassword判断是否相等
/*** 验证password和数据库拿出来的finalPassword进行验证* password:待验证的密码* finalPassword最终正确的密码在数据库查询的密码* param password* param finalPassword* return*/public static boolean decrypt(String password,String finalPassword) {//非空效验if(!StringUtils.hasLength(password) || !StringUtils.hasLength(finalPassword)) {return false;}if(finalPassword.length()!64) {return false;}//提取出盐值String salt finalPassword.substring(0,32);//使用盐值密码生成一个32位的密码String securityPassword DigestUtils.md5DigestAsHex((saltpassword).getBytes());//使用上一个32位的密码拼接上盐值进行密码验证return (saltsecurityPassword).equals(finalPassword);}实现SessionUtil类
这个工具类用来查询当前用户登录的session信息
public class SessionUtil {public static UserInfo getLoginUser(HttpServletRequest request) {HttpSession session request.getSession(false);if(session!null session.getAttribute(Constant.SESSION_USERINFO_KEY)!null) {UserInfo userInfo (UserInfo) session.getAttribute(Constant.SESSION_USERINFO_KEY);return userInfo;}return null;}
}
实现注册页面
这里开始就涉及到前后端交互了要约定好前后端交互的接口
实现前端代码
那先来编写前端代码打开reg.html进行编写代码 记得要引入jquery mysub方法
function mysub() {//1.非空效验var username jQuery(#username);var password jQuery(#password);var password2 jQuery(#password2);if(username.val()) {alert(用户名为空);username.focus();return false;}if(password.val()) {alert(密码为空);password.focus();return false;}if(password2.val()) {alert(确认密码为空);password2.focus();return false;}if(password.val()!password2.val()) {alert(两次输入的密码不一致);return false;}//2.发送ajax请求jQuery.ajax({url:/user/reg,type:POST,data:{username:username.val(),password:password.val()},success:function(result) {if(result.code200 result.data1) {alert(注册成功);if(confirm(是否去登录?)) {location.hreflogin.html;}} else if(result.code-2 result.msg!null) {alert(注册失败用户名已存在);} else {alert(注册失败请重试);}}});}实现后端代码
由前端代码可知url为/user/reg我们需要在AppConfig里放行这个url因为默认是所有url都要拦截但是注册不能拦截因为还没注册怎么能登录呢 后端代码基本思路就是controller调用serviceservice调用mapper 所以要在UserController中注入UserService,在UserService中注入UserMapper 在UserController中实现reg方法
RequestMapping(/reg)public Object reg(String username,String password) {//1.非空效验if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return AjaxResult.fail(-1,非法参数);}//根据用户名查询用户UserInfo userInfo userService.getUserByName(username);if(userInfo!null userInfo.getId()0) {//用户名已经存在return AjaxResult.fail(-2,用户名已存在);}//2.进行添加操作int result userService.add(username, SecurityUtil.encrypt(password));if(result1) {return AjaxResult.success(1,添加成功);} else {return AjaxResult.fail(-1,添加失败);}}可以看到我们还需要在UserService中实现getUserByName和add方法
public UserInfo getUserByName(String username) {return userMapper.getUserByName(username);}public int add(String username,String password) {return userMapper.add(username,password);}实现完之后还需要在UserMapper接口中定义getUserByName方法和add方法然后在对应的xml文件中实现sql语句 UserMapper public UserInfo getUserByName(Param(username) String username);public int add(Param(username) String username,Param(password) String password);对应的UserMapper.xml文件 select idgetUserByName resultTypecom.example.demo.model.UserInfoselect * from userinfo where username#{username}/selectinsert idaddinsert into userinfo(username,password)values(#{username},#{password})/insert到这里注册页面就实现完成了
实现登录页面
实现前端代码
打开login.html进行编写代码 还是一样引入jquery然后在submit设置onclick监听然后实现mysub()方法
function mysub() {//1.非空效验var username jQuery(#username);var password jQuery(#password);if(username.val()) {alert(用户名为空);username.focus();return false;}if(password.val()) {alert(密码为空);password.focus();return false;}//2.发送ajax请求jQuery.ajax({url:/user/login,type:POST,data:{username:username.val(),password:password.val()},success:function(result) {if(result.code200 result.data1) {location.hrefmyblog_list.html;} else {alert(用户名或密码错误);username.focus();}}});}实现后端代码
先放行/user/login接口 然后在UserController中编写login方法
RequestMapping(/login)public int login(HttpServletRequest request,String username,String password) {//1.非空效验if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return 0;}//2.进行数据库查询操作UserInfo userInfo userService.getUserByName(username);if(userInfo!null userInfo.getId()0) {//用户名正确验证密码if(SecurityUtil.decrypt(password,userInfo.getPassword())) {//将userInfo存到session中HttpSession session request.getSession(true);session.setAttribute(Constant.SESSION_USERINFO_KEY,userInfo);return 1;}}return 0;}实现个人主页
实现退出登录功能
当然由于退出登录功能在很多页面中都应该存在所以后面可能就不详细说明
实现前端代码
退出登录功能后面很多页面都会用所以我们新建一个Tool.js文件将退出登录的前端方法写在里面 然后在Tool.js中编写代码
//退出登录
function onExit() {if(confirm(是否确认退出)) {//发送ajax请求退出jQuery.ajax({url:/user/logout,type:POST,data:{},success: function(result) {location.hreflogin.html;},error:function(err) {if(err!null err.status401) {alert(用户未登录即将跳转登录页面);location.hreflogin.html;}}});}
}编写myblog_list.html文件 并引入Tool.js和jquery 然后只需要修改一句代码即可 当你点击退出登录它会自动调用Tool.js里的onExit()方法
实现后端代码
当用户点击退出登录发送ajax请求到后端时后端就将用户对应的session给删除即可。
在UserController中编写代码
RequestMapping(/logout)public boolean logout(HttpServletRequest request) {HttpSession session request.getSession(false);if(session!null session.getAttribute(Constant.SESSION_USERINFO_KEY)!null) {session.removeAttribute(Constant.SESSION_USERINFO_KEY);}return true;}初始化个人信息包括个人文章列表的个人信息和完成删除功能
当跳转到myblog_list.html时就前端就调用initList()和myinfo()方法这两个方法发送ajax请求去查询数据库然后将个人信息和个人发表的文章返回给前端前端再根据数据进行渲染
实现前端代码
scriptvar descLen 60;//简介最大长度//这个方法用来从正文中提取字符串function mySubStr(content) {if(content.lengthdesLen) {return content.substr(0,descLen);}return content;}//初始化个人列表信息function initList() {jQuery.ajax({url:/art/mylist,type:POST,data:{},//不用传uid因为session中有userinfo不能轻信前端传来的参数success:function(result) {if(result.code200 result.data!null result.data.length0) {var html ;for(var i0;iresult.data.length;i) {var item result.data[i];//如果state2说明是草稿箱里的文章不显示出来if(item.state2) {continue;}htmldiv classblog;htmldiv classtitleitem.title/div;htmldiv classdateitem.createtime/divdiv classdescmySubstr(item.content) /div;htmldiv styletext-align: center;margin-top: 50px;;htmla hrefblog_content.html?iditem.id查看详情/anbsp;nbsp;;htmla hrefblog_update.html?iditem.id修改/anbsp;nbsp;a hrefjavascript:myDel(item.id)删除/a/div;html/div;}jQuery(#artlistDiv).html(html);} else {//此人没有发表文章jQuery(#artlistDiv).html(h1暂无数据/h1);}},error:function(err) {if(err!null err.statue401) {alert(用户未登录即将跳转登录页面);location.hreflogin.html;}}});}initList();//当浏览器渲染引擎执行到此行时就会调用此方法//获取个人信息function myinfo() {jQuery.ajax({url:user/myinfo,type:POST,data:{},success:function(result) {if(result.code200 result.data!null) {jQuery(#username).text(result.data.username);}},error:function(err) {if(err!null err.status401) {alert(用户未登录即将跳转登录页面);location.hreflogin.html;}}});}myinfo();//删除功能function myDel(aid) {jQuery.ajax({url:art/del,type:POST,data:{aid:aid},success:function(result) {if(result.code200 result.data1) {alert(删除成功);location.hrefmyblog_list.html;}},error:function(err) {if(err!null err.status401) {alert(用户未登录);location.hreflogin.html} else {alert(删除失败);location.hrefmyblog_list.html}}});}/script代码很长但其实主要也就三个主要的方法这3个方法都有ajax请求所以需要在后端进行处理然后返回结果
实现后端代码
先处理initList的ajax请求 对于initList发送来的ajax请求我们要根据sesion里存的userInfo的id去查文章表拿到该用户的所有文章信息然后进行返回 同样先进行注入 RequestMapping(/mylist)public ListArticleInfo myList(HttpServletRequest request) {UserInfo userInfo SessionUtil.getLoginUser(request);if(userInfo!null) {return articleService.getMyList(userInfo.getId());}return null;}接下来一样要在ArticleService中实现getMyList方法 在ArticleService中也要先注入 public ListArticleInfo getMyList(Integer uid) {return articleMapper.getMyList(uid);}接下来在ArticleMapper中定义getMyList方法然后在对应的xml文件中编写sql语句
public ListArticleInfo getMyList(Param(uid) Integer uid);select idgetMyList resultTypecom.example.demo.model.ArticleInfoselect * from articleinfo where uid#{uid}/select接下来处理myinfo的ajax请求 RequestMapping(/myinfo)public UserInfo myInfo(HttpServletRequest request) {HttpSession session request.getSession(false);if(session!null session.getAttribute(Constant.SESSION_USERINFO_KEY)!null) {UserInfo userInfo SessionUtil.getLoginUser(request);return userInfo;}return null;}接下来处理myDel发送来的ajax请求
RequestMapping(/del)public Integer artDel(HttpServletRequest request,Integer aid) {//非空效验if(aid!null aid0) {//根据文章id查询文章详情ArticleInfo articleInfo articleService.getDetail(aid);//进行归属人验证UserInfo userInfo SessionUtil.getLoginUser(request);if(articleInfo!null userInfo!null userInfo.getId()!articleInfo.getUid()) {//归属人正确进行删除int result articleService.artDel(aid);return result;}}return null;}接下来得在ArticleService中实现getDetail方法和artDel方法
public ArticleInfo getDetail(Integer aid) {return articleMapper.getDetail(aid);}public Integer artDel(Integer aid) {return articleMapper.artDel(aid);}接下来在ArticleMapper中定义getDetail方法和artDel方法然后在对应的xml文件中编写sql语句
public ArticleInfo getDetail(Param(aid) Integer aid);public Integer artDel(Param(aid) Integer aid);select idgetDetail resultTypecom.example.demo.model.ArticleInfoselect * from articleinfo where id#{aid}/selectdelete idartDeldelete from articleinfo where id#{aid}/delete实现详情页blog_content.html myblog_list.html的页面效果大概是这样的其中删除功能已经实现了那么现在来实现查看详情功能当点击查看详情时就会跳转blog_content.html页面并将文章id传过去如下图
实现前端代码
那接下来打开blog_content.html来进行编写前端代码 这个页面同样也有退出登录功能跟上面写法一样 在编写之前我们需要在Tool.js中写一个方法getURLParam这个方法用来从url上获取参数 这里直接给代码
// 获取当前 url 中某个参数的方法
function getURLParam(key){var params location.search;if(params.indexOf(?)0){params params.substring(1);var paramArr params.split();for(var i0;iparamArr.length;i){var namevalues paramArr[i].split();if(namevalues[0]key){return namevalues[1];}}}else{return ;}
}然后开始编写blog_content.html //获取文章详细信息function getArtDetail() {//从url中获取文章id也就是在myblog_list.html跳转到这里时url中的参数var aid getURLParam(id);if(aid!null aid0) {//发送ajax请求查询数据库拿到文章详情jQuery.ajax({url:art/detail,type:POST,data:{aid:aid},success:function(result) {if(result.code200 result.data!null) {var art result.data;jQuery(#title).text(art.title);jQuery(#date).text(art.createtime);jQuery(#rcount).text(art.rcount);editormd editormd.markdownToHTML(editorDiv,{markdown : art.content});//根据uid获取个人信息myInfo(art.uid);}}});}}getArtDetail();//根据uid获取个人信息function myInfo(uid) {jQuery.ajax({url:user/myinfobyid,type:POST,data:{uid:uid},success:function(result) {if(result.code200 result.data!null) {jQuery(#username).text(result.data.username);}},error:function(err) {if(err!null err.status401) {alert(用户为登录即将跳转登录页面);location.hreflogin.html}}});}实现后端代码
上面前端代码也是两个ajax请求 先来处理getArtDetail方法发送来的ajax请求 要在AppConfig中放行detail接口 RequestMapping(/detail)public Object getDetail(Integer aid) {if(aid!null aid0) {ArticleInfo articleInfo articleService.getDetail(aid);//访问量加1synchronized (locker) {int result articleService.rountAdd(aid,articleInfo.getContent()1);if(result!1) {return AjaxResult.fail(-1,访问量添加失败);}}//返回文章详情return AjaxResult.success(articleInfo);}return AjaxResult.fail(-1,查询失败);}接下来要在ArtclieService中实现rcountAdd方法
public int rcountAdd(Integer aid,Integer rcount) {return articleMapper.rcountAdd(aid,rcount);}接下来在ArticleMapper中定义rcountAdd方法并在对应的xml文件中编写sql语句 public int rcountAdd(Param(aid) Integer aid,Param(rcount) Integer rcount);update idrcountAddupdate articleinfo set rcount#{rcount} where id#{aid}/update然后处理myinfo发送的ajax请求要在AppConfig放行/user/myinfobyuid
RequestMapping(/myinfobyuid)public UserInfo getMyInfoByUid(Integer uid) {if(uid!null uid0) {return userService.getMyInfoByUid(uid);}return null;}public UserInfo getMyInfoByUid(Integer uid) {return userMapper.getMyInfoByUid(uid);}public UserInfo getMyInfoByUid(Param(uid) Integer uid);select idgetMyInfoByUid resultTypecom.example.demo.model.UserInfoselect * from userinfo where id#{uid}/select实现博客修改功能blog_update.html 当点击修改时就会将跳转到blog_update.html并将id传过去
实现前端代码
同样这个页面有退出登录功能实现方法跟上面一样 function mysub(){// alert(editor.getValue()); // 获取值// editor.setValue(#123) // 设置值\var title jQuery(#title);var content editor.getValue();//非空效验if(title) {title.focus();alert(请先输入标题);return false;}if(content) {alert(请输入正文);return false;}jQuery.ajax({url:/art/update,type:POST,data:{aid:aid,title:title.val(),content:content},success:function(result) {if(result.code200 result.data0) {alert(修改成功);location.hrefmyblog_list.html;} else {alert(修改失败请重试);}},error:function(err) {if(err!null err.status401) {alert(用户未登录);location.hreflogin.html;}}});}//查询文章详情并展现function showArt() {//从url中获取文章idaid getURLParam(id);if(aid!null aid0) {//访问后端详情jQuery.ajax({url:/art/detailbyid,type:POST,data:{aid:aid},success:function(result) {if(result.code200 result.data!null) {var art result.data;jQuery(#title).val(art.title);initEdit(art.content);} else {alert(您没有权限修改);location.hrefmyblog_list.html;}},error:function(err) {if(err!null err.status401) {alert(用户还未登录即将跳转登录页面);location.hreflogin.html;}}});}}showArt();实现后端代码
上面前端代码中涉及到两个ajax请求 先来处理showArt方法发送的ajax请求 RequestMapping(/detailbyid)public Object getDetailById(HttpServletRequest request,Integer aid) {if(aid!null aid0) {//根据文章id查询文章详情ArticleInfo articleInfo articleService.getDetail(aid);//文章归属人验证UserInfo userInfo SessionUtil.getLoginUser(request);if(articleInfo!null userInfo!null articleInfo.getUid()userInfo.getId()) {//文章归属人正确return AjaxResult.success(articleInfo);}}return AjaxResult.fail(-1,查询失败);}接下来处理mysub发送的ajax请求
RequestMapping(/update)public int update(HttpServletRequest request,Integer aid,String title,String content) {//非空效验if(!StringUtils.hasLength(title) || !StringUtils.hasLength(content) || aid 0 || aid0) {return 0;}UserInfo userInfo SessionUtil.getLoginUser(request);if(userInfo!null userInfo.getId()0) {return articleService.update(aid,userInfo.getId(),title,content);}return 0;}然后在ArticleService中实现update public int update(Integer aid,Integer uid,String title,String content) {return articleMapper.update(aid,uid,title,content);}然后在ArticleMapper中定义update并在对应的xml文件中编写sql语句
public int update(Param(aid) Integer aid,Param(uid) Integer uid,Param(title) String title,Param(content) String content);update idupdateupdate articleinfo set title#{title},content#{content}where id#{aid} and uid#{uid}/update实现博客编辑页
实现前端代码 function mysub(){// alert(editor.getValue()); // 获取值// editor.setValue(#123) // 设置值var title jQuery(#title);var content editor.getValue();//非空效验if(title) {title.focus();alert(请先输入标题);return false;}if(content) {alert(请先输入正文);return false;}jQuery.ajax({url:/art/release,type:POST,data:{title:title.val(),content:content},success:function(result) {if(result.code200 result.data0) {alert(发布成功);location.hrefmyblog_list.html;} else {alert(发布失败请重试);}},error:function(err) {if(err!null err.status401) {alert(用户未登录即将跳转登录页面);location.hreflogin.html;}}});}//检查是否已经登录function checkIsLogged() {jQuery.ajax({url:/user/islogged,type:GET,success:function(result) {return true;},error:function(err) {alert(用户为登录即将跳转登录页面);location.hreflogin.html;}});}checkIsLogged();//保存文章到草稿箱将state设置为2function draft() {var title jQuery(#title);var content editor.getValue();//非空效验if(title) {title.focus();alert(请先输入标题);return false;}if(content) {alert(请先输入正文);return false;}jQuery.ajax({url:/art/draft,type:POST,data:{title:title.val(),content:content},success:function(result) {if(result.code200 result.data0) {alert(保存成功);location.hrefmyblog_list.html;} else {alert(发布失败请重试);}},error:function(err) {if(err!null err.status401) {alert(用户未登录即将跳转登录页面);location.hreflogin.html;}}});}实现后端代码
上面的前端代码总共涉及到3个ajax请求 先来处理 checkIsLogged方法的ajax请求这个主要用来验证时候登录 RequestMapping(/islogged)public int isLogged(HttpServletRequest request, HttpServletResponse response) {UserInfo userInfo SessionUtil.getLoginUser(request);if(userInfo!null) {return 1;}response.setStatus(401);return 0;}然后处理mysub()方法的ajax请求这个方法用来发布文章默认state1说明是发布的文章state2是保存在草稿箱的文章
RequestMapping(/release)public int releaseArt(HttpServletRequest request,String title,String content) {//非空效验if(!StringUtils.hasLength(title) || !StringUtils.hasLength(content)) {return 0;}UserInfo userInfo SessionUtil.getLoginUser(request);if(userInfo!null userInfo.getId()0) {int result articleService.releaseArt(title,content,userInfo.getId());if(result1) {return 1;}}return 0;}还要在ArtcileService中实现releaseArt方法然后在ArticleMapper中定义releaseArt方法并在对应的xml文件中编写sql语句 RequestMapping(/release)public int releaseArt(HttpServletRequest request,String title,String content) {//非空效验if(!StringUtils.hasLength(title) || !StringUtils.hasLength(content)) {return 0;}UserInfo userInfo SessionUtil.getLoginUser(request);if(userInfo!null userInfo.getId()0) {int result articleService.releaseArt(title,content,userInfo.getId());if(result1) {return 1;}}return 0;}public int releaseArt(Param(title)String title,Param(content) String content,Param(uid) Integer uid);insert idreleaseArtinsert into articleinfo (title,content,uid) values (#{title},#{content},#{uid})/insert最后处理保存文章也就是draft()的ajax请求处理方法是将文章保存到数据库中但是要将字段state设置为2然后前端在渲染时如果state2文章会显示在我的草稿箱中 RequestMapping(/draft)public int draftArt(HttpServletRequest request,String title,String content) {//非空效验if(!StringUtils.hasLength(title) || !StringUtils.hasLength(content)) {return 0;}int state 2;//将state设置为2UserInfo userInfo SessionUtil.getLoginUser(request);if(userInfo!null userInfo.getId()0) {//将文章保存到数据库并将state设置为2表示还未发布int result articleService.draftArt(title,content,userInfo.getId(),2);return result;}return 0;}还是老步骤在ArticleService中实现draftArt然后在ArticleMapper定义draftArt方法并在对应的xml文件中编写sql语句
public int draftArt(String title,String content,Integer uid,Integer state) {return articleMapper.draftArt(title,content,uid,state);}public int draftArt(Param(title) String title,Param(content) String content,Param(uid) Integer uid,Param(state) Integer state);insert iddraftArtinsert into articleinfo (title,content,uid,state) values (#{title},#{content},#{uid},#{state})/insert实现我的草稿箱draft_list.html 当点击我的草稿箱时就会跳转到draft_list.html页面
实现前端代码
draft_list.html的代码基本和myblog_list.html的代码一样最主要的区别主要是state2还是state2 var descLen 60;//简介最大长度//字符串截取将文章正文截取成简介function mySubstr(content) {if(content.lengthdescLen) {return content.substr(0,descLen);}return content;}//初始化个人列表信息function initList() {jQuery.ajax({url:/art/mylist,type:POST,data:{},//不用传uid,因为session中有userinfo不能轻信前端的参数success:function(result) {if(result.code200 result.data!null result.data.length0) {//todo:有文章var html;var count 0;for(var i0;iresult.data.length;i) {var itemresult.data[i];//如果state2说明是草稿箱里的文章显示出来if(item.state2) {htmldiv classblog;htmldiv classtitleitem.title/div;htmldiv classdateitem.createtime/divdiv classdescmySubstr(item.content) /div;htmldiv styletext-align: center;margin-top: 50px;;htmla hrefjavascript:publish(item.id)发布文章/anbsp;nbsp;;htmla hrefblog_update.html?iditem.id修改/anbsp;nbsp;a hrefjavascript:myDel(item.id)删除/a/div;html/div;count;}}jQuery(#artlistDiv).html(html); if(count0) {//此人草稿箱没有文章jQuery(#artlistDiv).html(h1暂无数据/h1);} } },error:function(err) {if(err!nullerr.status401) {alert(用户未登录即将跳转登录页面);location.hreflogin.html;}}});}initList();//当浏览器渲染引擎执行到此行时就会调用此方法//获取个人信息function myInfo() {jQuery.ajax({url:/user/myinfo,type:POST,data:{},success:function(result) {if(result.code200 result.data!null) {jQuery(#username).text(result.data.username);}},error:function(err) {if(err!nullerr.status401) {alert(用户未登录即将跳转登录页面);location.hreflogin.html;}}});}myInfo();//删除功能function myDel(aid) {jQuery.ajax({url:/art/del,type:POST,data:{aid:aid},success:function(result) {if(result.code200 result.data1) {alert(删除成功);location.hrefdraft_list.html;}},error:function(err) {if(err!nullerr.status401) {alert(该用户没有删除权限);} else {alert(删除失败);}location.hrefdraft_list.html;}});}//将草稿箱里的文章发布将state设置成1function publish(aid) {jQuery.ajax({url:/art/publish,type:POST,data:{aid:aid},success:function(result) {if(result.code200 result.data1) {alert(发布成功);location.hrefmyblog_list.html;}},error:function(err) {if(err!nullerr.status401) {alert(该用户没有发布权限);} else {alert(发布失败);}location.hrefdraft_list.html;}});}
实现后端代码
上面涉及到的3个ajax请求前两个ajax请求在之前的后端代码已经处理了所以这里只需要再处理publish方法里的ajax请求即可 RequestMapping(/publish)public int publishArt(Integer aid) {if(aid!null) {//将state设置为1int result articleService.publishArt(aid,1);if(result1) {return 1;}}return 0;}然后在ArticleService中实现draftArt方法接着在ArticleMapper中定义draftArt方法并在对应的xml文件中编写sql语句
public int publishArt(Integer aid,Integer state) {return articleMapper.publishArt(aid,state);}public int publishArt(Param(aid) Integer aid,Param(state) Integer state);update idpublishArtupdate articleinfo set state#{state} where id#{aid}/update实现博客主页blog_list.html 博客主页存放所有的博客有5个按钮分别是查看全文首页上一页下一页和末页
首页上一页下一页和末页就属于分页功能了接下来讲一下分页的思路 分页要素
页码pageIndex要查询第几页的数据每页显示多少条数据pageSize每页展示最大长度数据 首先所有的数据都是从数据库中去查询的查询的语句中有一个关键字limit这样就可以限制发送多少条记录去前端比如limit pageSize ,就查询pageSize条记录然后发送去前端然后进行渲染页面 数据库还有一个关键字就是offset偏移量比如说limit 2 offer 2就是跳过前两条记录然后查询第3第4条记录。 那么比如你想查询第pageIndex页的数据那么它的偏移量就是pageSize*(pageIndex-1) 所以分页公式偏移量pageSize*(pageIndex-1) 分页语法select * from articleinfo limit pageSize offset pageSize * (pageIndex-1)
实现前端代码 var descLen 200;//简介最大长度//字符串截取将文章正文截取成简介function mySubstr(content) {if(content.lengthdescLen) {return content.substr(0,descLen);}return content;}var pindex 1;//当前的页码var psize 2;//每页显示的条数信息var totalpage 1;//总页数//初始化分页参数尝试从url中获取pindex和psizefunction initPageParam() {var pi getURLParam(pindex);if(pi!) {pindexpi;}var ps getURLParam(psize);if(ps!) {psizeps;}}initPageParam();//查询分页数据function getList() {jQuery.ajax({url:/art/list,type:GET,data:{pindex:pindex,psize:psize},success:function(result) {if(result.code200 result.data!null result.data.length0) {//循环拼接控制documentvar finalHtml ;for(var i0;iresult.data.length;i) {var item result.data[i];//如果state2说明是草稿箱的文章不显示出来if(item.state2) {continue;}finalHtmldiv classblog;finalHtmldiv classtitleitem.title/div;finalHtmldiv classdateitem.createtime/div;finalHtmldiv classdescmySubstr(item.content)/div;finalHtmla hrefblog_content.html?iditem.id classdetail查看全文/a;finalHtml/div;}jQuery(#listDiv).html(finalHtml);}}});}getList();//查询总共多少页function getTotalPage() {jQuery.ajax({url:/art/totalpage,type:GET,data:{psize:psize},success:function(result) {if(result.code200 result.data!null) {totalpageresult.data;}}});}getTotalPage();//首页function firstClick() {location.hrefblog_list.html;}//上一页function beforeClick() {if(pindex1) {alert(前面已经没有内容了);return false;}pindex-1;location.hrefblog_list.html?pindexpindexpsizepsize;}//下一页function nextClick() {pindex parseInt(pindex);if(pindextotalpage) {//已经是最后一页alert(后面已经没有内容了哦);return false;}pindex1;location.hrefblog_list.html?pindexpindexpsizepsize;}//末页function lastClick() {pindextotalpage;location.hrefblog_list.html?pindexpindexpsizepsize;}实现后端代码
上面涉及到两个ajax请求 两个请求的url都需要放行
先来处理getTotalPage方法的ajax请求
RequestMapping(/totalpage)public Integer getTotalCount(Integer psize) {if(psize!null) {//参数有效int totalCount articleService.getTotalCount();int totalPage (int)Math.ceil(totalCount*1.0/psize);return totalPage;}return null;}接下来在ArticleService中实现getTotalCount方法然后在ArticleMapper中定义getTotalCount方法并在对应的xml文件中编写sql语句
public int getTotalCount() {return articleMapper.getTotalCount();}public int getTotalCount();select idgetTotalCount resultTypejava.lang.Integerselect count(*) from articleinfo where state1/select
接下来处理getList的ajax请求
public ListArticleInfo getList(Integer psize,Integer offset) {return articleMapper.getList(psize,offset);}然后在ArticleService中实现getList还要在ArticleMapper中定义getList并在对应的xml文件中编写sql语句 public ListArticleInfo getList(Param(psize) Integer psize,Param(offset) Integer offset);select idgetList resultTypecom.example.demo.model.ArticleInfoselect * from articleinfo limit #{psize} offset #{offset}/select将session持久化到redis
首先你需要在你的linux环境上安装redis,这里就不介绍怎么安装了 然后在你的项目上的application.properties上配置连接redis即可springboot已经内置了将session持久化到redis所以你只需要在application.properties上配置即可
spring.redis.host#你redis安装在哪台机器的ip地址
spring.redis.password#redis的密码
spring.redis.port6379#redis默认端口号
spring.redis.database0#redis操作的数据库
spring.session.store-typeredis
server.servlet.session.timeout1800#session存到redis的过期时间
spring.session.redis.flush-modeon_save#会将session保存到redis本地
spring.session.redis.namespacespring:session其他扩展功能
定时发布功能更换头像冻结账号 等等大家可以尝试自己实现下面这个源代码链接后续有新功能也会将源代码更新
源码链接https://gitee.com/maze-white/project/tree/master/blog_system_ssm