1. 程式人生 > >Spring boot+Spring security5.0+thymeleaf登入表單POST方式無法提交403或自動轉為GET方式的解決

Spring boot+Spring security5.0+thymeleaf登入表單POST方式無法提交403或自動轉為GET方式的解決

一、前言

    我這幾天看到檢視這篇博文的人比較多,特意更新了兩種解決第二個問題辦法。

    這兩天看隔壁組專案,由於我自己專案和他們專案一樣使用的Spring boot基礎框架,想看看有什麼值得學習的地方,結果就看到人家的登入表單可以正常分GET和POST提交,也沒做什麼特別的處理,唯一的區別就是人是用Ajax中並submit方法提交的。當時我的專案在登入模組也分GET和POST兩種請求方式的控制層方法。但我的POST方法直接通過表單形式提交的話會有本文標題的問題。

二、問題分析與解決

  1. 您訪問主頁,然後單擊連結。
  2. 請求轉到伺服器,伺服器確定您已請求受保護的資源。
  3. 由於您目前尚未通過身份驗證,因此伺服器會發回一個響應,指示您必須進行身份驗證。響應將是HTTP響應程式碼,或重定向到特定網頁。
  4. 根據身份驗證機制,您的瀏覽器將重定向到特定的網頁,以便您可以填寫表單,或者瀏覽器將以某種方式檢索您的身份(通過BASIC身份驗證對話方塊,cookie,X.509證書等) )。
  5. 瀏覽器將向伺服器發回響應。這將是包含您填寫的表單內容的HTTP POST,或者包含您的身份驗證詳細資訊的HTTP標頭。
  6. 接下來,伺服器將決定所呈現的憑證是否有效。如果它們有效,則下一步將會發生。如果它們無效,通常會要求您的瀏覽器再次嘗試(因此您將返回上面的第二步)。
  7. 將重試您進行身份驗證過程的原始請求。希望您已通過足夠授權的許可權進行身份驗證以訪問受保護資源。如果您有足夠的訪問許可權,請求將成功。否則,您將收到HTTP錯誤程式碼403,這意味著“禁止”。

     這裡說的很清楚是可以POST請求方式傳到後臺的。結合文件中關於CSRF的介紹,基本可以確定是CRSF機制轉發後POST變成了GET(這句沒錯,但是有坑)。

    處理這種CSRF問題(此處可以解決POST請求報403的錯誤)有多種解決方案,如下:

    第一種方法,也是官方推薦使用的。form 表單使用 th:action 屬性, thymeleaf 會自動在 form 表單中生成 _csrf 隱藏域

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
 ...
    <form class="form-signin" th:action="@{login}" action="login" method="post">
    ...
    </form>
 ...

    第二種方法,手動新增隱藏域。

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>

     第三種方法,加在請求頭部分

    <meta name="_csrf" th:content="${_csrf.token}"/>
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>

    第四種辦法,直接禁掉CSRF.。這個方法太極端。禁用方式不放出來了,總之強烈不推薦。

    第五種辦法,增加例外,讓CSRF直接通過。

http.csrf().ignoringAntMatchers("/login")

POST請求403的問題通過設定以上引數就可以解決了

下面解決POST登入表單直接提交後臺接收時變成GET的問題

    還記得上面講到CRSF時說的坑嗎?上面我們懷疑是POST表單提交後經由CRSF機制轉發後最終提交給後臺的是GET請求方式,由於不能正確提交登入資訊,導致不管怎樣反覆會跳到登入頁面。

   在說這個問題前,先列舉兩個它的野路子解法。最後再分析官方解法。

A、.do應用解決

原始碼如下:

頁面

<form class="form-signin" th:action="@{login}" action="login" method="post">
            <div class="form-group">
                <label for="username">賬號</label>
                <input type="text" class="form-control" name="username" value="" placeholder="賬號"/>
            </div>
            <div class="form-group">
                <label for="password">密碼</label>
                <input type="password" class="form-control" name="password" placeholder="密碼"/>
            </div>
            <input type="submit" id="login" value="Login" class="btn btn-primary"/>
        </form>

控制層

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

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

import com.music.league.service.SignManager;

@Controller
public class SignController {
	
	@Resource
	SignManager signManager;
	
	@RequestMapping(value="/login",method = RequestMethod.GET)
	public String sign(){
		System.out.println("Judge!!");
		return "login";
	}
	@RequestMapping(value="/login",method = RequestMethod.POST)
	public String sign(HttpServletRequest request){
		System.out.println("登入方法入參:"+request.getParameter("userName")+":"+request.getParameter("password"));
		return "welcome";
	}
}

SpringMVC配置

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
	@Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }
	@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }
}

Spring security配置

 http.authorizeRequests()
		.anyRequest().authenticated()
		.and().formLogin()
		.loginPage("/login")
		//設定預設登入成功跳轉頁面
		.defaultSuccessUrl("/welcome").failureUrl("/login?error").permitAll()

把上述幾處login的請求,除了控制層的POST方式登入方法和 th:action="@{login},其他的login都改成login.do。這樣做是為了通過除請求Method屬性外的第二種辦法去區分開get和post請求的不同。

B、引數差異

   這種方法是基於上面web請求鏈第二步請求本身無參的性質硬搞。當login方法無參時,自動處理走GET,大家和和美美。當有參時,手動寫邏輯去掉POST。不過可能會有安全問題,所以不太推薦。   

聽說用ModelAndView也可以解決,我簡單試了一下,好像不行哦。anyway,野路子解法到此為止。

正經的問題本質原因,如下:

    在翻看Spring security5.0官方文件的時候,發現文件中提到Spring security特別為大家提供了一個登入驗證表單(具體哪句找不到了,文件連線點我),傾力奉獻撒!繼續讀文件,通過前後文的聯絡,官方的表單頁面程式碼大概是這樣的(這段程式碼在文件5.3節末尾):

<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post">       1
	<c:if test="${param.error != null}">        2
		<p>
			Invalid username and password.
		</p>
	</c:if>
	<c:if test="${param.logout != null}">       3
		<p>
			You have been logged out.
		</p>
	</c:if>
	<p>
		<label for="username">Username</label>
		<input type="text" id="username" name="username"/>	4
	</p>
	<p>
		<label for="password">Password</label>
		<input type="password" id="password" name="password"/>	5
	</p>
	<input type="hidden"                        6
		name="${_csrf.parameterName}"
		value="${_csrf.token}"/>
	<button type="submit" class="btn">Log in</button>
</form>

    看到官方這麼貼心,然後我的表單整體樣式和它基本一致,就再次仔細看了一下。發現除了我的頁面寫的是loginName其他沒區別。於是抱著試一試的態度改成username。MMP,完美的POST請求進入控制層POST請求登入方法。MMP...要不要限制這麼死?CRSF底層實現我找不到,但問題就是在這個特殊的登入,官方給登入做了特別處理。

    經過進一步的驗證發現即使實體中寫的是userName或者loginName,只要你想在登入模組直接通過表單方式提交的話,就必須是username。

    如果改了名字還是無效,那麼還有兩個解決方法。就是改為小寫的username後,執行下面兩個方法之一。推薦第二個。

    第一個辦法是手動給指定一下登入請求的處理。就是在loginPage後面加一個loginProcessingUrl,內容是你登陸方法的控制層RequestMapping中的value和登入方法。如果你沒有寫RequestMapping的話,那就是控制層的Spring自動轉換值,一般是去掉Contrller的駝峰命名。比如SignController,這裡寫sign就行。

    第二個辦法是把defaultSuccessUrl改為successForwardUrl,這個辦法的原理就是把直接跳轉頁面改為跳轉後臺方法。defaultSuccessUrl("/login")改為successForwardUrl("/sign/login")。建議用這個,因為這個依舊會照常按security過濾器鏈自動載入許可權,第一個需要手動新增許可權,否則一直是匿名。

.loginPage("/login").loginProcessingUrl("/sign/login")

三、注意事項

    .do的應用解決也要把登入名改成小寫的username,官方的3.x版本文件寫的是must,否則無法通過表達提交

相關推薦

Spring boot+Spring security5.0+thymeleaf登入POST方式無法提交403自動轉為GET方式解決

一、前言     我這幾天看到檢視這篇博文的人比較多,特意更新了兩種解決第二個問題辦法。     這兩天看隔壁組專案,由於我自己專案和他們專案一樣使用的Spring boot基礎框架,想看看有什麼值得學習的地方,結果就看到人家的登入表單可以正常分GET和POST提交,也沒

新增Bootstrap驗證之後無法提交

問題描述:一個登陸頁面,新增BootstrapValidator表單驗證之後,輸入格式驗證正確,但點選登陸後沒有提交表單,登陸按鈕變為不可選,再次編輯任意一個輸入框之後才能成功提交。 原來的登陸按鈕是這麼寫的:<button type="submit" name="s

如何使用Google作為認證方配置Spring Boot 2 Security5整合的OAuth2登入我們自己的工程專案------範例1

Google客戶端授權生成client-id和client-secret我們需要登入以下地址 https://console.developers.google.com 第1部分: 範例工程專案結構如下: Enabling OAuth 2 login Supp

spring-boot-devtools 2.0熱部署失效

內部 lease 沒有 tools 什麽 ren class -s 原因 <parent> <groupId>org.springframework.boot</groupId> <arti

Spring Boot模板引擎實戰——Thymeleaf

一 點睛 1 靜態資源訪問 在我們開發Web應用的時候,需要引用大量的js、css、圖片等靜態資源。 2 預設配置 Spring Boot預設提供靜態資源目錄位置需置於classpath下,目錄名需符合如下規則: /static /public /

【重磅】Spring Boot 2.1.0 權威釋出

如果這兩天登入 start.spring.io/ 就會發現,Spring Boot 預設版本已經升到了 2.1.0。這是因為 Spring Boot 剛剛釋出了 2.1.0 版本,我們來看下 Spring Boot 2 釋出以來第一個子版本都發布了哪些內容? 2.1 中的新特性 將spring-bo

【重磅】Spring Boot 2.1.0 權威發布

-o 發布 cond apache 免費 servle executor 朋友 情況下 如果這兩天登錄 https://start.spring.io/ 就會發現,Spring Boot 默認版本已經升到了 2.1.0。這是因為 Spring Boot 剛剛發布了 2.1.

spring boot security 防止使用者重複登入(原創)

原理:在認證成功通過後,在顯示登入成功頁面之前,也就是在SavedRequestAwareAuthenticationSuccessHandler類中操作。 新增一個集合sessionMap 用於儲存認證成功的會話,鍵名為會話ID, 每次有使用者登入認證通過都要判斷一下是否重複登入

Spring Boot 2.1.0 已釋出,7 個重大更新!

1、第三方類庫升級 Hibernate 5.3 Micrometer 1.1 Reactor Californium Spring Data Lovelace Spring Framework 5.1 Tomcat 9 Un

Spring Boot 2.1.0 已釋出,7 個重大更新你需要了解

Spring Boot 2.1.0 在 10 月底就釋出了,我們來看下 Spring Boot 2.1.0 都更新了什麼,每一個 Java 技術人都值得關注。 棧長其實早就看到了更新了,現在才有時間來更新下。 1、第三方類庫升級 Hibernate 5.3 Micrometer 1.1 Reacto

Spring Boot 2.1.0 已發布,7 個重大更新你需要了解

pool for rep ctu err 自動配置 表示 req spring Spring Boot 2.1.0 在 10 月底就發布了,我們來看下 Spring Boot 2.1.0 都更新了什麽,每一個 Java 技術人都值得關註。 棧長其實早就看到了更新了,現在才有

Spring Boot、MyBatis、Thymeleaf 簡單增刪改查

Spring Boot、MyBatis、Thymeleaf 簡單增刪改查 原始碼地址:https://gitee.com/Azure_Sky/SpringStudy.git 一、建立專案 1。新建專案 二、建立資料庫及資料表 資料庫名為thymeleaf、

Spring Boot學習系列(五)------Thymeleaf

前言 在Web開發中,頁面是我們和使用者互動的主要方式,在以前SpringMVC的專案裡面,因為有tomcat容器的存在,我們可以使用jsp來返回頁面資料,現在我們使用了Spring Boot,它是預設不支援jsp的,所以我們可以選擇其他的模板引擎來使用,現在市

spring boot 專案中使用thymeleaf模板,將後臺資料傳遞給前臺介面。

1、將後臺資料傳遞給前臺有很多種方式,可以將後臺要傳遞的資料轉換成json格式,去傳遞給前臺,也可以通過model形式去傳遞出去,這篇部落格主要是使用thymeleaf模板,將後臺資料傳遞給前臺。 2、首先要在spring boot 專案中新增如下依賴:

Spring Boot + Spring Cloud 實現許可權管理系統 後端篇(十七):登入驗證碼實現(Captcha)

<el-form-item > <el-col :span="12"> <el-form-item prop="captcha"> <el-input type="test" v-model="loginForm.captcha" auto-

spring boot 使用ssm框架之登入

Spring Boot 開發Spring Boot的主要動機是簡化配置和部署spring應用程式的過程。 Spring Boot的主要特點: 建立獨立的Spring應用程式 直接嵌入Tomcat,Jetty或Undertow(無需部署WAR檔案) 提供“初始”的PO

Spring boot+Security OAuth2 自定義登入和授權頁面

Spring boot+Security OAuth2 自定義登入和授權頁面   1. 依賴 <!---------thymeleaf 模板引擎--------> <dependency> <groupId>org.springfra

Spring Boot-2.1.0整合Mybatis(一)

1.首先匯入依賴 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spri

Spring Boot-2.1.0整合Mybatis

1.首先匯入依賴 <dependency> <groupId>org.mybatis.spring.boot</groupId&g

Spring Boot-2.1.0整合Druid監控資料庫

1.首先匯入依賴 <!--Druid依賴包--> <dependency> <groupId>com.alibaba</groupId> <artifact