Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 精品成人免费播放国产片,99视频在线精品自拍,又色又爽又黄的网站免费视频

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          Scrapy 爬蟲(chóng)模擬登陸的3種策略

          Scrapy 爬蟲(chóng)模擬登陸的3種策略

          crapy 爬蟲(chóng)模擬登陸的3種策略

          1 Scrapy 爬蟲(chóng)模擬登陸策略

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

          1.1 策略一:直接POST請(qǐng)求登錄

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

          例子:人人網(wǎng)登錄

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

          案例步驟:

          第一步:創(chuàng)建項(xiàng)目。

          在 dos下切換到目錄

          D:\scrapy_project

          新建一個(gè)新的爬蟲(chóng)項(xiàng)目:scrapy startproject renren

          第二步:創(chuàng)建爬蟲(chóng)。

          在 dos下切換到目錄。

          D:\scrapy_project\renren\renren\spiders

          用命令 scrapy genspider renren1 " renren.com" 創(chuàng)建爬蟲(chóng)。

          第三步: 通過(guò)瀏覽器登錄人人網(wǎng),使用 fiddler 抓包抓取登錄 post 請(qǐng)求的 data。

          第四步:編寫爬蟲(chóng)文件。

          import scrapy

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

          # 下面示例:post 數(shù)據(jù)是賬戶密碼

          class Renren1Spider(scrapy.Spider):

          name="renren1"

          allowed_domains=["renren.com"]

          def start_requests(self):

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

          # FormRequest 是 Scrapy 發(fā)送 POST 請(qǐng)求的方法

          yield scrapy.FormRequest(

          url=url,

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

          callback=self.parse_page)

          # 回調(diào)方法,對(duì)返回的 response 進(jìn)行處理(把response.body保存到 xiao.html 中)

          def parse_page(self, response):

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

          filename.write(response.body)

          第五步:修改 settings 文件。

          設(shè)置爬蟲(chóng)請(qǐng)求的默認(rèn)頭信息。

          第六步:運(yùn)行爬蟲(chóng)。

          在 dos下切換到目錄

          D:\scrapy_project\renren\renren 下

          通過(guò)命令運(yùn)行爬蟲(chóng) :scrapy crawl renren1

          第七步:查看結(jié)果。

          xiao.html 中顯示的內(nèi)容正是登錄自己人人網(wǎng)之后的主頁(yè)內(nèi)容,說(shuō)明登錄成功。

          1.2 策略二:標(biāo)準(zhǔn)的模擬登陸

          標(biāo)準(zhǔn)的模擬登錄方法:

          1、首先發(fā)送登錄頁(yè)面的 get 請(qǐng)求,獲取到頁(yè)面里的登錄必須的參數(shù)。

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

          23.2.1 Cookie原理

          HTTP 是無(wú)狀態(tài)的面向連接的協(xié)議, 為了保持連接狀態(tài), 標(biāo)準(zhǔn)的模擬登陸案例引入了 Cookie 機(jī)制。

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

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

          .Cookie 的過(guò)期時(shí)間(Expires/Max-Age)

          .Cookie 作用路徑(Path)

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

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

          23.2.2 模擬登陸

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

          案例步驟:

          第一步:爬取前分析。

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

          輸入用戶名和錯(cuò)誤密碼獲取的 fiddler 結(jié)果:

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

          第二步:創(chuàng)建項(xiàng)目。

          在 dos下切換到目錄

          D:\scrapy_project

          新建一個(gè)新的爬蟲(chóng)項(xiàng)目:scrapy startproject github

          第三步:創(chuàng)建爬蟲(chóng)。

          在 dos下切換到目錄。

          D:\scrapy_project\github\github\spiders

          用命令 scrapy genspider gh "github.com" 創(chuàng)建爬蟲(chóng)。

          第四步: 開(kāi)始前的準(zhǔn)備工作。

          (一)、在 scrapy.cfg 同級(jí)目錄下創(chuàng)建 pycharm 調(diào)試腳本 run.py,內(nèi)容如下:

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

          from scrapy import cmdline

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

          (二)修改 settings 中的 ROBOTSTXT_OBEY=True 參數(shù)為 False,因?yàn)槟J(rèn)為 True,就是要遵守 robots.txt 的規(guī)則, robots.txt 是遵循 Robot協(xié)議 的一個(gè)文件,它保存在網(wǎng)站的服務(wù)器中,它的作用是,告訴搜索引擎爬蟲(chóng),本網(wǎng)站哪些目錄下的網(wǎng)頁(yè)不希望你進(jìn)行爬取收錄。在 Scrapy 啟動(dòng)后,會(huì)在第一時(shí)間訪問(wèn)網(wǎng)站的 robots.txt 文件,然后決定該網(wǎng)站的爬取范圍。查看 robots.txt 可以直接網(wǎng)址后接 robots.txt 即可。

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

          修改 settings 文件。

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

          COOKIES_ENABLED=True

          第五步:編寫爬蟲(chóng)文件-獲取 authenticity_token。

          首先要打開(kāi)登陸頁(yè)面,獲取 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 方法,通過(guò) meta 傳入特殊 key cookiejar,爬取 url 作為參數(shù)傳給回調(diào)函數(shù)

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

          def github_login(self, response):

          # 首先獲取authenticity_token,這里可以借助scrapy shell ”url“來(lái)獲取頁(yè)面

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

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

          # 利用 scrapy 內(nèi)置 logger 打印 info 信息

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

          pass

          運(yùn)行結(jié)果:

          通過(guò)運(yùn)行的結(jié)果,可以看到我們已經(jīng)獲取了 authenticity_token 的值,這一步重點(diǎn)要說(shuō)明meta、cookiejar 和 logger。

          【meta】:字典格式的元數(shù)據(jù),可以傳遞給下一個(gè)函數(shù) meta。

          【cookiejar】:是 meta 的一個(gè)特殊的key,通過(guò) cookiejar 參數(shù)可以支持多個(gè)會(huì)話對(duì)某網(wǎng)站進(jìn)行爬取,可以對(duì) cookie 做標(biāo)記,1,2,3,4......這樣 scrapy 就維持了多個(gè)會(huì)話;

          【logger】:scrapy 為每個(gè) spider 實(shí)例內(nèi)置的日志記錄器。

          為了能使用同一個(gè)狀態(tài)持續(xù)的爬取網(wǎng)站, 就需要保存cookie, 使用cookie保存狀態(tài), Scrapy 提供了 cookie 處理的中間件, 可以直接拿來(lái)使用,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)

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

          Scrapy 提供了 FormRequest 類,是 Request 類的擴(kuò)展,專門用來(lái)進(jìn)行 Form 表單提交。我們主要使用 FormRequest.from_response()方法來(lái)模擬簡(jiǎn)單登陸,通過(guò)FormRequest.from_response 提交后,交給回調(diào)函數(shù)處理。代碼如下:

          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 方法,通過(guò) meta 傳入特殊 key cookiejar,爬取 url 作為參數(shù)傳給回調(diào)函數(shù)

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

          def github_login(self, response):

          # 首先獲取authenticity_token,這里可以借助scrapy shell ”url“來(lái)獲取頁(yè)面

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

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

          # 利用 scrapy 內(nèi)置 logger 打印 info 信息

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

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

          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,

          )

          第七步:修改爬蟲(chóng)文件- 偽裝頭部。

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

          # 頭信息直接從 fiddler 中復(fù)制出來(lái)

          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",

          }

          第八步:修改爬蟲(chóng)文件- 增加回調(diào)函數(shù),主要是登陸成功之后,獲取登錄之后返回的頁(yè)面(response)的元素進(jìn)行斷言,驗(yàn)證登錄結(jié)果。

          登錄之后,主頁(yè)如下:

          # 回調(diào)函數(shù)

          def github_after(self, response):

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

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

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

          if 'Browse activity' in list:

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

          else:

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

          第九步:整理完整的爬蟲(chóng)文件

          import scrapy

          class GithubSpider(scrapy.Spider):

          name='gh'

          allowed_domains=['github.com']

          # 頭信息直接從 fiddler 中復(fù)制出來(lái)

          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 方法,通過(guò) meta 傳入特殊 key cookiejar,爬取 url 作為參數(shù)傳給回調(diào)函數(shù)

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

          def github_login(self, response):

          # 首先獲取authenticity_token,這里可以借助scrapy shell ”url“來(lái)獲取頁(yè)面

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

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

          # 利用 scrapy 內(nèi)置 logger 打印 info 信息

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

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

          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,

          )

          # 回調(diào)函數(shù)

          def github_after(self, response):

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

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

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

          if 'Browse activity' in list:

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

          else:

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

          第十步:查看運(yùn)行的結(jié)果。

          通過(guò)運(yùn)行的結(jié)果說(shuō)明登錄成功。

          1.3 策略三:直接使用保存登陸狀態(tài)的 Cookie 模擬登陸

          如果實(shí)在沒(méi)辦法了,可以用策略三這種方法模擬登錄,雖然麻煩一點(diǎn),但是成功率100%。

          案例步驟:

          第一步:創(chuàng)建項(xiàng)目。

          在 dos下切換到目錄

          D:\scrapy_project

          新建一個(gè)新的爬蟲(chóng)項(xiàng)目:scrapy startproject renren2

          第二步:創(chuàng)建爬蟲(chóng)。

          在 dos下切換到目錄。

          D:\scrapy_project\renren2\renren2\spiders

          用命令 scrapy genspider ren2 "renren.com" 創(chuàng)建爬蟲(chóng)。

          第三步: 通過(guò)瀏覽器登錄人人網(wǎng),使用 fiddler 抓包抓取登錄后的Cookis。

          第四步: 開(kāi)始前的準(zhǔn)備工作。

          (一)、在 scrapy.cfg 同級(jí)目錄下創(chuàng)建 pycharm 調(diào)試腳本 run.py,內(nèi)容如下:

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

          from scrapy import cmdline

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

          (二)修改 settings 中的 ROBOTSTXT_OBEY=True 參數(shù)為 False,因?yàn)槟J(rèn)為 True,就是要遵守 robots.txt 的規(guī)則, robots.txt 是遵循 Robot協(xié)議 的一個(gè)文件,它保存在網(wǎng)站的服務(wù)器中,它的作用是,告訴搜索引擎爬蟲(chóng),本網(wǎng)站哪些目錄下的網(wǎng)頁(yè)不希望你進(jìn)行爬取收錄。在 Scrapy 啟動(dòng)后,會(huì)在第一時(shí)間訪問(wèn)網(wǎng)站的 robots.txt 文件,然后決定該網(wǎng)站的爬取范圍。查看 robots.txt 可以直接網(wǎng)址后接 robots.txt 即可。

          修改 settings 文件。

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

          COOKIES_ENABLED=True

          第五步: 編寫爬蟲(chóng)文件。

          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 值,發(fā)送 POST 請(qǐng)求

          def start_requests(self):

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

          # FormRequest 是 Scrapy 發(fā)送 POST 請(qǐng)求的方法

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

          # 處理響應(yīng)內(nèi)容

          def parse_page(self, response):

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

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

          filename.write(response.body)

          第六步: 運(yùn)行程序,查看運(yùn)行結(jié)果。

          Xiao2.html 中顯示的內(nèi)容正是登錄自己人人網(wǎng)之后的主頁(yè)內(nèi)容,說(shuō)明登錄成功。

          節(jié)重點(diǎn)來(lái)介紹一下JSON,JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式,我們稱之為JavaScript對(duì)象表示法。也就是說(shuō),JSON是一種格式。首先搞清楚三個(gè)概念,即什么是JSON字符串,什么是JavaScript對(duì)象,還有什么又叫做JSON對(duì)象?先來(lái)說(shuō)一個(gè)事,在沒(méi)有JSON之前,前臺(tái)頁(yè)面和Java等語(yǔ)言充當(dāng)?shù)姆?wù)器層,到底是如何傳輸數(shù)據(jù)的呢?沒(méi)錯(cuò),是通過(guò)XML來(lái)傳輸?shù)摹1热缫粋€(gè)登陸頁(yè)面。

          頁(yè)面上有用戶名和密碼兩個(gè)輸入框,當(dāng)我點(diǎn)擊登錄按鈕,這兩個(gè)數(shù)據(jù)就會(huì)被傳遞到服務(wù)器層。那么,如何傳輸呢?如果用XML,也許是這樣的:

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

          后臺(tái)接收到這個(gè)數(shù)據(jù),然后就可以開(kāi)始解析,最終拿到zhangsan和123兩個(gè)字面量。時(shí)間線再往前推,在XML還沒(méi)有出來(lái)的時(shí)候,怎么辦呢?聰明的程序開(kāi)發(fā)人員則會(huì)規(guī)定幾種特殊的格式,拼接一個(gè)特殊的字符串,傳遞到后臺(tái)中去。比如像這樣的:

          "name=zhangsan&password=123"

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

          接下來(lái)還是談JSON,其實(shí)JSON就是一種數(shù)據(jù)格式。諸如:

          {
           key1 : value1 ,
           key2 : value2
          };
          

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

          "{'name' : 'Jack'}"
          

          它歸根到底還是一個(gè)字符串,不是一個(gè)對(duì)象。而JSON對(duì)象,其實(shí)就是Javascript對(duì)象,我們可以通過(guò)字面值的方式直接創(chuàng)造一個(gè)對(duì)象,比如:

          var person={name : 'Jack'}
          

          等同于:

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

          在上邊這個(gè)例子中,name可加單引號(hào),也可加雙引號(hào),甚至可以什么都不加。而右邊的值必須是一個(gè)實(shí)實(shí)在在的東西,比如字符串,或者一個(gè)對(duì)象,甚至是一個(gè)函數(shù)。我們不考慮JS內(nèi)部的對(duì)象機(jī)制,只是簡(jiǎn)單地說(shuō)明一下,是有這么個(gè)事情的。這就是所謂的JSON對(duì)象,也就是js對(duì)象。在JavaScript中,對(duì)象是鍵值對(duì)的集合,符合JSON格式。我們可以通過(guò)下面的方法,把JS對(duì)象轉(zhuǎn)換成JSON格式的字符串。

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

          同樣,一個(gè)JSON格式的字符串,可以變成一個(gè)JS對(duì)象,如:

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

          做個(gè)小結(jié),JSON字符串就是符合JSON格式的字符串,他還是字符串,JSON對(duì)象就是JavaScript對(duì)象,我們推薦使用字面值的方式來(lái)創(chuàng)建一個(gè)JS對(duì)象。然后,JS對(duì)象和JSON字符串可以互相轉(zhuǎn)換。通過(guò)這一個(gè)特點(diǎn),我們能夠?qū)崿F(xiàn)JS對(duì)象的拷貝。一般來(lái)說(shuō),比如我有一個(gè)js對(duì)象。

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

          這樣做,并不是對(duì)象的復(fù)制,person2僅僅是一個(gè)指針,他和person一樣,指向了{(lán)'name' : 'Jack'}這一片內(nèi)存空間。當(dāng)person發(fā)生改變,person2必然也跟著改變。

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

          那有沒(méi)有什么辦法可以實(shí)現(xiàn)對(duì)象的復(fù)制呢?一個(gè)好的解決方案就是,先把person轉(zhuǎn)換成JSON字符串,然后再轉(zhuǎn)成JS對(duì)象,這個(gè)時(shí)候就是另外一個(gè)JS對(duì)象了。比如:

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

          接下來(lái)說(shuō)說(shuō)js對(duì)象內(nèi)容的訪問(wèn)和操作,我們上面已經(jīng)說(shuō)了,JS對(duì)象中無(wú)非是一些鍵值對(duì)的集合,他更像是一個(gè)容器,既然是容器,自然有內(nèi)容,我們?nèi)绾卧L問(wèn)其中的內(nèi)容呢?在上面的例子中,我們已經(jīng)通過(guò)“對(duì)象.屬性名”的方式來(lái)訪問(wèn)JS對(duì)象的具體內(nèi)容。比如:

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

          另外一種方式,就是通過(guò) 對(duì)象["屬性名"] 來(lái)操作其內(nèi)容。比如:

          var id=obj['id']
          

          可以用雙引號(hào),也可以用單引號(hào),看個(gè)人習(xí)慣了。在JS對(duì)象中,屬性名永遠(yuǎn)都是字符串,雖然諸如這樣的代碼:

          var obj={
           id : 1
          };
          

          id沒(méi)有加上引號(hào),但它實(shí)際上還是以字符串的形式被保存起來(lái)的。再說(shuō)一遍,如果你要訪問(wèn)和操作JS對(duì)象的內(nèi)容,有兩種方式,第一種方式是用點(diǎn),第二種方式則是用中括號(hào)。兩種方式如果做一個(gè)比較,顯然是第二種方式較為靈活,因?yàn)樗怯米址フ覍?duì)應(yīng)的鍵值對(duì),而不是用一個(gè)標(biāo)識(shí)符。比如剛才的例子,你這樣寫:

          var id=obj.id;
          

          我問(wèn)你,obj.id中的id是什么?為了符合規(guī)范,id必須是標(biāo)識(shí)符,你不能寫 obj.123 吧。這顯然是不合法,也無(wú)法運(yùn)行通過(guò)的。比如,你能這樣寫嗎?

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

          肯定不行,會(huì)報(bào)錯(cuò)的:

          但是,如果你用中括號(hào)就可以:

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

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

          現(xiàn)在,我們已經(jīng)對(duì)JSON格式和JS對(duì)象有了一個(gè)比較充分的了解,我要在此拋出一個(gè)問(wèn)題,有沒(méi)有什么辦法能夠獲取JS對(duì)象的屬性詳情呢?注意我的用詞,是屬性詳情,也就是說(shuō),比如有一個(gè)JS對(duì)象:

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

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

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

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

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

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

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

          比如,我們把message設(shè)置為只讀:

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

          不好意思,修改無(wú)效,因?yàn)槲乙呀?jīng)把這個(gè)屬性設(shè)置為只讀了。在嚴(yán)格模式下,甚至?xí)?bào)錯(cuò),啥,你問(wèn)我什么叫做嚴(yán)格模式?好吧,其實(shí)就是一句話的事。

          這就是嚴(yán)格模式,你不要問(wèn)為什么這樣就行了,我不會(huì)告訴你,因?yàn)槲乙膊欢N抑恢溃@樣寫就可以,于是乎,接下來(lái)運(yùn)行就報(bào)錯(cuò)了。

          ?

          本文就介紹到這里,對(duì)JSON進(jìn)行了一個(gè)簡(jiǎn)單的說(shuō)明。至于深入的學(xué)習(xí),還請(qǐng)各位自行去百度吧。

          本專欄前篇文章中介紹了HttpBasic模式,該模式比較簡(jiǎn)單,只是進(jìn)行了通過(guò)攜帶Http的Header進(jìn)行簡(jiǎn)單的登錄驗(yàn)證,而且沒(méi)有可以定制的登錄頁(yè)面,所以使用場(chǎng)景比較窄。

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

          1. 新建項(xiàng)目

          在介紹相關(guān)內(nèi)容之前,需要先搭建一個(gè)demo,新建一個(gè)項(xiàng)目spring-security-02,需要添加依賴如下:

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

          除此之外其實(shí)還需要添加web、thymeleaf的依賴,這里就不在貼出來(lái)了

          demo結(jié)構(gòu)如下:

          2. 新建登錄頁(yè)面

          這里不再使用Security默認(rèn)的頁(yè)面,自己定制一個(gè),代碼如下:

          單純的一個(gè)表單登錄頁(yè)面,需要注意以下幾個(gè)參數(shù):

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

          以上三個(gè)參數(shù)都可以在security通過(guò)配置的方式定義

          3. 新建首頁(yè)

          這個(gè)是登錄成功后跳轉(zhuǎn)的首頁(yè),代碼如下:

          4. 新建接口

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

          5. formLogin配置

          在介紹如何配置之前,先來(lái)看下formLogin模式登錄的5個(gè)要素:

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

          一般來(lái)說(shuō),使用權(quán)限認(rèn)證框架的的業(yè)務(wù)系統(tǒng)登錄驗(yàn)證邏輯是固定的,而資源訪問(wèn)控制規(guī)則和用戶信息是從數(shù)據(jù)庫(kù)或其他存儲(chǔ)介質(zhì)靈活加載的。但本文所有的用戶、資源、權(quán)限信息都是代碼配置寫死的,旨在為大家介紹formLogin認(rèn)證模式,如何從數(shù)據(jù)庫(kù)加載權(quán)限認(rèn)證相關(guān)信息我還會(huì)結(jié)合RBAC權(quán)限模型再寫文章的。

          針對(duì)上述5個(gè)的要素,formLogin配置代碼如下:

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

          上述代碼分為兩個(gè)部分:

          第一部分是formLogin配置段,用于配置登錄驗(yàn)證邏輯相關(guān)的信息。如:登錄頁(yè)面、登錄成功頁(yè)面、登錄請(qǐng)求處理路徑等。

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

          第二部分是authorizeRequests配置段,用于配置資源的訪問(wèn)控制規(guī)則

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

          6. 用戶、角色配置

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

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

          上述的代碼配置很簡(jiǎn)單,創(chuàng)建了兩個(gè)用戶且指定了角色,分別如下:

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

          配置解釋如下:

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

          7. 簡(jiǎn)單測(cè)試

          按照上述6個(gè)步驟基本實(shí)現(xiàn)了一個(gè)表單登錄,下面測(cè)試一下

          瀏覽器訪問(wèn)http://localhost:8081/hello2,第一次訪問(wèn)由于未登錄會(huì)自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面,如下圖:

          輸入用戶名和密碼,由于/hello2這個(gè)資源需要admin的角色才能訪問(wèn),因此必須用admin這個(gè)用戶登錄,否則將會(huì)報(bào)403的錯(cuò)誤,登錄成功后將能夠正常訪問(wèn)

          如果用戶名或者密碼錯(cuò)誤將會(huì)觸發(fā).failureUrl("/login/page")這個(gè)配置,自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面

          8. 自定義登錄結(jié)果

          在第5步的配置中,和登錄結(jié)果相關(guān)的配置有如下兩個(gè):

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

          這兩個(gè)配置都是指定URL的方式:

          • 當(dāng)我們登錄成功的時(shí)候,是由AuthenticationSuccessHandler進(jìn)行登錄結(jié)果處理,默認(rèn)跳轉(zhuǎn)到defaultSuccessUrl配置的路徑對(duì)應(yīng)的資源頁(yè)面(一般是首頁(yè)index.html)。
          • 當(dāng)我們登錄失敗的時(shí)候,是由AuthenticationfailureHandler進(jìn)行登錄結(jié)果處理,默認(rèn)跳轉(zhuǎn)到failureUrl配置的路徑對(duì)應(yīng)的資源頁(yè)面(一般也是跳轉(zhuǎn)登錄頁(yè)login.html,重新登錄)。

          但是在web應(yīng)用開(kāi)發(fā)過(guò)程中需求是千變?nèi)f化的,有時(shí)需要我們針對(duì)登錄結(jié)果做個(gè)性化處理,比如:

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

          因此需要自定義的登錄結(jié)果,這篇文章先介紹如何定制跳轉(zhuǎn)頁(yè)面,關(guān)于JSON格式數(shù)據(jù)就是前后端分離架構(gòu)下需要用到,后文介紹

          8.1 自定義登錄成功結(jié)果

          AuthenticationSuccessHandler接口是Security提供的認(rèn)證成功處理器接口,我們只需要去實(shí)現(xiàn)它即可。但是通常來(lái)說(shuō),我們不會(huì)直接去實(shí)現(xiàn)AuthenticationSuccessHandler接口,而是繼承SavedRequestAwareAuthenticationSuccessHandler 類,這個(gè)類會(huì)記住用戶上一次請(qǐng)求的資源路徑,比如/hello2這個(gè)路徑,登錄成功后將會(huì)自動(dòng)跳轉(zhuǎn)到/hello2這個(gè)頁(yè)面而不是首頁(yè)

          代碼如下:

          8.2 自定義登錄失敗結(jié)果

          這里我們同樣沒(méi)有直接實(shí)現(xiàn)AuthenticationFailureHandler接口,而是繼承SimpleUrlAuthenticationFailureHandler 類。該類中默認(rèn)實(shí)現(xiàn)了登錄驗(yàn)證失敗的跳轉(zhuǎn)邏輯,即登陸失敗之后回到登錄頁(yè)面。我們可以利用這一點(diǎn)簡(jiǎn)化我們的代碼。

          代碼如下:

          8.3 SecurityConfig中配置

          配置如下:

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

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

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

          總結(jié)

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

          來(lái)源:https://juejin.cn/post/7140096326829621261


          主站蜘蛛池模板: 中文字幕日韩人妻不卡一区| 无码精品人妻一区二区三区免费 | 中文字幕一区在线播放| 精品国产免费一区二区三区香蕉| 麻豆一区二区99久久久久| 视频一区二区三区在线观看| 国精产品一区一区三区有限在线 | 精品视频一区二区三区免费 | 国产在线精品一区二区不卡麻豆| 激情无码亚洲一区二区三区| 人体内射精一区二区三区| 日韩电影一区二区三区| 国产丝袜无码一区二区三区视频| 视频一区二区三区人妻系列| 国产在线精品一区二区在线看| 伊人无码精品久久一区二区| 亚洲天堂一区二区三区| 久久一区二区精品| 国产精品熟女一区二区| 国精产品一区一区三区免费视频 | 日韩在线一区视频| 一本一道波多野结衣一区| 国产韩国精品一区二区三区久久 | 国产一区二区在线视频播放| 日韩免费视频一区| 国产在线观看一区二区三区四区| 国产成人一区二区三区免费视频| 无码人妻一区二区三区av| 午夜AV内射一区二区三区红桃视| 亚洲国产精品无码第一区二区三区| 日本一区二区视频| av一区二区三区人妻少妇| 亚洲日本va一区二区三区| 在线日产精品一区| 中文字幕一区二区人妻| 人妻免费一区二区三区最新| 精品无码一区二区三区爱欲九九| 美日韩一区二区三区| 国产高清不卡一区二区| 精品一区狼人国产在线| 久久se精品一区二区|