攔截器
爲什麼使用攔截器,之前我們做的所有頁面,用戶只要輸入正確的訪問地址就可以進行一系列操作,儘管我們做了後臺驗證,但是這仍是不安全的,比如,如果用戶沒有登錄就訪問之前的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> 寫文章</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"><<</span>
</a>
</li>
<li th:if="${paginationInfo.isHasPrePage()}">
<a th:href="@{${#httpServletRequest.getContextPath()}(page=${paginationInfo.page-1})}" aria-label="Previous">
<span aria-hidden="true"><</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">></span>
</a>
</li>
<li th:if="${paginationInfo.isHasLastPage()}">
<a th:href="@{${#httpServletRequest.getContextPath()}(page=${paginationInfo.getCountPage()})}" aria-label="Last">
<span aria-hidden="true">>></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> 我的文章</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上了。