整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Scrapy 爬蟲模擬登陸的3種策略

          Scrapy 爬蟲模擬登陸的3種策略

          crapy 爬蟲模擬登陸的3種策略

          1 Scrapy 爬蟲模擬登陸策略

          前面學習了爬蟲的很多知識,都是分析 HTML、json 數據,有很多的網站為了反爬蟲,除了需要高可用代理 IP 地址池外,還需要登錄,登錄的時候不僅僅需要輸入賬戶名和密碼,而且有可能驗證碼,下面就介紹 Scrapy 爬蟲模擬登陸的幾種策略。

          1.1 策略一:直接POST請求登錄

          前面介紹的爬蟲 scrapy 的基本請求流程是 start_request 方法遍歷 start_urls 列表,然后 make_requests_from_url方法,里面執行 Request 方法,請求 start_urls 里面的地址,使用的是 GET 方法,由于直接使用用戶名和密碼可以登錄,使用 POST 方法進行登錄。

          例子:人人網登錄

          登錄地址:http://www.renren.com/PLogin.do

          案例步驟:

          第一步:創建項目。

          在 dos下切換到目錄

          D:\scrapy_project

          新建一個新的爬蟲項目:scrapy startproject renren

          第二步:創建爬蟲。

          在 dos下切換到目錄。

          D:\scrapy_project\renren\renren\spiders

          用命令 scrapy genspider renren1 " renren.com" 創建爬蟲。

          第三步: 通過瀏覽器登錄人人網,使用 fiddler 抓包抓取登錄 post 請求的 data。

          第四步:編寫爬蟲文件。

          import scrapy

          # 登錄只需要提供 post 數據就可以登錄的,就可以用這種方法,

          # 下面示例:post 數據是賬戶密碼

          class Renren1Spider(scrapy.Spider):

          name="renren1"

          allowed_domains=["renren.com"]

          def start_requests(self):

          url='http://www.renren.com/PLogin.do'

          # FormRequest 是 Scrapy 發送 POST 請求的方法

          yield scrapy.FormRequest(

          url=url,

          formdata={"email" : "13554799060", "password" : "xny123"},

          callback=self.parse_page)

          # 回調方法,對返回的 response 進行處理(把response.body保存到 xiao.html 中)

          def parse_page(self, response):

          with open("xiao.html", "wb") as filename:

          filename.write(response.body)

          第五步:修改 settings 文件。

          設置爬蟲請求的默認頭信息。

          第六步:運行爬蟲。

          在 dos下切換到目錄

          D:\scrapy_project\renren\renren 下

          通過命令運行爬蟲 :scrapy crawl renren1

          第七步:查看結果。

          xiao.html 中顯示的內容正是登錄自己人人網之后的主頁內容,說明登錄成功。

          1.2 策略二:標準的模擬登陸

          標準的模擬登錄方法:

          1、首先發送登錄頁面的 get 請求,獲取到頁面里的登錄必須的參數。

          2、登錄必須的參數和賬戶密碼一起 post 到服務器,登錄成功。

          23.2.1 Cookie原理

          HTTP 是無狀態的面向連接的協議, 為了保持連接狀態, 標準的模擬登陸案例引入了 Cookie 機制。

          Cookie 是 http 消息頭中的一種屬性,包括:

          .Cookie 名字(Name)Cookie 的值(Value)

          .Cookie 的過期時間(Expires/Max-Age)

          .Cookie 作用路徑(Path)

          .Cookie 所在域名(Domain),使用 Cookie 進行安全連接(Secure)。

          前兩個參數是 Cookie 應用的必要條件,另外,還包括 Cookie 大小( Size,不同瀏覽器對Cookie 個數及大小限制是有差異的 )。

          23.2.2 模擬登陸

          爬取的網站:github (https://github.com/login)

          案例步驟:

          第一步:爬取前分析。

          打開 fiddler,接著我們打開 github 的登陸頁面(https://github.com/login ),輸入用戶名、密碼( 輸入錯誤的密碼 ),提交查看 fiddler 獲取的信息,結果入下:

          輸入用戶名和錯誤密碼獲取的 fiddler 結果:

          我們用 google 瀏覽器看源碼也可以看到 form 提交時會加入 authenticity_token 參數一起,如下圖:

          第二步:創建項目。

          在 dos下切換到目錄

          D:\scrapy_project

          新建一個新的爬蟲項目:scrapy startproject github

          第三步:創建爬蟲。

          在 dos下切換到目錄。

          D:\scrapy_project\github\github\spiders

          用命令 scrapy genspider gh "github.com" 創建爬蟲。

          第四步: 開始前的準備工作。

          (一)、在 scrapy.cfg 同級目錄下創建 pycharm 調試腳本 run.py,內容如下:

          # -*- coding: utf-8 -*-

          from scrapy import cmdline

          cmdline.execute("scrapy crawl github".split())

          (二)修改 settings 中的 ROBOTSTXT_OBEY=True 參數為 False,因為默認為 True,就是要遵守 robots.txt 的規則, robots.txt 是遵循 Robot協議 的一個文件,它保存在網站的服務器中,它的作用是,告訴搜索引擎爬蟲,本網站哪些目錄下的網頁不希望你進行爬取收錄。在 Scrapy 啟動后,會在第一時間訪問網站的 robots.txt 文件,然后決定該網站的爬取范圍。查看 robots.txt 可以直接網址后接 robots.txt 即可。

          例如百度:https://www.baidu.com/robots.txt

          修改 settings 文件。

          (三)模擬登陸時,必須保證 settings.py 里的 COOKIES_ENABLED (Cookies中間件) 處于開啟狀態。

          COOKIES_ENABLED=True

          第五步:編寫爬蟲文件-獲取 authenticity_token。

          首先要打開登陸頁面,獲取 authenticity_token,代碼如下:

          import scrapy

          class GithubSpider(scrapy.Spider):

          name='gh'

          allowed_domains=['github.com']

          def start_requests(self):

          urls=['https://github.com/login']

          for url in urls:

          # 重寫 start_requests 方法,通過 meta 傳入特殊 key cookiejar,爬取 url 作為參數傳給回調函數

          yield scrapy.Request(url, meta={'cookiejar': 1}, callback=self.github_login)

          def github_login(self, response):

          # 首先獲取authenticity_token,這里可以借助scrapy shell ”url“來獲取頁面

          # 然后從源碼中獲取到authenticity_token的值

          authenticity_token=response.xpath("http://input[@name='authenticity_token']/@value").extract_first()

          # 利用 scrapy 內置 logger 打印 info 信息

          self.logger.info('authenticity_token='+ authenticity_token)

          pass

          運行結果:

          通過運行的結果,可以看到我們已經獲取了 authenticity_token 的值,這一步重點要說明meta、cookiejar 和 logger。

          【meta】:字典格式的元數據,可以傳遞給下一個函數 meta。

          【cookiejar】:是 meta 的一個特殊的key,通過 cookiejar 參數可以支持多個會話對某網站進行爬取,可以對 cookie 做標記,1,2,3,4......這樣 scrapy 就維持了多個會話;

          【logger】:scrapy 為每個 spider 實例內置的日志記錄器。

          為了能使用同一個狀態持續的爬取網站, 就需要保存cookie, 使用cookie保存狀態, Scrapy 提供了 cookie 處理的中間件, 可以直接拿來使用,Scrapy 官方的文檔中給出了下面的代碼范例 :

          for i, url in enumerate(urls):

          yield scrapy.Request("http://www.example.com", meta={'cookiejar': i},

          callback=self.parse_page)

          def parse_page(self, response):

          # do some processing

          return scrapy.Request("http://www.example.com/otherpage",

          meta={'cookiejar': response.meta['cookiejar']},

          callback=self.parse_other_page)

          第六步:修改爬蟲文件- FormRequest(登錄表單提交)

          Scrapy 提供了 FormRequest 類,是 Request 類的擴展,專門用來進行 Form 表單提交。我們主要使用 FormRequest.from_response()方法來模擬簡單登陸,通過FormRequest.from_response 提交后,交給回調函數處理。代碼如下:

          import scrapy

          class GithubSpider(scrapy.Spider):

          name='gh'

          allowed_domains=['github.com']

          def start_requests(self):

          urls=['https://github.com/login']

          for url in urls:

          # 重寫 start_requests 方法,通過 meta 傳入特殊 key cookiejar,爬取 url 作為參數傳給回調函數

          yield scrapy.Request(url, meta={'cookiejar': 1}, callback=self.github_login)

          def github_login(self, response):

          # 首先獲取authenticity_token,這里可以借助scrapy shell ”url“來獲取頁面

          # 然后從源碼中獲取到authenticity_token的值

          authenticity_token=response.xpath("http://input[@name='authenticity_token']/@value").extract_first()

          # 利用 scrapy 內置 logger 打印 info 信息

          self.logger.info('authenticity_token='+ authenticity_token)

          # url 可以從 fiddler 抓取中獲取,dont_click 作用是如果是 True,表單數據將被提交,而不需要單擊任何元素。

          return scrapy.FormRequest.from_response(

          response,

          url='https://github.com/session',

          meta={'cookiejar': response.meta['cookiejar']},

          headers=self.headers,

          formdata={'utf8':'?',

          'authenticity_token': authenticity_token,

          'login': 'xxxxxx@qq.com',

          'password': 'xxxxxx'},

          callback=self.github_after,

          dont_click=True,

          )

          第七步:修改爬蟲文件- 偽裝頭部。

          為了更真實的模擬瀏覽器登陸網站,需要進行頭部偽裝, 在 scrapy 中 Request 和 FormRequest 初始化的時候都有一個 headers 字段, 可以自定義頭部, 這樣我們可以添加 headers 字段。

          # 頭信息直接從 fiddler 中復制出來

          headers={

          "Connection": "keep-alive",

          "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",

          "Referer": "https: // github.com /",

          "Content - Type": "application / x - www - form - urlencoded",

          }

          第八步:修改爬蟲文件- 增加回調函數,主要是登陸成功之后,獲取登錄之后返回的頁面(response)的元素進行斷言,驗證登錄結果。

          登錄之后,主頁如下:

          # 回調函數

          def github_after(self, response):

          # 獲取登錄頁面主頁中的字符串'Browse activity'

          list=response.xpath("http://a[@class=‘tabnav-tab selected‘]/text()").extract()

          # 如果含有字符串,則打印日志說明登錄成功

          if 'Browse activity' in list:

          self.logger.info('我已經登錄成功了,這是我獲取的關鍵字:Browse activity')

          else:

          self.logger.error('登錄失敗')

          第九步:整理完整的爬蟲文件

          import scrapy

          class GithubSpider(scrapy.Spider):

          name='gh'

          allowed_domains=['github.com']

          # 頭信息直接從 fiddler 中復制出來

          headers={

          "Connection": "keep-alive",

          "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",

          "Referer": "https: // github.com /",

          "Content - Type": "application / x - www - form - urlencoded",

          }

          def start_requests(self):

          urls=['https://github.com/login']

          for url in urls:

          # 重寫 start_requests 方法,通過 meta 傳入特殊 key cookiejar,爬取 url 作為參數傳給回調函數

          yield scrapy.Request(url, meta={'cookiejar': 1}, callback=self.github_login)

          def github_login(self, response):

          # 首先獲取authenticity_token,這里可以借助scrapy shell ”url“來獲取頁面

          # 然后從源碼中獲取到authenticity_token的值

          authenticity_token=response.xpath("http://input[@name='authenticity_token']/@value").extract_first()

          # 利用 scrapy 內置 logger 打印 info 信息

          self.logger.info('authenticity_token='+ authenticity_token)

          # url 可以從 fiddler 抓取中獲取,dont_click 作用是如果是 True,表單數據將被提交,而不需要單擊任何元素。

          return scrapy.FormRequest.from_response(

          response,

          url='https://github.com/session',

          meta={'cookiejar': response.meta['cookiejar']},

          headers=self.headers,

          formdata={'utf8':'?',

          'authenticity_token': authenticity_token,

          'login': '55666727@qq.com',

          'password': 'xny8816056'},

          callback=self.github_after,

          dont_click=True,

          )

          # 回調函數

          def github_after(self, response):

          # 獲取登錄頁面主頁中的字符串'Browse activity'

          list=response.xpath("http://a[@class=‘tabnav-tab selected‘]/text()").extract()

          # 如果含有字符串,則打印日志說明登錄成功

          if 'Browse activity' in list:

          self.logger.info('我已經登錄成功了,這是我獲取的關鍵字:Browse activity')

          else:

          self.logger.error('登錄失敗')

          第十步:查看運行的結果。

          通過運行的結果說明登錄成功。

          1.3 策略三:直接使用保存登陸狀態的 Cookie 模擬登陸

          如果實在沒辦法了,可以用策略三這種方法模擬登錄,雖然麻煩一點,但是成功率100%。

          案例步驟:

          第一步:創建項目。

          在 dos下切換到目錄

          D:\scrapy_project

          新建一個新的爬蟲項目:scrapy startproject renren2

          第二步:創建爬蟲。

          在 dos下切換到目錄。

          D:\scrapy_project\renren2\renren2\spiders

          用命令 scrapy genspider ren2 "renren.com" 創建爬蟲。

          第三步: 通過瀏覽器登錄人人網,使用 fiddler 抓包抓取登錄后的Cookis。

          第四步: 開始前的準備工作。

          (一)、在 scrapy.cfg 同級目錄下創建 pycharm 調試腳本 run.py,內容如下:

          # -*- coding: utf-8 -*-

          from scrapy import cmdline

          cmdline.execute("scrapy crawl renren".split())

          (二)修改 settings 中的 ROBOTSTXT_OBEY=True 參數為 False,因為默認為 True,就是要遵守 robots.txt 的規則, robots.txt 是遵循 Robot協議 的一個文件,它保存在網站的服務器中,它的作用是,告訴搜索引擎爬蟲,本網站哪些目錄下的網頁不希望你進行爬取收錄。在 Scrapy 啟動后,會在第一時間訪問網站的 robots.txt 文件,然后決定該網站的爬取范圍。查看 robots.txt 可以直接網址后接 robots.txt 即可。

          修改 settings 文件。

          (三)模擬登陸時,必須保證 settings.py 里的 COOKIES_ENABLED ( Cookies 中間件) 處于開啟狀態。

          COOKIES_ENABLED=True

          第五步: 編寫爬蟲文件。

          import scrapy

          class RenrenSpider(scrapy.Spider):

          name="renren"

          allowed_domains=["renren.com"]

          Cookies={

          "anonymid": "jlvxr345k9ondn",

          "wp_fold": "0",

          "depovince": "GW",

          "jebecookies": "3af719cc-f819-4493-bcb6-c967fc59f04a|||||",

          "_r01_": "1",

          "JSESSIONID": "abcwnUubDsWO467i0mgxw",

          "ick_login":"27af5597-30d7-469c-b7c4-184a6e335fcb",

          "jebe_key":"d1f5682d-03b4-46cd-87c0-dc297525ed11%7Ccfcd208495d565ef66e7dff9f98764da%7C1536628574466%7C0%7C1536628572944",}

          # 可以重寫 Spider 類的 start_requests 方法,附帶 Cookie 值,發送 POST 請求

          def start_requests(self):

          url='http://www.renren.com/PLogin.do'

          # FormRequest 是 Scrapy 發送 POST 請求的方法

          yield scrapy.FormRequest(url, cookies=self.Cookies, callback=self.parse_page)

          # 處理響應內容

          def parse_page(self, response):

          print("===========" + response.url)

          with open("xiao2.html", "wb") as filename:

          filename.write(response.body)

          第六步: 運行程序,查看運行結果。

          Xiao2.html 中顯示的內容正是登錄自己人人網之后的主頁內容,說明登錄成功。

          節重點來介紹一下JSON,JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式,我們稱之為JavaScript對象表示法。也就是說,JSON是一種格式。首先搞清楚三個概念,即什么是JSON字符串,什么是JavaScript對象,還有什么又叫做JSON對象?先來說一個事,在沒有JSON之前,前臺頁面和Java等語言充當的服務器層,到底是如何傳輸數據的呢?沒錯,是通過XML來傳輸的。比如一個登陸頁面。

          頁面上有用戶名和密碼兩個輸入框,當我點擊登錄按鈕,這兩個數據就會被傳遞到服務器層。那么,如何傳輸呢?如果用XML,也許是這樣的:

          <LoginData> <name>zhangsan</name> <password>123</password></LoginData>

          后臺接收到這個數據,然后就可以開始解析,最終拿到zhangsan和123兩個字面量。時間線再往前推,在XML還沒有出來的時候,怎么辦呢?聰明的程序開發人員則會規定幾種特殊的格式,拼接一個特殊的字符串,傳遞到后臺中去。比如像這樣的:

          "name=zhangsan&password=123"

          那么后臺的程序員也知道這個規則,如果是Java的話,就可以使用String的splite方法,先通過逗號把這個字符串分割成兩份,也就是變成:name=zhangsan還有password=123兩個字符串,然后再通過“=”分割,將“name=zhangsan”分割成“name”和“zhangsan”,把“password=123”分割成“password”和“123”兩部分。終于,到底還是拿到用戶名和密碼了。

          接下來還是談JSON,其實JSON就是一種數據格式。諸如:

          {
           key1 : value1 ,
           key2 : value2
          };
          

          這樣的格式就是JSON格式,它是一系列鍵值對的集合,不同的鍵值對之間用逗號分隔,最后一個鍵值對不需要加逗號。符合這種格式的字符串就是JSON字符串。比如:

          "{'name' : 'Jack'}"
          

          它歸根到底還是一個字符串,不是一個對象。而JSON對象,其實就是Javascript對象,我們可以通過字面值的方式直接創造一個對象,比如:

          var person={name : 'Jack'}
          

          等同于:

          var person={'name' : 'Jack'}
          

          在上邊這個例子中,name可加單引號,也可加雙引號,甚至可以什么都不加。而右邊的值必須是一個實實在在的東西,比如字符串,或者一個對象,甚至是一個函數。我們不考慮JS內部的對象機制,只是簡單地說明一下,是有這么個事情的。這就是所謂的JSON對象,也就是js對象。在JavaScript中,對象是鍵值對的集合,符合JSON格式。我們可以通過下面的方法,把JS對象轉換成JSON格式的字符串。

          var person={'name' : 'Jack'}
          alert(JSON.stringify(person));
          

          同樣,一個JSON格式的字符串,可以變成一個JS對象,如:

          console.log(JSON.parse("{\"name\":\"Jack\"}"));
          

          做個小結,JSON字符串就是符合JSON格式的字符串,他還是字符串,JSON對象就是JavaScript對象,我們推薦使用字面值的方式來創建一個JS對象。然后,JS對象和JSON字符串可以互相轉換。通過這一個特點,我們能夠實現JS對象的拷貝。一般來說,比如我有一個js對象。

          var person={'name' : 'Jack'}
          var person2=person;
          

          這樣做,并不是對象的復制,person2僅僅是一個指針,他和person一樣,指向了{'name' : 'Jack'}這一片內存空間。當person發生改變,person2必然也跟著改變。

          var person={'name' : 'Jack'}
          var person2=person;
          person.age=10; //給person動態地添加一個屬性
          alert(JSON.stringify(person2)); //person2也跟著變了
          

          那有沒有什么辦法可以實現對象的復制呢?一個好的解決方案就是,先把person轉換成JSON字符串,然后再轉成JS對象,這個時候就是另外一個JS對象了。比如:

          var person={'name' : 'Jack'}
          var person2=JSON.parse(JSON.stringify(person));
          person.age=10; //給person動態地添加一個屬性
          alert(JSON.stringify(person2)); //person2不變
          

          接下來說說js對象內容的訪問和操作,我們上面已經說了,JS對象中無非是一些鍵值對的集合,他更像是一個容器,既然是容器,自然有內容,我們如何訪問其中的內容呢?在上面的例子中,我們已經通過“對象.屬性名”的方式來訪問JS對象的具體內容。比如:

          var obj={
           id : 1
          };
          var id=obj.id;
          alert(id);
          

          另外一種方式,就是通過 對象["屬性名"] 來操作其內容。比如:

          var id=obj['id']
          

          可以用雙引號,也可以用單引號,看個人習慣了。在JS對象中,屬性名永遠都是字符串,雖然諸如這樣的代碼:

          var obj={
           id : 1
          };
          

          id沒有加上引號,但它實際上還是以字符串的形式被保存起來的。再說一遍,如果你要訪問和操作JS對象的內容,有兩種方式,第一種方式是用點,第二種方式則是用中括號。兩種方式如果做一個比較,顯然是第二種方式較為靈活,因為它是用字符串去找對應的鍵值對,而不是用一個標識符。比如剛才的例子,你這樣寫:

          var id=obj.id;
          

          我問你,obj.id中的id是什么?為了符合規范,id必須是標識符,你不能寫 obj.123 吧。這顯然是不合法,也無法運行通過的。比如,你能這樣寫嗎?

          var obj={
           123 : 'Hello JavaScript!'
          };
          var id=obj.123;
          alert(id);
          

          肯定不行,會報錯的:

          但是,如果你用中括號就可以:

          var obj={
           123 : 'Hello JavaScript!'
          };
          var id=obj['123'];
          alert(id);
          

          具體用那種方式,隨你喜好而定。

          現在,我們已經對JSON格式和JS對象有了一個比較充分的了解,我要在此拋出一個問題,有沒有什么辦法能夠獲取JS對象的屬性詳情呢?注意我的用詞,是屬性詳情,也就是說,比如有一個JS對象:

          var obj={
           message: 'Hello JavaScript!'
          };
          

          message就是它的屬性,關于這個屬性,有沒有什么詳細的描述信息呢?答案是有的,在JS中,有一個內置的Object對象,它給我們提供了一個getOwnPropertyDescriptor方法,可以看到某個對象的某個屬性的具體情況。你可以把這個理解為Java中的靜態類調用方法。我們可以這樣做:

          var obj={
           message : 'Hello JavaScript!'
          };
          

          console.log(Object.getOwnPropertyDescriptor(obj,'message'));

          可以看到,我們成功挖掘出了四個屬性,如果你不明白我在說什么,我就說得更加直白一些,就是說,

          var obj={
           message: 'Hello JavaScript!'
          };
          

          obj里面有一個屬性message,而message又有四個描述性的東西,分別是configurable(可配置),enumerable(可枚舉),value(值),還有 writable(可寫入)。這四樣東西,專業術語叫做屬性描述符,或者數據描述符。目前我們看到的數據描述符都被賦予了默認值,我們也可以通過defineProperty方法對其進行個性化配置。

          比如,我們把message設置為只讀:

          var obj={
           message : 'Hello JavaScript!'
          };
          console.log(Object.getOwnPropertyDescriptor(obj,'message'));
          Object.defineProperty(obj,'message',{
           writable:false
          });
          obj.message='haha';
          alert(obj.message);
          

          不好意思,修改無效,因為我已經把這個屬性設置為只讀了。在嚴格模式下,甚至會報錯,啥,你問我什么叫做嚴格模式?好吧,其實就是一句話的事。

          這就是嚴格模式,你不要問為什么這樣就行了,我不會告訴你,因為我也不懂。我只知道,這樣寫就可以,于是乎,接下來運行就報錯了。

          ?

          本文就介紹到這里,對JSON進行了一個簡單的說明。至于深入的學習,還請各位自行去百度吧。

          本專欄前篇文章中介紹了HttpBasic模式,該模式比較簡單,只是進行了通過攜帶Http的Header進行簡單的登錄驗證,而且沒有可以定制的登錄頁面,所以使用場景比較窄。

          對于一個完整的應用系統,與登錄驗證相關的頁面都是高度定制化的,非常美觀而且提供多種登錄方式。這就需要Spring Security支持我們自己定制登錄頁面,也就是本文給大家介紹的FormLogin模式登錄認證模式。

          1. 新建項目

          在介紹相關內容之前,需要先搭建一個demo,新建一個項目spring-security-02,需要添加依賴如下:

          <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-security</artifactId>
          </dependency>
          復制代碼

          除此之外其實還需要添加web、thymeleaf的依賴,這里就不在貼出來了

          demo結構如下:

          2. 新建登錄頁面

          這里不再使用Security默認的頁面,自己定制一個,代碼如下:

          單純的一個表單登錄頁面,需要注意以下幾個參數:

          1. action:security登錄的url,可以自定義,下文介紹
          2. username:security登錄的用戶名,可以自定義,下文介紹
          3. password:security登錄的密碼,可以自定義,下文介紹

          以上三個參數都可以在security通過配置的方式定義

          3. 新建首頁

          這個是登錄成功后跳轉的首頁,代碼如下:

          4. 新建接口

          在security中一切的接口都稱之為資源,下面新建兩個測試接口,代碼如下:

          5. formLogin配置

          在介紹如何配置之前,先來看下formLogin模式登錄的5個要素:

          1. 登錄認證邏輯-登錄URL:這個URL在security中默認是/login且POST請求,但是也可以通過配置自定義
          2. 如何接收登錄參數:用戶名、密碼默認接收的字段分別是username、password,同樣也是可以通過配置自定義
          3. 登陸成功后邏輯:登錄成功后的處理邏輯,比如跳轉到指定的頁面、返回特定的JSON數據,這個也是可以定制
          4. 資源訪問控制規則:這個用于控制什么用戶、什么角色可以訪問什么資源,可以靜態指定也可以從數據庫中加載
          5. 用戶具有角色權限:配置某個用戶擁有什么角色、擁有什么權限,可以靜態指定也可以從數據庫中加載

          一般來說,使用權限認證框架的的業務系統登錄驗證邏輯是固定的,而資源訪問控制規則和用戶信息是從數據庫或其他存儲介質靈活加載的。但本文所有的用戶、資源、權限信息都是代碼配置寫死的,旨在為大家介紹formLogin認證模式,如何從數據庫加載權限認證相關信息我還會結合RBAC權限模型再寫文章的。

          針對上述5個的要素,formLogin配置代碼如下:

          首先,我們要繼承WebSecurityConfigurerAdapter ,重寫configure(HttpSecurity http) 方法,該方法用來配置登錄驗證邏輯。請注意看代碼中的注釋信息。

          上述代碼分為兩個部分:

          第一部分是formLogin配置段,用于配置登錄驗證邏輯相關的信息。如:登錄頁面、登錄成功頁面、登錄請求處理路徑等。

          • .loginPage("/login/page"):指定的第2步定制的登錄頁面,需要寫個mvc接口跳轉到login.html,見源碼
          • .loginProcessingUrl("/login"):指定處理登錄的邏輯的url,這個接口不需要開發者定義,security中通過過濾器UsernamePasswordAuthenticationFilter處理,后文介紹
          • .usernameParameter("username"):指定用戶名的接收參數的字段,默認是username,具體邏輯在UsernamePasswordAuthenticationFilter
          • .passwordParameter("password"):指定密碼的接收參數的字段,默認是username,具體邏輯在UsernamePasswordAuthenticationFilter
          • .defaultSuccessUrl("/"):登錄認證成功后默認轉跳的路徑,這里/則是跳轉到/index.html,可以自定義
          • .failureUrl("/login/page"):登陸失敗的跳轉的路徑

          第二部分是authorizeRequests配置段,用于配置資源的訪問控制規則

          • .antMatchers("/login/page","/login").permitAll():配置登錄頁面、登錄接口直接放行,不需要攔截登錄
          • .antMatchers("/","/hello1").hasAnyAuthority("ROLE_user","ROLE_admin"):設置/hello1、/這兩個資源需要user和admin的角色才可以訪問
          • .antMatchers("/hello2").hasAnyRole("admin"):配置/hello2這個資源需要admin的角色才可以訪問
          • .anyRequest().authenticated():除了上面的配置的規則,訪問其他的資源都需要登錄認證通過才可以訪問

          6. 用戶、角色配置

          在上述的規則中配置了一些資源需要特定的角色才可以訪問,比如user、admin,那么這些角色如何去指定呢?

          在security中提供了配置的方式,代碼如下:

          上述的代碼配置很簡單,創建了兩個用戶且指定了角色,分別如下:

          • user:密碼123456,賦予的角色為user
          • admin:密碼123456,賦予的角色為user、admin

          配置解釋如下:

          • .inMemoryAuthentication():指的是在內存里面存儲用戶的身份認證和授權信息;這里還可以配置從數據庫中動態加載,后文介紹
          • withUser("user"):用戶名是user
          • password(passwordEncoder().encode("123456")):密碼是加密之后的123456
          • roles():方法用于指定用戶的角色,一個用戶可以有多個角色
          • passwordEncoder(passwordEncoder()):指定密碼的加密方式,使用的是BCryptPasswordEncoder,后文介紹

          7. 簡單測試

          按照上述6個步驟基本實現了一個表單登錄,下面測試一下

          瀏覽器訪問http://localhost:8081/hello2,第一次訪問由于未登錄會自動跳轉到登錄頁面,如下圖:

          輸入用戶名和密碼,由于/hello2這個資源需要admin的角色才能訪問,因此必須用admin這個用戶登錄,否則將會報403的錯誤,登錄成功后將能夠正常訪問

          如果用戶名或者密碼錯誤將會觸發.failureUrl("/login/page")這個配置,自動跳轉到登錄頁面

          8. 自定義登錄結果

          在第5步的配置中,和登錄結果相關的配置有如下兩個:

          • .defaultSuccessUrl("/"):登錄認證成功后默認轉跳的路徑,這里/則是跳轉到/index.html,可以自定義
          • .failureUrl("/login/page"):登陸失敗的跳轉的路徑

          這兩個配置都是指定URL的方式:

          • 當我們登錄成功的時候,是由AuthenticationSuccessHandler進行登錄結果處理,默認跳轉到defaultSuccessUrl配置的路徑對應的資源頁面(一般是首頁index.html)。
          • 當我們登錄失敗的時候,是由AuthenticationfailureHandler進行登錄結果處理,默認跳轉到failureUrl配置的路徑對應的資源頁面(一般也是跳轉登錄頁login.html,重新登錄)。

          但是在web應用開發過程中需求是千變萬化的,有時需要我們針對登錄結果做個性化處理,比如:

          • 我們希望不同的人登陸之后,看到不同的首頁(及向不同的路徑跳轉)
          • 我們應用是前后端分離的,驗證響應結果是JSON格式數據,而不是頁面跳轉
          • …… 其他未盡的例子

          因此需要自定義的登錄結果,這篇文章先介紹如何定制跳轉頁面,關于JSON格式數據就是前后端分離架構下需要用到,后文介紹

          8.1 自定義登錄成功結果

          AuthenticationSuccessHandler接口是Security提供的認證成功處理器接口,我們只需要去實現它即可。但是通常來說,我們不會直接去實現AuthenticationSuccessHandler接口,而是繼承SavedRequestAwareAuthenticationSuccessHandler 類,這個類會記住用戶上一次請求的資源路徑,比如/hello2這個路徑,登錄成功后將會自動跳轉到/hello2這個頁面而不是首頁

          代碼如下:

          8.2 自定義登錄失敗結果

          這里我們同樣沒有直接實現AuthenticationFailureHandler接口,而是繼承SimpleUrlAuthenticationFailureHandler 類。該類中默認實現了登錄驗證失敗的跳轉邏輯,即登陸失敗之后回到登錄頁面。我們可以利用這一點簡化我們的代碼。

          代碼如下:

          8.3 SecurityConfig中配置

          配置如下:

          將自定義的AuthenticationSuccessHandler和AuthenticationFailureHandler注入到Spring Security配置類中

          使用formlogin模式,配置successHandler和failureHandler。

          不要配置defaultSuccessUrl和failureUrl,否則自定義handler將失效。handler配置與URL配置只能二選一

          總結

          本篇文章介紹了Spring Security 的 formLogin的配置方式,需要注意的是這里不支持前后端分離架構,關于前后端分離架構如何整合,后文會介紹

          來源:https://juejin.cn/post/7140096326829621261


          主站蜘蛛池模板: 亚洲狠狠久久综合一区77777 | 国偷自产av一区二区三区| 色综合视频一区中文字幕| 无码av免费毛片一区二区| 成人免费视频一区二区| 国产亚洲日韩一区二区三区 | 精品一区二区久久久久久久网精 | 日本一区午夜爱爱| 青青青国产精品一区二区| 亚洲一区二区三区无码影院| 在线精品视频一区二区| 中文人妻av高清一区二区| 日本一区免费电影| 无码成人一区二区| 91精品一区国产高清在线| 激情啪啪精品一区二区| 一本一道波多野结衣一区| 国模无码人体一区二区| 亚洲中文字幕丝袜制服一区| 国产成人一区二区三区| 麻豆天美国产一区在线播放| 国产精品一区二区三区免费| 国产精品无码AV一区二区三区| 亚洲av综合av一区二区三区| 日韩好片一区二区在线看| 中文字幕人妻无码一区二区三区 | 亚洲av无码一区二区三区人妖 | 国产伦精品一区二区三区免费迷| 中文字幕一区二区三区5566| 一区二区视频传媒有限公司| 亚洲午夜日韩高清一区| 国产成人精品亚洲一区| 亚洲AV无码一区二区三区电影| 亚洲一区免费在线观看| 亚洲第一区香蕉_国产a| 自慰无码一区二区三区| 成人免费av一区二区三区| 国产一区二区视频在线播放| 国产另类ts人妖一区二区三区 | 午夜一区二区免费视频| 国产成人精品一区二三区熟女 |