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;
}
}