JavaWeb進階修煉手冊37---spring-security(三)ssm整合spring-security

1. 今日內容

2. ssm整合spring-security

ssm整合spring-security後,整個請求處理的流程圖:
在這裏插入圖片描述

* 流程詳解:
	1. 前端發送的所以請求都應該被`spring-security`攔截

	2. `spring-security`攔截後判斷該請求是否是登入請求,即否是`login-processing-url`指定的請求
		* 若不是,則一律響應給用戶login-page指定的頁面
		* 若是,則spring-security內部判斷該用戶是否有權限
			* 若沒有,則響應給用戶authentication-failure-url指定的頁面
			* 若有,則進行第3步

	3. 跳轉到default-target-url指定的頁面,跳轉成功後發出authentication-success-forward-url指定的請求給springmvc處理
		同時給用戶一個cookie,下次用戶再訪問資源時,spring-security根據cookie驗證,沒有cookie再跳轉到login-page指定的登入頁面

整合步驟:

配置ssm的web.xml文件,在該文件中加載spring-security.xml文件:

<!-- 配置加載類路徑的配置文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
</context-param>

再設置過濾器,哪些請求給spring-security先處理:

<!-- 攔截哪些請求給spring-security處理驗證 -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

這樣就整合好了,很簡單,但是你可能遇到一些問題,後面介紹。

我的ssm的web.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app.xsd"
         version="3.1">

    <!-- 配置加載類路徑的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
    </context-param>

    <!-- 配置監聽器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 解決中文亂碼過濾器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 攔截哪些請求給spring-security處理驗證 -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 前端控制器(加載classpath:springmvc.xml 服務器啓動創建servlet) -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化參數,創建完DispatcherServlet對象,加載springmvc.xml配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- 服務器啓動的時候,讓DispatcherServlet對象創建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

3. 整合過程中遇到的問題

3.1 404錯誤

3.1.1 第一種可能

如果你設置的登入頁面是.html頁面,則會出現404的問題。因爲spring-security要操作頁面,都是請求springmvc得到的。spring-security要跳轉到login-page="/ogin.html",會給springmvc發送/login的請求,而html是靜態資源,如果你不在spring-mvc.xml配置文件中設置允許該靜態資源允許被訪問,則會找不到資源。

所以,在spring-mvc.xml配置文件中編寫一下代碼:

<mvc:resources location="/login.html" mapping="/login.html"/>

然後就可以訪問資源了。不過個人建議用jsp比較好。

3.1.2 第二種可能

沒有在spring-security.xml中設置csrf:<security:csrf disabled="true"/>,其位置如下:

<security:http auto-config="true" use-expressions="false">
    <!-- 配置資料連接,表示任意路徑都需要ROLE_USER權限 -->
    <security:intercept-url pattern="/**" access="ROLE_USER"/>
    <security:form-login
            login-page="/pages/login.jsp"
            login-processing-url="/login"
            username-parameter="username"
            password-parameter="password"
            authentication-failure-url="/pages/failure.jsp"
            default-target-url="/pages/success.jsp"
            always-use-default-target="true"
            />
    <security:logout invalidate-session="true" logout-url="/logout"
                     logout-success-url="/login.jsp"/>

    <!-- 關閉CSRF,默認是開啓的 -->
    <security:csrf disabled="true"/>
</security:http>

3.2 頁面上顯示錯誤“重定向請求過多”

你用spring-security攔截了所有的請求,但是,請求登入界面的請求不應該被攔截,不然就陷入了死循環,一直重定向,造成此錯誤。
應該設置:<security:http security="none" pattern="/pages/login.jsp"/>,表示不過濾某些請求,其位置如下:

<!-- 配置不過濾的資源(靜態資源及登錄相關) -->
<security:http security="none" pattern="/pages/login.jsp"/>

<security:http auto-config="true" use-expressions="false">
    <!-- 配置資料連接,表示任意路徑都需要ROLE_USER權限 -->
    <security:intercept-url pattern="/**" access="ROLE_USER"/>
    <security:form-login
            login-page="/pages/login.jsp"
            login-processing-url="/login"
            username-parameter="username"
            password-parameter="password"
            authentication-failure-url="/pages/failure.jsp"
            default-target-url="/pages/success.jsp"
            always-use-default-target="true"
            />
    <security:logout invalidate-session="true" logout-url="/logout"
                     logout-success-url="/login.jsp"/>

    <!-- 關閉CSRF,默認是開啓的 -->
    <security:csrf disabled="true"/>
</security:http>

3.3 無論登入成功還是失敗一直重定向在登入界面

3.3.1 第一個可能

出現這個原因,你應該和我一樣用的是jsp文件,我都網上翻了各種資料,找了好幾個小時,沒找到。第二天自己無意間把錯誤找到了:

在登入的login.jsp頁面中,登入的請求必須要絕對請求路徑,不能用相對請求路徑:

必須這樣:
<form action="${pageContext.request.contextPath}/login" method="post">

不能這樣:
<form action="login" method="post">

3.3.2 第二個可能

登陸界面的請求與login-processing-url請求的名字不一樣:

<form action="${pageContext.request.contextPath}/login" method="post">

login-processing-url="/login"

這裏 action="${pageContext.request.contextPath}/XXX" 和 login-processing-url="/XXX"
這裏的XXX必須一致

這樣基本上沒有問題了。

4. 最後附上我的ssm整合spring-security項目

項目結構:
在這裏插入圖片描述
效果:
在這裏插入圖片描述

4.1 web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app.xsd"
         version="3.1">

    <!-- 配置加載類路徑的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
    </context-param>

    <!-- 配置監聽器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 解決中文亂碼過濾器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 攔截哪些請求給spring-security處理驗證 -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 前端控制器(加載classpath:springmvc.xml 服務器啓動創建servlet) -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化參數,創建完DispatcherServlet對象,加載springmvc.xml配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- 服務器啓動的時候,讓DispatcherServlet對象創建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

4.2 spring-security.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security.xsd">

    <!-- 配置不過濾的資源(靜態資源及登錄相關) -->
    <security:http security="none" pattern="/login.do"/>
    <security:http security="none" pattern="/login.do?error=a"/>

    <security:http auto-config="true" use-expressions="false">
        <!-- 配置資料連接,表示任意路徑都需要ROLE_USER權限 -->
        <security:intercept-url pattern="/**" access="ROLE_USER"/>

        <!-- 自定義登陸頁面
            login-page 登陸頁面
            login-processing-url : 發送登陸請求數據的url
            authentication-failure-url 用戶權限校驗失敗後後纔會跳轉到這個頁面,
            default-target-url 登陸成功後跳轉的頁面。
            always-use-default-target: true 適用於後臺管理系統,防止訪問歷史記錄
                                       false 適用於前臺頁面,提升用戶體驗
        -->
        <security:form-login
                login-page="/login.do"
                login-processing-url="/login"
                username-parameter="username"
                password-parameter="password"
                authentication-failure-url="/login.do?error=a"
                default-target-url="/pages/success.jsp"
                always-use-default-target="true"
                />

        <!-- 登出:
            invalidate-session 是否刪除session
            logout-url:登出處理鏈接
            logout-successurl:登出成功頁面
            注:登出操作 只需要鏈接到 logout即可登出當前用戶
        -->
        <security:logout
                invalidate-session="true"
                logout-url="/logout"
                logout-success-url="/login.do?logout"/>

        <!-- 關閉CSRF,默認是開啓的 -->
        <security:csrf disabled="true"/>
    </security:http>

    <!-- 在內存中構造用戶們 -->
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{noop}user" authorities="ROLE_USER"/>
                <security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

4.3 spring-mvc.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/mvc
               http://www.springframework.org/schema/mvc/spring-mvc.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 掃描controller的註解,別的不掃描 -->
    <context:component-scan base-package="cn.wanghao.springSecurity.controller"></context:component-scan>

    <!-- 開啓對SpringMVC註解的支持 -->
    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- 配置視圖解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- JSP文件所在的目錄 -->
        <property name="prefix" value="/pages/"></property>
        <!-- 文件的後綴名 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 設置靜態資源不過濾 -->
    <!--<mvc:resources location="/css/" mapping="/css/**"/>-->
    <!--<mvc:resources location="/images/" mapping="/images/**"/>-->
    <!--<mvc:resources location="/js/" mapping="/js/**"/>-->

</beans>

4.4 login.jsp文件

<%--
  Created by IntelliJ IDEA.
  User: WangHao
  Date: 2020/4/4
  Time: 16:24
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="post">
    <label>用戶名:</label> <input type="text" name="username"> <br>
    <label>密碼:</label>  <input type="password" name="password"> <br>
    <input type="submit" value="提交"> <input type="reset" value="重置"/>
    <span>
        <c:if test="${not empty error}">
            <div>${error}</div>
        </c:if>
		<c:if test="${not empty logout}">
            <div>${logout}</div>
        </c:if>
    </span>
</form>
</body>
</html>

4.5 success.jsp

<%--
  Created by IntelliJ IDEA.
  User: WangHao
  Date: 2020/4/4
  Time: 14:18
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    恭喜您,自定義登入設置成功!<br>
    <a href="${pageContext.request.contextPath}/logout">註銷</a>
</body>
</html>

4.6 LoginController.java

package cn.wanghao.springSecurity.controller;

import cn.wanghao.springSecurity.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController {

    /**
     * 處理登入請求
     *  1. 不帶error、logout參數時,跳轉到登入頁面
     *  2. 帶error時,表示登入失敗,需要返回給前端登陸失敗的信息
     *  3. 帶logout時,表示註銷,需要返回給前端註銷成功的信息
     * @param error
     * @param logout
     * @return 跳轉登陸頁面
     */
    @RequestMapping("/login.do")
    public ModelAndView login(
            @RequestParam(value = "error", required = false)String error,
            @RequestParam(value = "logout", required = false)String logout) {
        ModelAndView model = new ModelAndView();
        if (error != null) {
            model.addObject("error", "登入失敗!");
        }

        if (logout != null) {
            model.addObject("logout", "註銷成功!");
        }
        model.setViewName("login");
        return model;
    }

}

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