怎么做前后端分离的个人博客,内部还有仿网易云音乐模块?点进便知!(基于Vue,Springboot+mybatisplus+SpringCloud+ElasticSearch等框架实现)

一.个人博客简介

项目地址:http://www.tcefrep.site/ (源码地址在本文末尾),感谢阿里云爸爸,送了一个两个月的2核4g的服务器,现在搜索模块也用上了elasticsearch,https://github.com/asiL-tcefreP,源码在此,觉得项目不错就star一下吧
在这里插入图片描述

1.1 博客主要页面:

 1.1.1 首页

在这里插入图片描述

在这里插入图片描述

 1.1.2 分类页

在这里插入图片描述

 1.1.3 分类页

在这里插入图片描述

 1.1.4 归档页

在这里插入图片描述
在这里插入图片描述

 1.1.5 友链页

在这里插入图片描述
在这里插入图片描述

 1.1.6 关于我

在这里插入图片描述

 1.1.7 留言页面

在这里插入图片描述

扫描二维码关注公众号,回复: 14226760 查看本文章

 1.1.8 登录页面

在这里插入图片描述

 1.1.9 博客详情页面

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

 1.1.10 爬虫页面

在这里插入图片描述
 从csdn爬取了三千篇文章放到数据库做文本分类的训练集,仅做学习使用。

 1.1.11 AI模块页面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 博客后台管理页面:

 后台管理主要有三大模块构成:用户管理,博客管理,数据统计构成。

在这里插入图片描述

 1.2.1 用户管理模块

  • 个人信息界面
    在这里插入图片描述

 1.2.2 博客管理模块

  • 发布博客页面
    在这里插入图片描述
  • 查看博客页面
    在这里插入图片描述
  • 标签管理
    在这里插入图片描述
  • 分类管理
    在这里插入图片描述

 1.2.3 数据统计模块

  • 博文数据
    在这里插入图片描述
  • 单篇博客分析
    在这里插入图片描述

1.3 网站音乐盒模块(仿网易云):

  后花园中的音乐盒有,五个主要模块组成:发现音乐,推荐歌单,最新音乐,最新MV,搜索模块。后台接口调用的是网易云的api

1.3.1 发现音乐:

在这里插入图片描述在这里插入图片描述

1.3.2 推荐歌单:

在这里插入图片描述

1.3.3 最新音乐:

在这里插入图片描述

1.3.4 最新MV:

在这里插入图片描述

1.3.5 搜索详情页:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

1.3.6 MV详情页:

在这里插入图片描述

1.3.7 歌单详情页:

在这里插入图片描述

1.3.8 评论详情:

 歌单和MV中都有评论
在这里插入图片描述
在这里插入图片描述

1.4 网站聊天室模块:

1.4.1 初始界面

在这里插入图片描述

1.4.2 群聊界面

在这里插入图片描述

1.4.3 私聊界面

在这里插入图片描述

1.5 功能介绍:

 本博客简单实现了博客展示、后台管理、发布博客还有评论等功能,其中后台管理、发布博客和评论功能要在用户登录后才可使用,而后台管理的某些功能普通用户只有查看的权限,并没有分配增删改的权限。

1.6 博客介绍

 由于博客是由博主一人完成的,所以暂且只做了一些简单的功能,部分地方还是有不完善的地方甚至有bug,欢迎各位在本篇博文下评论处指出。

1.7 Tips

 1.7.1

 编写博客的markdown编辑器在文章过长时,编写栏和预览栏可能会有错位,此时可手动拉动预览栏滚动条)

 1.7.2

 暂未设置图片上传功能,涉及图片的上传和使用建议使用网络地址。推荐的图片地址(https://picsum.photos/images#1),使用的时候,将右侧链接的(https://unsplash.it/100/100?image=1002) 1002 改成自己的图片id即可,100/100是图片的尺寸,即长宽。

 1.7.3

 前端的请求没有用baseURL封装到一个js里。

 1.7.4

 由于笔者偷懒,没有使用vuex存储某些共享数据,用的都是sessionstorage。

二.前端开发:

2.1 简介:

https://github.com/asiL-tcefreP/blog-vue(前端源码地址)
 采用了vue.js,前端框架采用了semantic-ui和element-ui,此外还有一些关于页面动态和渲染的js和css类似(animate.css,pricsm等)。此外,需要说明的是,本人后端狗一枚,页面样式是基于网上部分模板样式的修改,其余开发是独立完成的。

2.2 项目介绍

 项目结构采用的是vue-cli3,值得一提的是其中用到的插件还是不错的。

编辑器 Markdown

内容排版 typo.css

动画 animate.css

代码高亮 prism

目录生成 Tocbot

滚动侦测 waypoints

平滑滚动 jquery.scrollTo

二维码生成 qrcode.js

弹幕效果 vue-baberrage

背景的彩带效果 ribbon

统计图 echarts

音乐播放器 vue-aplayer

三.后端开发:

3.1 简介:

https://github.com/asiL-tcefreP/blog(后端源码地址)

  • 大致框架采用了SpringBoot+MybatisPlus+SpringCloud(Eureka)+ElasticSearch完成的,用redis做缓存中间件,采用微服务的架构。
  • 安全方面采用了SpringSecurity和BCEncrypt
  • 用了jwt来请求访问接口
  • 利用RSA算法对前端发送的重要参数进行加密,经过网关解密后把参数发送到后端服务器。
  • 由于服务器内存和配置的原因,服务器只上线了四个模块
    在这里插入图片描述
    在这里插入图片描述

项目是由八个模块组成的,

  • blog-common: 博客服务端的实体类
  • blog-encrypt: 博客的服务代理类(从前端接收请求,网关RSA解密后转发给服务端接口)
  • blog-eureka: 微服务注册中心server
  • blog-server: 主体服务端
  • blog-extension: 拓展服务端(留言和友链功能),上线的版本集成了blog-search-api模块,因为阿里云服务器内存太小了
  • blog-search-api: ElasticSearch的服务端,分出一个模块是为了更清晰的展现微服务架构,但是服务器内存太小,所以集成在上述模块中,自己开发可以直接使用本模块
  • blog-article-crawler:爬虫和人工智能模块,用的webmagic框架爬取数据,deeplearning4j做文本分类
  • blog-ai:里面的服务类调用了py脚本来实现古诗词生成

3.3 开发中遇到的一些问题:

3.3.1 关于jwt与zuul

 本人使用自定义注解@LoginRequired来对某些类或者接口进行jwt验证,但是在一开始加入网关微服务的时候,发现后端用了jwt验证的接口一直访问不通过。在浏览器看,发的请求的请求头明明都带上了token,这是一开始百思不得其解的地方之一。

 后来才得知,原来是在网关转发前端的请求后,再把请求转发给后端服务器时,请求头中的token丢失,于是只能在网关filter里面,在转发请求给后端前,手动的把token加到头部。

package pers.fjl.encrypt.filter;

import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import pers.fjl.encrypt.rsa.RsaKeys;
import pers.fjl.encrypt.service.RsaService;

import javax.annotation.Resource;
import javax.crypto.BadPaddingException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;

/**
 * 网关过滤器
 *
 * @author fangjiale 2020年1月6日
 */

@Component
@CrossOrigin
public class RSARequestFilter extends ZuulFilter {
    
    

    @Resource
    private RsaService rsaService;

    private String tokenHeader = "Authorization";

    @Override
    public String filterType() {
    
    
        //过滤器在什么环境下执行,解密操作需要在转发之前执行
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
    
    
        //设置过滤器的执行顺序
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
    }

    @Override
    public boolean shouldFilter() {
    
    
        //是否使用过滤器
        return true;
    }

    @Override
    public Object run() throws ZuulException {
    
    
        /**
         * 1. 从request body中读取出加密后的请求参数
         * 2. 将加密后的请求参数用私钥解密
         * 3. 将解密后的请求参数写回request body中
         * 4. 转发请求
         */

        //获取容器
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();
        String token =  request.getHeader(this.tokenHeader);
        //声明存放加密后的数据变量
        String requestData = null;
        //声明解密后的数据变量
        String decryptData = null;
        //网关从前端接收过来的request后,还要再加上token到头转发request,否则后端服务器会拦截
        ctx.addZuulRequestHeader("Authorization",token);
        //需要设置request请求头中的Content-Type为json格式,否则api接口模块就需要进行url转码操作
        ctx.addZuulRequestHeader("Content-Type", String.valueOf(MediaType.APPLICATION_JSON) + ";charset=UTF-8");

        try {
    
    
            //通过request获取inputStream
            ServletInputStream inputStream = request.getInputStream();

            //从inputStream中得到加密后的数据
            requestData = StreamUtils.copyToString(inputStream, Charsets.UTF_8);
            String s = URLDecoder.decode(requestData, "UTF-8");
            String s2 = s.replace(' ', '+');
            if (requestData != null && s2 != null) {
    
    
                System.out.println("加密后" + requestData);
                System.out.println("替换后" + s2);
            }
            //对加密后的数据进行解密
            if (!Strings.isNullOrEmpty(s2)) {
    
    
                try {
    
    
                    decryptData = rsaService.RSADecryptDataPEM(s2, RsaKeys.getServerPrvKeyPkcs8());
                } catch (BadPaddingException e) {
    
    
                    System.out.println("网关发送的是明文数据");
                }
                System.out.println("解密后" + decryptData);
            }

            if (!Strings.isNullOrEmpty(decryptData)) {
    
    
                byte[] bytes = decryptData.getBytes();

                //使用RequestContext进行数据的转发

                ctx.setRequest(new HttpServletRequestWrapper(request) {
    
    

                    @Override
                    public String getHeader(String name) {
    
    
                        return token;
                    }

                    @Override
                    public ServletInputStream getInputStream() throws IOException {
    
    
                        return new ServletInputStreamWrapper(bytes);
                    }

                    @Override
                    public int getContentLength() {
    
    
                        return bytes.length;
                    }

                    @Override
                    public long getContentLengthLong() {
    
    
                        return bytes.length;
                    }
                });
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

        return null;
    }
}

3.3.2 关于RSA解密与Json

在这里插入图片描述
 获取到前端的加密请求参数时,还有对其进行URLdecode解码,然而加密后的数据中的空格依旧没有转换成加号,此时就得自己用字符串替换。前端传来的数据要进行解码否则就会有%2F,%3D等出现,其次base64编码的+号会变成空格,要对字符串进行处理重新变为+号,关于转码问题可以参考: 这篇文章
在这里插入图片描述

3.4 有意思的插件

  • captcha:自动生成验证码
  • commonmark:将markdown格式的文章转换成html格式的显示在页面
  • NeteaseCloudMusicApi:网易云音乐后台数据的api接口

3.5 关于ELK

 之前一直没做项目关于ElasticSearch的整合,是因为不知道项目如果采用了ES后,对数据库的操作该怎么实现。

例如:当我更改数据库的数据时,还要同步ES索引中的数据,这也未免太过繁琐。此外,数据库中的多对多、一对多关系在ES索引中该如何表示?Linux系统下该怎么部署ES?

 后来查阅资料,才明白不一定要把数据库表的所有字段都存在索引中,只需要存我们需要的字段即可,ES的查询速度比mysql确实快的不止一星半点,并且还有IK分词器对搜索词分词,然后进行得分排序显示查询出来的结果。
在这里插入图片描述
 然后要说的就是Logstash了,就是他完成了数据库和索引的实时同步(最快就是一分钟同步一次),不过只能实现增量同步。
具体关于ELK的配置,还请各位点击我的这篇文章,其中有详细说明。

3.6 关于AI和爬虫模块

  用了三千篇从csdn上爬取下来的文章做训练集。总共迭代了二十次,词向量维度是200,
在这里插入图片描述
在这里插入图片描述

可以看到的是在第一个Epoch,准确率只有65%,但是在第二个Epoch的时候已经有了明显提升。

第十次Epoch,loss值在0.6左右

第二十次Epoch,loss值在0.4左右

第三十次Epoch,loss值在0.2几左右,但是此时准确率已经达到了96.7%。

在这里插入图片描述

第四十次Epoch,loss值约在0.2,准确率达到97.6%。
训练完成后,用ModelSerializer.writeModel方法将模型持久化即可。
在这里插入图片描述
在这里插入图片描述

  根据爬取下来的三千篇文章的内容和标题做的分词,以及词向量分析,算法就不在此细说了,用的都是别人的,源码中都有。
值得一提的是,在使用webmagic根据页面元素的Xpath获取指定数据的过程还是挺有趣的

在这里插入图片描述

  项目涉及增删改查的部分还是挺好理解的,各位看看源码应该都能理解,所以笔者在此不再赘述。
 如果这篇文章对你有帮助,麻烦点个赞,并star一下仓库,有问题请在评论处指出,感谢各位支持!

猜你喜欢

转载自blog.csdn.net/Dlihctcefrep/article/details/113591543
今日推荐