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
前面學(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)證模式。
在介紹相關(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)如下:
這里不再使用Security默認(rèn)的頁(yè)面,自己定制一個(gè),代碼如下:
單純的一個(gè)表單登錄頁(yè)面,需要注意以下幾個(gè)參數(shù):
以上三個(gè)參數(shù)都可以在security通過(guò)配置的方式定義
這個(gè)是登錄成功后跳轉(zhuǎn)的首頁(yè),代碼如下:
在security中一切的接口都稱之為資源,下面新建兩個(gè)測(cè)試接口,代碼如下:
在介紹如何配置之前,先來(lái)看下formLogin模式登錄的5個(gè)要素:
一般來(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)求處理路徑等。
第二部分是authorizeRequests配置段,用于配置資源的訪問(wèn)控制規(guī)則
在上述的規(guī)則中配置了一些資源需要特定的角色才可以訪問(wèn),比如user、admin,那么這些角色如何去指定呢?
在security中提供了配置的方式,代碼如下:
上述的代碼配置很簡(jiǎn)單,創(chuàng)建了兩個(gè)用戶且指定了角色,分別如下:
配置解釋如下:
按照上述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è)面
在第5步的配置中,和登錄結(jié)果相關(guān)的配置有如下兩個(gè):
這兩個(gè)配置都是指定URL的方式:
但是在web應(yīng)用開(kāi)發(fā)過(guò)程中需求是千變?nèi)f化的,有時(shí)需要我們針對(duì)登錄結(jié)果做個(gè)性化處理,比如:
因此需要自定義的登錄結(jié)果,這篇文章先介紹如何定制跳轉(zhuǎn)頁(yè)面,關(guān)于JSON格式數(shù)據(jù)就是前后端分離架構(gòu)下需要用到,后文介紹
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è)
代碼如下:
這里我們同樣沒(méi)有直接實(shí)現(xiàn)AuthenticationFailureHandler接口,而是繼承SimpleUrlAuthenticationFailureHandler 類。該類中默認(rèn)實(shí)現(xiàn)了登錄驗(yàn)證失敗的跳轉(zhuǎn)邏輯,即登陸失敗之后回到登錄頁(yè)面。我們可以利用這一點(diǎn)簡(jiǎn)化我們的代碼。
代碼如下:
配置如下:
將自定義的AuthenticationSuccessHandler和AuthenticationFailureHandler注入到Spring Security配置類中
使用formlogin模式,配置successHandler和failureHandler。
不要配置defaultSuccessUrl和failureUrl,否則自定義handler將失效。handler配置與URL配置只能二選一
本篇文章介紹了Spring Security 的 formLogin的配置方式,需要注意的是這里不支持前后端分離架構(gòu),關(guān)于前后端分離架構(gòu)如何整合,后文會(huì)介紹
來(lái)源:https://juejin.cn/post/7140096326829621261
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。