從0開始碼第一個Spring Boot項目(javaweb個人博客系統)之Spring攔截器使用及個人文章列表展示

攔截器

爲什麼使用攔截器,之前我們做的所有頁面,用戶只要輸入正確的訪問地址就可以進行一系列操作,儘管我們做了後臺驗證,但是這仍是不安全的,比如,如果用戶沒有登錄就訪問之前的http://loaclhoat:8080/publish,就可以進入到發佈文章頁面,爲此我們需要做攔截器,當訪問資源的時候,對其進行攔截並處理,選擇通過還是不通過

配置:

編寫一個攔截器類SessionIntercetpor.java,然後實現HandlerInterceptor接口,並重寫preHandle、postHandle、afterCompletion方法
preHandle方法是在請求之前執行(Controller之前)
postHandle方法在preHandle方法返回true後執行(Controller之後)
afterCompletion方法也是在preHandle方法返回true後執行(整個請求完成之後)

SessionIntercetpor.java
@Component
public class SessionIntercetpor implements HandlerInterceptor {

    @Autowired
    private UserMapper userMapper;
    //preHandle方法是在請求之前執行(Controller之前)
    //處理用戶Cookie驗證,如果用戶沒登錄,驗證不通過,不允許訪問,反之將用戶信息寫入session,用戶後面頁面訪問獲取
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	//其他類中的這段代碼將刪除,具體可查看我的源碼
        User user;
        Cookie[] cookies = request.getCookies();
        if(cookies!=null && cookies.length>0){
            for (Cookie cookie:cookies){
                if("token".equals(cookie.getName())){
                    String token = cookie.getValue();
                    user =  userMapper.selectUserByToken(token);
                    if(user!=null){
                        request.getSession().setAttribute("user",user);
                    }
                }
            }
        }
        return true;
    }

	//postHandle方法在preHandle方法返回true後執行(Controller之後)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

	//afterCompletion方法也是在preHandle方法返回true後執行(整個請求完成之後)
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

編寫一個配置類,用於攔截器配置:

WebConfig.java
//編寫配置類,用於Spring管理我們的自定義配置
@Configuration
public class WebConfig implements WebMvcConfigurer {

	//注入編寫好的攔截器
    @Autowired
    private SessionIntercetpor sessionIntercetpor;

	//添加攔截器,並攔截所有請求
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //攔截所有請求
        registry.addInterceptor(sessionIntercetpor).addPathPatterns("/**");

    }
}

然後在Controller類中編寫如下代碼,用戶session用戶驗證,獲取不到用戶信息將重定向到首頁

//檢查用戶是否登錄
User user = (User) request.getSession().getAttribute("user");
 if(user==null){
     return "redirect:/";
 }

做完如上處理後,我們就可以對所有資源進行攔截,如果沒有登錄,只能訪問首頁和登錄頁

個人文章列表展示

登錄成功後,我們點擊用戶名稱下拉菜單,然後選擇我的文章,如下:
我的文章
點擊之後頁面會跳轉到個人文章列表,這裏只展示自己發佈過的文章信息

我的文章

具體代碼

html頁面代碼模板化引入,如下
我們將共用的頭部導航欄和底部分頁欄抽離成一個模板,這個模板可以在多個html中引入,避免html頁面臃腫、代碼重複等問題

頂部導航欄模板navigation.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!--定義一個div然後起個名字,用於後面引入使用-->
<div th:fragment="nav">
    <!--頂部導航欄-->
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">個人博客</span>
                </button>
                <a class="navbar-brand" href="/">個人博客</a>
            </div>

            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <form class="navbar-form navbar-left">
                    <div class="form-group">
                        <input type="text" class="form-control" placeholder="請搜索問題">
                    </div>
                    <button type="submit" class="btn btn-default">搜索</button>
                </form>
                <ul class="nav navbar-nav navbar-right">
                    <li th:if="${session.user!=null}">
                        <a href="/publish"><i class="iconfont icon-fabu2"></i>&nbsp;&nbsp;寫文章</a>
                    </li>
                    <li th:if="${session.user==null}">
                        <a href="/login">登錄</a>
                    </li>
                    <li class="dropdown" th:if="${session.user!=null}">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span th:text="${session.user.name}"></span><span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="/main/personal">個人中心</a></li>
                            <li><a href="/main/article">我的文章</a></li>
                            <li><a href="/main/message">我的消息</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="/logout">退出登錄</a></li>
                        </ul>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
</div>
</body>
</html>
底部分頁欄pagination.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!--定義一個div然後起個名字,用於後面引入使用-->
<div th:fragment="page">
    <!--分頁-->
    <nav aria-label="Page navigation" class="page-right" th:if="${paginationInfo.countPage != 0}">

        <ul class="pagination">
            <li th:if="${paginationInfo.isHasFirstPage()}">
                <a th:href="@{${#httpServletRequest.getContextPath()}(page=1)}" aria-label="First">
                    <span aria-hidden="true">&lt;&lt;</span>
                </a>
            </li>
            <li th:if="${paginationInfo.isHasPrePage()}">
                <a th:href="@{${#httpServletRequest.getContextPath()}(page=${paginationInfo.page-1})}" aria-label="Previous">
                    <span aria-hidden="true">&lt;</span>
                </a>
            </li>
            <li th:each="page: ${paginationInfo.getPageList()}" th:class="${page==paginationInfo.page}? 'active'"><a th:href="@{${#httpServletRequest.getContextPath()}(page = ${page})}" th:text="${page}"></a></li>
            <li th:if="${paginationInfo.isHasNextPage()}">
                <a th:href="@{${#httpServletRequest.getContextPath()}(page=${paginationInfo.page+1})}" aria-label="Next">
                    <span aria-hidden="true">&gt;</span>
                </a>
            </li>
            <li th:if="${paginationInfo.isHasLastPage()}">
                <a th:href="@{${#httpServletRequest.getContextPath()}(page=${paginationInfo.getCountPage()})}" aria-label="Last">
                    <span aria-hidden="true">&gt;&gt;</span>
                </a>
            </li>
        </ul>
    </nav>
    <div class="noArticle" th:if="${paginationInfo.countPage == 0}">暫無數據,點擊 <a th:href=" @{/publish}">這裏</a>編寫第一篇文章吧</div>
</div>
</body>
</html>

我們可以使用如下方式進行引入:

<!-- 使用thymelaf的th:replace語法進行引入,::之前是html頁面名字,後面是之前起的div名字-->
<div th:replace="pagination :: page"></div>
我的文章列表頁面佈局article.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- 踩過的坑,springboot引入靜態資源路徑不要加/static/,否則會報404-->
    <title>個人博客</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" href="/bootstrap-3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/community.css">
    <link rel="stylesheet" href="//at.alicdn.com/t/font_1643567_rm1fqucxtan.css">
    <link rel="stylesheet" href="/bootstrap-3.3.7/css/bootstrap-theme.min.css">
    <script src="/jquery-1.12.4/jquery-1.12.4.min.js"></script>
    <script src="/bootstrap-3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div th:replace="navigation :: nav"></div>

<div class="row main">
    <div class="col-lg-9 col-md-12 col-sm-12 col-xs-12 col-left">
        <div class="row">
            <div class="col-lg-3 col-md-3 col-sm-3 col-xs-3"><h4><i class="iconfont icon-liebiao"></i>&nbsp;我的文章</h4></div>
        </div>
        <div class="media media_list" th:each="article : ${myArticleList}">
            <div class="media-left">
                <a href="#">
                    <img class="media-object img-rounded" th:src="${article.user.getAvatarUrl()}"  >
                </a>
            </div>
            <div class="media-body">
                <h4 class="media-heading" th:text="${article.title}">標題標題</h4>
                <span th:text="${article.answerCount}"></span> 個回覆 • <span th:text="${article.readCount}"></span> 次瀏覽 • 發佈時間<span th:text="${#dates.format(article.createTime,'yyyy-MM-dd HH:mm:ss')}"></span>
            </div>
        </div>

        <!-- 分頁 -->
        <div th:replace="pagination :: page"></div>
    </div>
    <div class="col-lg-3 col-md-12 col-sm-12 col-xs-12">
        <div class="list-group">
            <a href="/main/personal" th:class="${selection=='personal'?'active list-group-item':'list-group-item'} ">個人中心</a>
            <a href="/main/article" th:class="${selection=='article'?'active list-group-item':'list-group-item'} ">我的文章</a>
            <a href="/main/message" th:class="${selection=='message'?'active list-group-item':'list-group-item'} ">我的消息<span class="badge">4</span></a>
        </div>
    </div>
</div>
</body>
</html>
MainSelectionController.java
@Controller
public class MainSelectionController{

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private PaginationService paginationService;

    @GetMapping("/main/{selection}")
    public String article(@PathVariable(name = "selection") String selection,
                          @RequestParam(name = "page" ,defaultValue = "1") int page,
                          @RequestParam(name = "size" ,defaultValue = "5") int size,
                          HttpServletRequest request,
                          Model model){
        //檢查用戶是否登錄
        User user = (User) request.getSession().getAttribute("user");
        if(user==null){
            return "redirect:/";
        }
        int totalCount = articleMapper.count(user.getId());
        PaginationDTO paginationDTO =  paginationService.getPageInfo(page,size,totalCount);
        model.addAttribute("paginationInfo",paginationDTO);
        switch (selection){
            case "article":
            //修改articleMapper中的list列表方法,我們這裏傳入用戶id,如果id爲空則表示查詢所有,如果id不爲空,則查詢當前id的用戶信息
                List<Article> myArticleList = articleMapper.list(user.getId(), paginationDTO.getOffset(), size);
                model.addAttribute("myArticleList",myArticleList);
                //用於判斷當前的操作
                model.addAttribute("selection",selection);
                return "article";
            case "personal":
                return "personal";
            case "message":
                return "message";
            default:
                return "redirect:/";
        }
    }
}

ArticleMapper.java中查詢語句修改
 //查詢文章列表信息
 	//手動拼接sql語句,可做一些判斷
    @Select({"<script> " +
            "select u.*,a.id as aid,a.title,a.author_id, a.description,a.read_count,a.answer_count,a.like_count,a.create_time from article a,user u " +
            "where a.author_id = u.id " +
            "<if test='uId!=null'> and a.author_id=#{uId}</if>"+
            "limit #{page},#{size}"+
            "</script>"})
    @Results(id="ArticleUser",value = {
            @Result(id=true,property = "id",column = "id"),
            @Result(property = "id",column = "aid"),
            @Result(property = "title",column = "title"),
            @Result(property = "description",column = "description"),
            @Result(property = "authorId",column = "author_id"),
            @Result(property = "readCount",column = "read_count"),
            @Result(property = "answerCount",column = "answer_count"),
            @Result(property = "likeCount",column = "like_count"),
            @Result(property = "user.name",column = "name"),
            @Result(property = "user.avatarUrl",column = "avatar_url")
    })
    List<Article> list(@Param(value = "uId") Long uId, @Param(value = "page") int page,@Param(value = "size") int size);

    //查詢總文章數
    @Select({"<script> " +
            "select count(1) from article a,user u where a.author_id = u.id"+
            "<if test='uId!=null'> and u.id=#{uId}</if>"+
            "</script>"})
    int count(@Param(value = "uId") Long uId);

源碼

個人博客系統長期更新,所有源碼都放在了我的GitHub上了。

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