從零開始搭建博客02----發表博客個人中心

頭部登錄狀態

shiro標籤的引用

由於shiro標籤不是html的原生標籤,所有我們需要先引入一個額外的依賴,shiro的標籤庫(thymeleaf的拓展標籤)。

	<dependency>
			<groupId>com.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
			<version>2.0.0</version>
		</dependency>

依賴添加好之後,然後,我們需要在com.fly.config.ShiroConfig 中初始化一下,注入對應的Bean, 頁面才能渲染出來

    //用於thymeleaf模板使用shiro標籤,shiro方言標籤
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

然後在需要使用shiro標籤的html 文件的頭部添加

<html xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

添加好之後,就可以使用<shiro:user></shiro:user> 將要權限控制的內容包起來,當然shiro 標籤還有很多
在這裏插入圖片描述

用戶信息存到session中

用戶登錄成功之後需要將用戶的信息保存的session中。我們只需要在用戶認證的方法中com.fly.shiro.OAuth2Realm 類的doGetAuthenticationInfo 方法中加上如下語句:

//        將登陸信息放在session
        SecurityUtils.getSubject().getSession().setAttribute("profile",profile);

經過如下如上設置我們就實現了登錄頭部狀態的控制
在這裏插入圖片描述

完善個人信息

用戶中心

用戶中心主要就兩個,我發的貼和我收藏的貼
在這裏插入圖片描述
我發的帖子,在這裏插入代碼片com.homework.controller.CenterController#center 查詢條件只有用戶id

        QueryWrapper<Post> wrapper = new QueryWrapper<Post>().eq("user_id", getProfileId())
                .orderByDesc("created");
        IPage<Map<String, Object>> pageData = postService.pageMaps(page, wrapper);
        request.setAttribute("pageData", pageData);

我的收藏

 IPage<Map<String, Object>> pageData = userCollectionService.
                pageMaps(page, new QueryWrapper<UserCollection>()
                        .eq("user_id", getProfileId()).orderByDesc("created"));

        postService.join(pageData, "post_id");
        request.setAttribute("pageData", pageData);

基本設置

  1. tab 切換回顯的問題,一個頁面有多個tab,如何讓在選中tab 之後刷新不丟失原來的tab選中選項呢?答案是在url 後面加上#,這相當於標籤的效果。
    在這裏插入圖片描述
    當前tab頁標籤定義:

在這裏插入圖片描述

static/mods/user.js 有如下語句:

  //顯示當前tab
  if(location.hash){
    element.tabChange('user', location.hash.replace(/^#/, ''));
  }

  element.on('tab(user)', function(){
    var othis = $(this), layid = othis.attr('lay-id');
    if(layid){
      location.hash = layid;
    }
  });

我們在templates/common/static.html 放入瞭如下代碼,並修改下信息

<script th:inline="javascript" th:if="${session.profile != null}">
    layui.cache.page = '';
    layui.cache.user = {
        username: [[${session.profile.username}]]
        ,uid: [[${session.profile.id}]]
        ,avatar: [[${session.profile.avatar}]]
        ,experience: 0
        ,sex: [[${session.profile.gender}]]
    };
    layui.config({
        version: "3.0.0",
        base: '/mods/' //這裏實際使用時,建議改成絕對路徑
    }).extend({
        fly: 'index'
    }).use('fly');
</script>

通過上面代碼,我們把初始化layui的部分js代碼,頁面中很多class或id 的div 就擁有了特定的監聽或其他。其中就報貨截取url 獲取#後面的標籤用於tab 回顯功能,還有頭像的上傳功能封裝等。
加上了上面代碼之後你會發現經常會有個異常的彈框,那是瀏覽器控制檯發現去訪問/message/nums 的鏈接,在index.js 文件中找到 新消息通知,按照接口要求我們修改地址爲/user/message/nums 並在添加該接口

    @ResponseBody
    @PostMapping("/message/nums")
    public Object getMessNums() {
        Map<Object, Object> result = new HashMap<>();
        result.put("status", 0);
        result.put("count", 3);
        return result;
    }
  1. 頭像
    頭像上傳接口com.fly.controller.CenterController#upload,
    頭像上傳核心代碼
        String orgName = file.getOriginalFilename();
        log.info("上傳文件名爲:" + orgName);
//        獲取後綴名
        String suffixName = orgName.substring(orgName.lastIndexOf("."));
        log.info("上傳的後綴名爲:" + suffixName);
//        文件上傳後的路徑
        String filePath = Constant.uploadDir;
        if ("avatar".equalsIgnoreCase(type)) {
            fileName = "/avatar/avatar_" + getProfileId() + suffixName;
        } else if ("post".equalsIgnoreCase(type)) {
            fileName = "post/post_" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_FORMAT) + suffixName;
        }

        File dest = new File(filePath + fileName);
//        檢查目錄是否存在
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdir();
        }
            //上傳文件
            file.transferTo(dest);
            log.info("上傳成功之後文件的路徑={}", dest.getPath());

目前上傳的圖片我們是到了一個指定目錄,然後nginx或者tomcat是可以讀取這個目錄的,所以可以通過url來訪問,一般來說我們把圖片上傳到雲存儲服務上。這裏先這樣弄了。
頭像上傳之後,更新shiro 中的頭像信息

    AccountProfile profile = getProfile();
      profile.setAvatar(url);

圖片上傳之後更新圖像信息
在這裏插入圖片描述

  1. 密碼
    密碼重置接口com.fly.controller.CenterController#resetPwd
    接口代碼比較簡單:
    @ResponseBody
    @PostMapping("/resetPwd")
    public R restPwd(String nowpass, String pass) {
        //查詢用戶
        User user = userService.getById(getProfileId());
        if (user == null || !nowpass.equals(user.getPassword())) {
            return R.failed("密碼不正確");
        }
        user.setPassword(pass);
        boolean result = userService.updateById(user);
        return R.ok(result);
    }

前端頁面在 /user/setting.html

發表,編輯博客

發表和編輯博客是同一個頁面,前端頁面展示
在這裏插入圖片描述
ajax 請求代碼:

    $(function() {
        layui.use('form', function() {
            var form = layui.form;
            //監聽提交
            form.on('submit(post)', function (data) {
                $.ajax({
                    url: '/user/post',
                    type: "POST",
                    data: data.field,
                    success: function (res) {
                        if (res.code == 0) {
                            layer.msg("操作成功");
                            setTimeout(function () {
                                location.href="/post/" + res.data;
                            }, 1000);

                        } else {
                            layer.msg(res.msg);
                        }
                    }
                });
                return false;
            });
        });
    });

後臺接口在com.fly.controller.PostController類中:

  @ResponseBody
    @PostMapping("/user/post")
    public R postArticle(@Valid Post post, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return R.failed(bindingResult.getFieldError().getDefaultMessage());
        }
//        新增文章
        if (post.getId() == null) {
            post.setUserId(getProfileId());

            post.setModified(new Date());
            post.setCreated(new Date());
            post.setCommentCount(0);
            post.setEditMode(Constant.EDIT_HTML_MODEL);
            post.setLevel(0);
            post.setRecommend(false);
            post.setViewCount(0);
            post.setVoteDown(0);
            post.setVoteUp(0);
            post.setStatus(Constant.NORMAL_STATUS);

        } else {
            Post tempPost = postService.getById(post.getId());
            if (tempPost.getUserId().equals(getProfileId())) {
                return R.failed("不是自己的帖子");
            }
        }
        postService.saveOrUpdate(post);
        // TODO: 2018/12/13 給所有訂閱人發送消息

        return R.ok(post.getId());
    }

顯示渲染博客

我們原先的顯示博客內容是通過如下標籤來顯示的

<div class="detail-body photos" th:text="${post.content}"></div>

但這樣顯示出來的內容明顯和我們預覽的不一樣,還需要經過layui的渲染,所以我們要加上一段js代碼。在body後面加上(templates/post/index.html:253)

<script>
    layui.use(['fly','face'],function () {
        var $ = layui.$
            ,fly=layui.fly;

//        如果你是採用模板自帶的編輯器,你需要開啓以下語句來解析
        $('.detail-body').each(function () {
            var othis = $(this), html = othis.html();
            othis.html(fly.content(html));
        });

博文回顯

用戶編輯完博客之後,點擊提交保存之後就可以 調用/user/post 進行博文回顯,博客的地址com.fly.controller.PostController#index, 博文回顯主要博文,用戶,分類以及評論信息,核心代碼如下:

        Map<String, Object> post = postService.getMap(new QueryWrapper<Post>().eq("id", id));

        userService.join(post, "user_id");
        categoryService.join(post, "category_id");

        Assert.notNull(post, "該文章已被刪除");

        req.setAttribute("post", post);
        req.setAttribute("currentCategoryId", post.get("category_id"));


        Page<Comment> page = new Page<>();
        page.setCurrent(current);
        page.setSize(size);

        IPage<Map<String, Object>> pageData = commentService.pageMaps(page, new QueryWrapper<Comment>()
                .eq("post_id", id)
                .orderByDesc("created"));

        userService.join(pageData, "user_id");
        commentService.join(pageData, "parent_id");

        req.setAttribute("pageData", pageData);

前端頁面在 templates/post/index.html 部分代碼如下:
在這裏插入圖片描述
頁面效果如下:
在這裏插入圖片描述
在這裏插入圖片描述

用戶主頁

博客評論功能

用戶評論表:

CREATE TABLE `comment` (
  `id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `content` longtext NOT NULL COMMENT '評論的內容',
  `parent_id` bigint(32) DEFAULT NULL COMMENT '回覆的評論ID',
  `post_id` bigint(32) NOT NULL COMMENT '評論的內容ID',
  `user_id` bigint(32) NOT NULL COMMENT '評論的用戶ID',
  `vote_up` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '“頂”的數量',
  `vote_down` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '“踩”的數量',
  `level` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '置頂等級',
  `status` tinyint(2) DEFAULT NULL COMMENT '評論的狀態',
  `created` datetime NOT NULL COMMENT '評論的時間',
  `modified` datetime DEFAULT NULL COMMENT '評論的更新時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

後端接口代碼在在這裏插入代碼片

    @ResponseBody
    @PostMapping("/user/post/comment")
    public R commentAdd(@Valid Comment comment, BindingResult bindingResult) {
        Post post = postService.getById(comment.getPostId());
        Assert.isTrue(post != null, "該帖子已被刪除");

        comment.setUserId(getProfileId());
        comment.setCreated(new Date());
        comment.setModified(new Date());
        comment.setStatus(Constant.NORMAL_STATUS);

        // TODO 記錄動作

        // TODO 通知作者
        commentService.save(comment);
        return R.ok(null);
    }

前端頁面在 templates/post/index.html 提交評論代碼如下:
在這裏插入圖片描述

配置異步請求登錄過濾器

在我們shiroConfig中,我們配置了非ajax的請求直接跳轉到登錄頁面,但是受限的ajax請求則不能處理。
如未登錄狀態下直接評論文檔,我們應該給出 請先登錄 的提示。
在shiro中有很多過濾器。其中org.apache.shiro.web.filter.authc.UserFilter

在這裏插入圖片描述
在此我們繼承UserFilter然後重寫redirectToLogin方法。

public class AuthFilter extends UserFilter {

    @Override
    protected void redirectToLogin(ServletRequest servletRequest, ServletResponse response) throws IOException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

//        異步請求要先登錄
        String header = request.getHeader("X-Requested-With");
        if (header != null && "XMLHttpRequest".equals(header)) {
            Subject subject = SecurityUtils.getSubject();
            if (!subject.isAuthenticated()) {
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().print(JSONUtil.toJsonStr(R.failed("請先登錄!")));
            } else {
                super.redirectToLogin(servletRequest, response);

            }
        }

    }
}

然後在com.fly.config.ShiroConfig中注入AuthFilter 的Bean

 @Bean
    public AuthFilter authFilter(){
        return new AuthFilter();
    }

在這裏插入圖片描述
參考代碼:
https://github.com/XWxiaowei/FlyBlog/tree/v5-collection-center

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章