SpringMVC 07: WEB-INF下的資源存取 + SpringMVC攔截器

2022-09-03 15:00:46

WBE-INF目錄下的資源存取

  • 專案設定和Spring部落格集(指SpringMVC 02)中設定一樣

  • 出於對網站資源的安全性保護,放在WBE-INF目錄下的資源不可以被外部直接存取

  • 在WEB-INF/jsp/下新建index.jsp和main.jsp,作為WEB-INF目錄下的資源

  • 部署並啟動tomcat,根據2個資源的位置,嘗試直接在位址列存取

  • 結果如下,兩個資源均存取不到

  • 修改springmvc.xml中檢視解析器如下
    <!-- 新增檢視解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 設定字首-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 設定字尾-->
        <property name="suffix" value=".jsp"/>
    </bean>
  • 新增控制器:WebInFAction,兩個action方法分別轉發請求WEB-INF下的相應資源
package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class WebInfAction {

    @RequestMapping("/showIndex")
    public String showIndex(){
        return "index";
    }

    @RequestMapping("/showMain")
    public String showMain(){
        return "main";
    }
}

  • 分別存取localhost:8080/showIndex.action和localhost:8080/showMain.action,成功存取WEB-INF下的資源

  • 由於在web.xml中設定了接受處理的請求的格式"*.action",所以如果請求想要被處理必須以".action"結尾,修改該請求通配條件為"/",即對所有請求都接受處理
  • 分別存取localhost:8080/showIndex和localhost:8080/showMain,去掉存取字尾後,也可以成功存取WEB-INF下的資源

  • 雖然通過action轉發比直接輸入資源地址要安全一些,但是如果直接輸入:localhost:8080/showMain也可以直接存取到WEB-INF下的資源,所以上述經action方法轉發的方式其實也不安全。
  • 我們嘗試增加一個登陸驗證,使得只有登陸成功的使用者才可以存取到WEB-INF下的main.jsp資源
  • webapp/index.jsp:點選超連結,向伺服器請求登陸頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index.jsp</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/showLogin">去登陸</a>
</body>
</html>
  • WEB-INF/jsp/新增login.jsp:作為登陸頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login.jsp</title>
</head>
<body>

<!-- 完成登陸資訊的提交 -->
    
<h1>登陸</h1>
<form action="${pageContext.request.contextPath}/login" method="post">
    使用者名稱:<input type="text" name="username">
    密碼:<input type="password" name="password">
    <input type="submit" value="提交">
</form>
    
<!-- 如果登陸失敗,顯示登陸失敗資訊-->
${msg}
    
</body>
</html>
  • 控制器WebInfAction如下:新增登陸頁面的轉發功能,以及對登陸資訊的驗證判斷功能
package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class WebInfAction {

    @RequestMapping("/showIndex")
    public String showIndex(){
        return "index";
    }

    @RequestMapping("/showMain")
    public String showMain(){
        return "main";
    }

    @RequestMapping("/showLogin")
    public String showLogin(){
        return "login";//轉發並返回登陸頁面
    }

    //對登陸資訊進行判斷,使用者資訊符合是轉發跳轉到main.jsp,資訊不符合時,填入登陸失敗的資訊並重新轉發到登陸頁面
    @RequestMapping("/login")
    public String login(String username, String password, HttpServletRequest request){
        if(username.equals("荷包蛋") && password.equals("hebaodan")){
            return "main";
        }else{
            request.setAttribute("msg", "使用者名稱或密碼錯誤");
            return "login";
        }
    }
}
  • 部署並啟動tomcat測試
  • 當登陸資訊錯誤時

  • 當登陸資訊正確時

  • 但是如果直接存取:localhost:8080/showMain,仍然可以越過登陸,直接存取WEB-INF下的資源,仍然不安全

SpringMVC攔截器

  • 攔截器執行原理

  • 攔截器的作用:

  • 針對請求和響應進行額外的處理,在請求和響應的過程中新增預處理,後處理和最終處理

  • 攔截器執行的時機:

  • preHandle():在請求被處理之前進行操作,即預處理

  • postHandle():在請求被處理之後,但結果還沒有渲染前進行操作,可以改變響應結果,即後處理

  • afterCompletion:所有的請求響應結束後執行善後工作,清理物件,關閉資源,即最終處理

  • 攔截器實現的兩種方式:

  • 繼承HandlerInterceptorAdapter父類別

  • 或者實現HandlerInterceptor介面(推薦使用介面方式:java遵循單繼承,不要輕易的去繼承那些非核心業務的父類別,否則就無法再繼承其他業務類了,對於某些附加功能,去實現相應介面就可以了,因為允許實現很多介面)

  • 攔截器的實現步驟:

  • 改造登陸方法,在session中儲存使用者資訊,用於進行許可權驗證

  • 開發攔截器的功能,實現HandlerInterceptor介面,重寫preHandle()方法,進行登陸資訊驗證

  • 在springmvc.xml檔案中註冊攔截器

  • 使用攔截器繼續改造上述WEB-INF下資源的安全存取:

  • WebInfAction中的login方法修改如下:登陸成功後,向session域中存放使用者資訊,用於後續安全驗證

    @RequestMapping("/login")
    public String login(String username, String password, HttpServletRequest request){
        if(username.equals("荷包蛋") && password.equals("hebaodan")){
            //如果登陸成功,則設定session資訊
            request.getSession().setAttribute("user", username);
            return "main";
        }else{
            request.setAttribute("msg", "使用者名稱或密碼錯誤");
            return "login";
        }
    }
  • 新增登陸攔截器
package com.example.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 登陸攔截器
 */
public class LoginInterceptor implements HandlerInterceptor {
    //預處理攔截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //驗證session資訊,判斷是否登陸成功
        if(request.getSession().getAttribute("user") == null){
            request.setAttribute("msg", "還未登陸,清先登陸");
            //打回登陸頁面(頁面轉發實現頁面跳轉)
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
            return false;//驗證資訊未通過
        }
        return true;//驗證資訊通過,放行
    }
}
  • 在springmvc.xml中新增對攔截器的註冊
    <!-- 設定SpringMVC攔截器-->
    <mvc:interceptors>
        <!-- 可以設定多個攔截規則,形成攔截鏈-->
        <mvn:interceptor>
            <!-- 對映要攔截的請求:所有請求-->
            <mvn:mapping path="/**"/>
            <!-- 設定要放行的請求:有些必備的登陸請求不能攔截-->
            <mvn:exclude-mapping path="/showLogin"/>
            <mvn:exclude-mapping path="/login"/>
            <!-- 設定實現攔截器功能的實現類-->
            <bean class="com.example.interceptor.LoginInterceptor"/>
        </mvn:interceptor>
    </mvc:interceptors>
  • 部署並啟動tomcat測試
  • 當越過登陸直接存取WEB-INF下的資源時,未能通過驗證,被打回登陸頁面

  • 登陸成功後,新建視窗,直接存取,此時session域中資訊還未過期,成功通過驗證