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

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

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

          57挑戰(zhàn)之54,實(shí)現(xiàn)一個(gè)url 跳轉(zhuǎn)服務(wù)(七) 客戶+服務(wù)端聯(lián)調(diào)2

          要求:

          上代碼:

          服務(wù)端:

          
          
          import redis
          import re
          import json
          import time
          import cgi
          from redis import StrictRedis, ConnectionPool
          from flask import Flask,jsonify,request
          import requests
          
          app = Flask(__name__)
          
          
          def insert_into_redis(shorturl,longurl):
              print("----come to function--- insert_into_redis(shorturl,longurl)")
              # 如果含義為插入,則做插入操作
              pool = ConnectionPool(host='localhost', port=6379, db=0, decode_responses=True)
              # redis 取出的結(jié)果默認(rèn)是字節(jié),我們可以設(shè)定 decode_responses=True 改成字符串。
              r = StrictRedis(connection_pool=pool)
              string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
              r.hset("shorttolong",shorturl, json.dumps({"longurl": longurl,"visittime": [], "creattime":string}))
              r.hset("longtoshort",longurl,shorturl)
              print("Info: The value {0} is inserted".format(shorturl))
              return 1
          
          def check_if_exist(longurl):
              print("----come to function--- check_if_exist(longurl)")
              pool = ConnectionPool(host='localhost', port=6379, db=0, decode_responses=True)
              r = StrictRedis(connection_pool=pool)
              # 判斷是否存在,如果存在則返回1
              if r.hexists("longtoshort",longurl):
                  result = 1
              else:
                  result = 0
              return result
          
          def check_shorturl_if_exist(shorturl):
              print("----come to function--- check_shorturl_if_exist(shorturl)")
              pool = ConnectionPool(host='localhost', port=6379, db=0, decode_responses=True)
              r = StrictRedis(connection_pool=pool)
              # 判斷是否存在,如果存在則返回1
              if r.hexists("shorttolong",shorturl):
                  result = 1
              else:
                  result = 0
              return result
          
          def get_longurl(shorturl):
              print("----come to function--- get_longurl(shorturl)")
              pool = ConnectionPool(host='localhost', port=6379, db=0, decode_responses=True)
              r = StrictRedis(connection_pool=pool)
              # 判斷是否存在,如果存在則返回1
              longurljson = r.hmget("shorttolong",shorturl)
              longurl = json.loads(longurljson[0])["longurl"]
              #print(longurljson)
              #print(longurl)
              return longurl
          
          
          def update_jumptime(shorturl):
              print("----come to function--- update_jumptime(shorturl)")
              pool = ConnectionPool(host='localhost', port=6379, db=0, decode_responses=True)
              r = StrictRedis(connection_pool=pool)
              longurljson = r.hmget("shorttolong", shorturl)
              dic1 = json.loads(longurljson[0])
              list1 = dic1["visittime"]
              #print(list1)
              string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
              list1.append(string)
              dic1["visittime"] = list1
              r.hset("shorttolong", shorturl, json.dumps(dic1))
              print("info: update the visittime of the {0} success".format(shorturl))
              return 1
          
          
          """
          0.判斷長(zhǎng)鏈接是否已經(jīng)存在
          在用戶端進(jìn)行創(chuàng)建的時(shí)候,要檢查長(zhǎng)鏈接是否已經(jīng)在服務(wù)端存在了,如果存在應(yīng)該忽略。
          如何確定服務(wù)端存在,通過post 接口來查詢。
          
          檢查長(zhǎng)URL是否有效:通過checkurl(longurl)函數(shù),當(dāng)longurl 已經(jīng)存在,
          返回json字段{'result':1},不存在則否則返回{'result':0}
          """
          
          @app.route('/api/checkurl', methods=['POST'])
          def check_url():
              print("----come to function--- check_url() /api/checkurl logic")
              longurl = request.json['longurl']
              result = check_if_exist(longurl)
              if result == 0:
                 print("Info: The longurl {0} is not exist at serverside storage".format(longurl))
              else:
                 print("Info: The longurl {0} is already exist at serverside storage".format(longurl))
              return jsonify({'result':result,"longurl":longurl}),200
          
          @app.route('/api/checkshorturl', methods=['POST'])
          def check_short_url():
              print("----come to function--- check_short_url() /api/check_short_url logic")
              shorturl = request.json['shorturl']
              result = check_shorturl_if_exist(shorturl)
              if result == 0:
                 print("Info: The shorturl {0} is not exist at serverside storage".format(shorturl))
              else:
                 print("Info: The shorturl {0} is already exist at serverside storage".format(shorturl))
              return jsonify({'result':result,"shorturl":shorturl}),200
          
          """
          1.收集長(zhǎng)鏈接生成短鏈接:
          根據(jù)客戶端的post 請(qǐng)求,收集客戶端發(fā)送的shorturl以及l(fā)ongurl,把關(guān)聯(lián)關(guān)系插入到redis中
          插入的時(shí)候,redis表中有兩個(gè)hash庫(kù),
          "shorttolong" 庫(kù),結(jié)構(gòu)為 shorturl:{"longurl":longurl,"visittime",{},"createtime":string}
          "longtoshort" 庫(kù),結(jié)構(gòu)為 longurl:shorturl
          """
          @app.route('/api/shorturlcreate',methods=['POST'])
          def shorturl_create():
              print("----come to function---shorturl_create() /api/shorturlcreate logic")
              shorturl = request.json['shorturl']
              longurl = request.json['longurl']
              insert_into_redis(shorturl, longurl)
              print("info: insert into redis {0}:{1} pair success".format(shorturl,longurl))
              return jsonify({"info":"insert into redis "+longurl+shorturl+" pair succuss"})
          
          
          
          """
          2.收集短鏈接做相關(guān)跳轉(zhuǎn):
          客戶端發(fā)出短鏈接請(qǐng)求,
          2-1: 判斷短鏈接是否存在
          2-2: 如果存在,則找到對(duì)應(yīng)的長(zhǎng)鏈接
          2-3: 返回301 指令,并讓客戶端做跳轉(zhuǎn)
          
          """
          @app.route('/api/shorturljump',methods=['POST'])
          def shorturl_jump():
              print("----come to function---shorturl_jump() /api/shorturljump logic")
              shorturl = request.json['shorturl']
              if check_shorturl_if_exist(shorturl) == 1:
              # getlongurl mock
                 longurl=get_longurl(shorturl)
              # 增加一個(gè)跳轉(zhuǎn)的時(shí)間,對(duì)他記錄。
              # redis_update_jumptime(shorturl) mock
                 update_jumptime(shorturl)
              # jumpto destination longurl,mock
                 print("info: jump to destination longurl {0} ".format(longurl))
              #redirect_to_longurl(longurl)
                 return jsonify({"info": "jump to destionation","longurl":longurl})
              else:
                 return jsonify({"info": "the site {0} is not exist".format(shorturl),"longurl":"notexist"})
          
          
          if __name__ == '__main__':
              app.run(host='0.0.0.0',port=8008,debug = True)
          

          客戶端:

          
          
          from flask import Flask,render_template,request,url_for,redirect
          from datetime import date
          import requests
          import json
          import time
          
          def check_if_valid(longurl):
              #檢查長(zhǎng)鏈接是否有效,這里mock一下
              print("----come to function--- check_if_valid(longurl)")
              try:
                  r = requests.get("https://"+longurl)
                  statuscode = r.status_code
                  if statuscode == 200 or statuscode == 301:
                      print("The site is reachable on internet")
                      result = 1
                  else:
                      result = 0
          
              except:
                  result = 0
          
              return result
          
          def check_if_exist_url(longurl):
              print("----come to function--- check_if_exist_url(longurl)")
              #檢查長(zhǎng)鏈接是否在服務(wù)端存在,mock一下
              print("Process: check if the longurl {0} is exist at serverside redis storage".format(longurl))
              r = requests.post("http://127.0.0.1:8008/api/checkurl", json={"longurl": longurl})
              textjson = r.json()
              print("get the return info from the serverside {0}".format(longurl))
              print(textjson)
              print("get the type info of the serverside")
              print(type(textjson))
              print("Get the longurl ")
              print(textjson["longurl"])
              #print(dic2["longurl"])
              result = textjson["result"]
              return result
          
          def post_shorturlcreate(shorturl,longurl):
              #模擬postman,傳遞數(shù)據(jù)到服務(wù)端。
              print("----come to function--- post_shorturlcreate(shorturl,longurl)")
              print("Process: to deliver create link to serverside redis storage")
              r = requests.post("http://127.0.0.1:8008/api/shorturlcreate",json={"shorturl":shorturl,"longurl":longurl})
              print("get info from serverside \n"+ r.text)
              return 1
          
          def post_shorturljump(shorturl):
              print("----come to function--- post_shorturljump(shorturl)")
              print("Process: jump to shorturl")
              r = requests.post("http://127.0.0.1:8008/api/shorturljump", json={"shorturl": shorturl})
              print("get info from serverside \n" + r.text)
              return r
          
          def create_shorturl(longurl):
              print("----come to function--- create_shorturl(longurl)")
              print("Process: to create shorturl from longurl")
              #返回shorturl
              shorturl = "/"+longurl.split(".")[1]
              print("Process:The short url is "+shorturl)
              return shorturl
          
          def check_shorturl_if_exist(shorturl):
              print("----come to function--- check_shorturl_if_exist()")
              print("Process: check if the shorturl {0} is exist at serverside redis storage".format(shorturl))
              r = requests.post("http://127.0.0.1:8008/api/checkshorturl", json={"shorturl": shorturl})
              textjson = r.json()
              print("Print the info return from serverside,this is the info")
              print(textjson)
              print("Check the type of the info")
              print(type(textjson))
              print("Check the mapping(longurl) of the shorturl {0}".format(shorturl))
              print(textjson["shorturl"])
              # print(dic2["longurl"])
              result = textjson["result"]
              return result
          
          app = Flask(__name__)
          @app.route('/',methods=['GET','POST'])
          def index():
              if request.method == 'POST': #根據(jù)post 表單獲取的內(nèi)容做相關(guān)判斷
                 longurl = request.form['longurl']
                 shorturl = request.form['shorturl']
          
                 print("longurl is {0}, shorturl is {1}".format(longurl,shorturl))
                 if longurl is not None and shorturl == "empty": #當(dāng)longurl 非空
                     print("進(jìn)入第一個(gè)邏輯")
                     if check_if_valid(longurl) == 1 and check_if_exist_url(longurl) == 0: #當(dāng)longurl 可用,且longurl在服務(wù)端不存在
                         shorturl = create_shorturl(longurl)
                         post_shorturlcreate(shorturl, longurl)
                         notes = "the longurl {0} and shorturl {1} pair is created".format(longurl,shorturl)
                         return render_template('display.html', info=notes)
                     else: #否則條件沒達(dá)到,通知失敗
                         notes = "the longurl is not exist or it's already at serverside"
                         return render_template('display.html', info=notes)
          
                 if shorturl is not None and longurl == "empty": #當(dāng)shorturl 非空,執(zhí)行第二個(gè)邏輯
                     print("進(jìn)入第二個(gè)邏輯")
                     if check_shorturl_if_exist(shorturl) == 1:# 如果短url在服務(wù)端存在,則做跳轉(zhuǎn)等邏輯
                        r = post_shorturljump(shorturl)
                        print(r.json())
                        print(type(r.json()))
                        longurl = r.json()["longurl"]
                        print(longurl)
                        return redirect("https://" + longurl)
                     else:
                        notes = "the shorturl is not exist"
                        return render_template('display.html', info=notes)
          
                 if shorturl is not None and longurl == "statics": #當(dāng)shorturl 非空,且longurl為統(tǒng)計(jì),執(zhí)行第三個(gè)邏輯
                     print("進(jìn)入第三個(gè)邏輯")
                     visittime = []
                     if check_shorturl_if_exist(shorturl) == 1:# 如果短url在服務(wù)端存在,則收集它的訪問時(shí)間并存到字典
                        visittime =
                     else:
                        notes = "the shorturl is not exist"
                        return render_template('display.html', info=notes)
          
              else:
                 return render_template('index5.html')
          
          @app.route('/api/static',methods=['GET','POST'])
          def get_static():
              print("進(jìn)入第三個(gè)邏輯")
              print("統(tǒng)計(jì)某個(gè)短鏈的訪問情況")
          
              return render_template('display.html', info="The site info is displayed")
          
          
          if __name__ == '__main__':
              app.run(host='0.0.0.0',port=80,debug = True)
          





          index5.html

          <!DOCTYPE html>
          
          <html lang="en">
          <script type="text/javascript">
          if(form.longurl.value ==''){
             form.longurl.value == "empty"
          }
          if(form.shorturl.value ==''){
             form.shorturl.value == "empty"
          }
          
          </script>
          <head>
              <meta charset="UTF-8">
              <title>57-54</title>
          </head>
          <body>
          <form method = "post">
           <label for="longurl">提供想生成短鏈接的url</label>
              <input type="text" name="longurl" required><br>
              <input type="hidden" name="shorturl" value ="empty"><br>
              <input type="submit" name="submit" value="記錄">
          </form>
          <form>
           <label for="shorturl">提供想跳轉(zhuǎn)的url</label>
              <input type="hidden" name="longurl" value="empty"><br>
              <input type="text" name="shorturl" required><br>
              <input type="submit" name="submit" value="跳轉(zhuǎn)">
          </form>
          
          
          </body>
          </html>

          display.html

          
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>57-53</title>
          </head>
          <body>
           {{info}}
          
          </body>
          </html>


          前端

          鍵入www.qq.com

          點(diǎn)擊記錄



          實(shí)現(xiàn)了插入邏輯:


          插入邏輯前端日志:

          插入邏輯后端日志:



          查看數(shù)據(jù)庫(kù):



          跳轉(zhuǎn)前邏輯:

          鍵入qq,點(diǎn)擊跳轉(zhuǎn)


          跳轉(zhuǎn)后:

          前端日志:


          后端日志:


          看訪問做了跳轉(zhuǎn)302,

          update后,跳轉(zhuǎn)時(shí)間做了記錄

          url腦圖

          一、常規(guī)用法

          最常用的是直接請(qǐng)求,默認(rèn)是GET方法

          curl <https://baidu.com>

          1.1 自定義header

          添加header使用 -H, --header

          curl -H "X-MY-HEADER: abc" <https://baidu.com>

          如果想添加多個(gè)header,那么就寫多次

          curl -H "X-MY-HEADER: abc" -H "X-MY-HEADER2: def" <https://baidu.com>
          

          1.2 請(qǐng)求方法

          有些時(shí)候想要post或者put請(qǐng)求,可以使用 -X, --request <method> 來自設(shè)置

          curl -X POST <https://baidu.com>

          當(dāng)post的時(shí)候想要傳輸body參數(shù),可以使用 -d, --data <data> 來設(shè)置

          curl -X POST -d "name=abc&gender=0" <https://example.com>
          或者
          curl -X POST -d name=bac -d gender=0 <https://example.com>

          1.3 設(shè)置cookie

          需要添加cookie來請(qǐng)求,可以使用 -b, --cookie 來設(shè)置

          curl -b "token=abcdef;uid=123123" <https://example.com>

          那如果想要把返回的cookie存儲(chǔ)起來呢,可以用 -c, --cookie-jar <file> 來設(shè)置存儲(chǔ)的位置

          curl -c cookie.txt <https://www.baidu.com>

          1.4 結(jié)果輸出到文件

          需要把請(qǐng)求的返回結(jié)果輸出到文件,以便查看分析,可以用 -o, --output <file> 來設(shè)置輸出到的文件

          curl -o baidu.html <https://www.baidu.com>

          示例如下圖,會(huì)打印出整體進(jìn)度和統(tǒng)計(jì)

          二、多種協(xié)議

          curl被人熟知的是系統(tǒng)自帶的用來請(qǐng)求HTTP url的工具。但是,其不但可以處理http協(xié)議,還可以處理:FILE, FTP, FTPS, GOPHER, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET and TFTP等。。。這么多協(xié)議,怎樣,功能是不是很強(qiáng)大。

          • 用來發(fā)送郵件
          curl --mail-from "abc@def.com" --mail-rcpt "12345566@qq.com" --upload-file mail.txt --user "user_name:password" --url "smtp://mail.qq.com"
          • 上傳文件,使用 -T, --upload-file <file> 參數(shù),同時(shí)可以批量上傳, 用[] 來設(shè)置范圍,示例如下。同時(shí),下面會(huì)講解如何處理多個(gè)url同時(shí)請(qǐng)求的情況。
          curl -T "img[1-1000].png" <ftp://ftp.example.com/upload/>
          或者
          curl --upload-file "{file1,file2}" <http://www.example.com>

          三、多個(gè)請(qǐng)求

          最簡(jiǎn)單的使用方式是:curl <https://baidu.com> ,但是偶爾會(huì)有想同時(shí)請(qǐng)求多個(gè)url,能不能辦的到呢,當(dāng)然可以

          curl https://{baidu,cnblogs}.com

          這樣的話就會(huì)順序請(qǐng)求 https://baidu.com、https://cnblogs.com 這兩個(gè)網(wǎng)站。(PS: 終端里顯示{ 或}之前有\(zhòng),是終端做的轉(zhuǎn)義功能,請(qǐng)忽略。)

          又有說請(qǐng)求的url不一定都是.com結(jié)尾呢,能不能辦呢,那必須可以

          curl https://{baidu.com,csdn.net}

          又有人較真說,我請(qǐng)求的多個(gè)url協(xié)議都是不同的,比如:curl {<http://baidu.com>, <https://csdn.net>} , 能不能這么請(qǐng)求呢,那不好意思,這樣不行,會(huì)解析錯(cuò)誤。

          這種用法多用于某個(gè)站點(diǎn)多種path或query的情況,比如

          四、使用代理

          可以使用 -x,--proxy [protocol://]host[:port] 參數(shù) 代理來請(qǐng)求目標(biāo)網(wǎng)站

          curl --proxy <http://127.0.0.1:1087> <https://baidu.com>

          使用http的代理是可以來請(qǐng)求https目標(biāo)網(wǎng)站的,其中原理是使用了http的proxy tunnel功能,這個(gè)在后續(xù)文章中會(huì)做詳細(xì)介紹。

          五、請(qǐng)求詳情

          有些時(shí)候想要知道詳細(xì)的請(qǐng)求情況,比如怎么建立連接的,請(qǐng)求過程是如何的,那么可以這么來用,使用 -v ,--verbose

          curl -v <https://baidu.com>

          六、請(qǐng)求追蹤

          有了上面的詳細(xì)請(qǐng)求可能還不太滿足一些需求,比如想要知道花費(fèi)建立連接時(shí)間、傳輸時(shí)間、相應(yīng)時(shí)間等詳細(xì)的性能信息,這個(gè)對(duì)于最終網(wǎng)絡(luò)問題很有幫助,那么怎么辦呢。

          那就要拿出一個(gè)厲害的參數(shù):-w, —write-out

          比如下面的例子可以輸出:發(fā)起鏈接時(shí)間,開始傳輸時(shí)間以及總花費(fèi)時(shí)間。

          curl -w 'time_connect %{time_connect}s\ntime_starttransfer %{time_starttransfer}s\ntime_total %{time_total}s\n' <https://baidu.com>

          可以來一個(gè)更詳細(xì)的追蹤

          curl -w 'http_code: %{http_code}\ncontent_type: %{content_type}\ntime_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_appconnect: %{time_appconnect}\ntime_redirect: %{time_redirect}\ntime_pretransfer: %{time_pretransfer}\ntime_starttransfer: %{time_starttransfer}\nspeed_download: %{speed_download}\nspeed_upload: %{speed_upload}\nsize_download: %{size_download}\nsize_upload: %{size_upload}\n---------\n time_total: %{time_total}\n' <https://baidu.com>

          結(jié)果如下圖

          -w 可以使用的變量比較多,常用的如下表所示:

          • url_effective 上次訪問的URL
          • http_code 上一次 HTTP 或 FTP 數(shù)據(jù)傳輸過程中的 response 數(shù)值代碼
          • http_connect 上一次 CONNECT 請(qǐng)求中的數(shù)值代碼
          • time_total 數(shù)據(jù)傳輸消耗的總時(shí)間,以秒為單位,精度為毫秒。
          • time_namelookup 從數(shù)據(jù)傳輸開始到域名解析完成所花費(fèi)的時(shí)間。 以秒為單位
          • time_connect TCP連接建立成功所花費(fèi)的時(shí)間。 以秒為單位
          • time_appconnect 應(yīng)用層協(xié)議,如 SSL/SSH、三次握手等過程完成所花費(fèi)的時(shí)間。 以秒為單位
          • time_redirect 從跳轉(zhuǎn)鏈接被激活到真正開始從跳轉(zhuǎn)鏈接下載數(shù)據(jù)所經(jīng)過的時(shí)間。 以秒為單位
          • time_starttransfer 從請(qǐng)求連接開始,到第一個(gè)字節(jié)被傳送前所經(jīng)過的時(shí)間。 以秒為單位
          • size_download 數(shù)據(jù)傳輸過程中下載的總數(shù)據(jù)大小。以字節(jié)為單位
          • size_upload 數(shù)據(jù)傳輸過程中上傳的總數(shù)據(jù)大小。以字節(jié)為單位
          • size_header 下載的數(shù)據(jù)包中,header 字段的總數(shù)據(jù)大小。以字節(jié)為單位
          • size_request 被發(fā)送的 HTTP request 的總數(shù)據(jù)大小。以字節(jié)為單位
          • speed_download 整個(gè)數(shù)據(jù)傳輸過程中的平均數(shù)據(jù)下載速度。 以字節(jié)為單位
          • speed_upload 整個(gè)數(shù)據(jù)傳輸過程中的平均數(shù)據(jù)上傳速度。 以字節(jié)為單位
          • content_type 被請(qǐng)求訪問的文件的 Content_Type 類型。
          • num_redirects 訪問請(qǐng)求中包含的跳轉(zhuǎn)鏈接數(shù)量。
          • redirect_url 跳轉(zhuǎn)鏈接指向的URL ssl_verify_result SSL驗(yàn)證的結(jié)果。值為 0 時(shí)表示驗(yàn)證成功。

          七、設(shè)置重試

          可以設(shè)置失敗請(qǐng)求重試次數(shù),以及最大重試次數(shù)、超時(shí)時(shí)間等。

          • —retry <num> 設(shè)置重試次數(shù)
          • —retry-delay <seconds> 設(shè)置重試時(shí)等待時(shí)間,秒級(jí)
          • —retry-max-time <seconds> 設(shè)置最大重試時(shí)間
          • -m, —max-time <seconds> 設(shè)置最大請(qǐng)求時(shí)間,即超時(shí)時(shí)間

          例如,設(shè)置重復(fù)次數(shù)請(qǐng)求一個(gè)不存在的url

          curl --retry 3 --retry-delay 1 --retry-max-time 10 <https://notexisturl.com>

          例如,設(shè)置超時(shí)時(shí)間來請(qǐng)求

          curl -m 1 <https://baidu.com>

          pring Security 系列繼續(xù)。

          前面的視頻+文章,松哥和大家簡(jiǎn)單聊了 Spring Security 的基本用法,并且我們一起自定義了一個(gè)登錄頁面,讓登錄看起來更炫一些!

          今天我們來繼續(xù)深入這個(gè)表單配置,挖掘一下這里邊常見的其他配置。學(xué)習(xí)本文,強(qiáng)烈建議大家看一下前置知識(shí)(松哥手把手帶你入門 Spring Security,別再問密碼怎么解密了),學(xué)習(xí)效果更佳。

          1.登錄接口

          很多初學(xué)者分不清登錄接口和登錄頁面,這個(gè)我也很郁悶。我還是在這里稍微說一下。

          登錄頁面就是你看到的瀏覽器展示出來的頁面,像下面這個(gè):

          登錄接口則是提交登錄數(shù)據(jù)的地方,就是登錄頁面里邊的 form 表單的 action 屬性對(duì)應(yīng)的值。

          在 Spring Security 中,如果我們不做任何配置,默認(rèn)的登錄頁面和登錄接口的地址都是 /login,也就是說,默認(rèn)會(huì)存在如下兩個(gè)請(qǐng)求:

          • GET http://localhost:8080/login
          • POST http://localhost:8080/login

          如果是 GET 請(qǐng)求表示你想訪問登錄頁面,如果是 POST 請(qǐng)求,表示你想提交登錄數(shù)據(jù)。

          在上篇文章中,我們?cè)?SecurityConfig 中自定定義了登錄頁面地址,如下:

          .and()
          .formLogin()
          .loginPage("/login.html")
          .permitAll()
          .and()

          當(dāng)我們配置了 loginPage 為 /login.html 之后,這個(gè)配置從字面上理解,就是設(shè)置登錄頁面的地址為 /login.html。

          實(shí)際上它還有一個(gè)隱藏的操作,就是登錄接口地址也設(shè)置成 /login.html 了。換句話說,新的登錄頁面和登錄接口地址都是 /login.html,現(xiàn)在存在如下兩個(gè)請(qǐng)求:

          • GET http://localhost:8080/login.html
          • POST http://localhost:8080/login.html

          前面的 GET 請(qǐng)求用來獲取登錄頁面,后面的 POST 請(qǐng)求用來提交登錄數(shù)據(jù)。

          有的小伙伴會(huì)感到奇怪?為什么登錄頁面和登錄接口不能分開配置呢?

          其實(shí)是可以分開配置的!

          在 SecurityConfig 中,我們可以通過 loginProcessingUrl 方法來指定登錄接口地址,如下:

          .and()
          .formLogin()
          .loginPage("/login.html")
          .loginProcessingUrl("/doLogin")
          .permitAll()
          .and()

          這樣配置之后,登錄頁面地址和登錄接口地址就分開了,各是各的。

          此時(shí)我們還需要修改登錄頁面里邊的 action 屬性,改為 /doLogin,如下:

          <form action="/doLogin" method="post">
          <!--省略-->
          </form>

          此時(shí),啟動(dòng)項(xiàng)目重新進(jìn)行登錄,我們發(fā)現(xiàn)依然可以登錄成功。

          那么為什么默認(rèn)情況下兩個(gè)配置地址是一樣的呢?

          我們知道,form 表單的相關(guān)配置在 FormLoginConfigurer 中,該類繼承自 AbstractAuthenticationFilterConfigurer ,所以當(dāng) FormLoginConfigurer 初始化的時(shí)候,AbstractAuthenticationFilterConfigurer 也會(huì)初始化,在 AbstractAuthenticationFilterConfigurer 的構(gòu)造方法中,我們可以看到:

          protected AbstractAuthenticationFilterConfigurer() {
          	setLoginPage("/login");
          }

          這就是配置默認(rèn)的 loginPage 為 /login。

          另一方面,F(xiàn)ormLoginConfigurer 的初始化方法 init 方法中也調(diào)用了父類的 init 方法:

          public void init(H http) throws Exception {
          	super.init(http);
          	initDefaultLoginFilter(http);
          }

          而在父類的 init 方法中,又調(diào)用了 updateAuthenticationDefaults,我們來看下這個(gè)方法:

          protected final void updateAuthenticationDefaults() {
          	if (loginProcessingUrl == null) {
          		loginProcessingUrl(loginPage);
          	}
          	//省略
          }

          從這個(gè)方法的邏輯中我們就可以看到,如果用戶沒有給 loginProcessingUrl 設(shè)置值的話,默認(rèn)就使用 loginPage 作為 loginProcessingUrl。

          而如果用戶配置了 loginPage,在配置完 loginPage 之后,updateAuthenticationDefaults 方法還是會(huì)被調(diào)用,此時(shí)如果沒有配置 loginProcessingUrl,則使用新配置的 loginPage 作為 loginProcessingUrl。

          好了,看到這里,相信小伙伴就明白了為什么一開始的登錄接口和登錄頁面地址一樣了。

          2.登錄參數(shù)

          說完登錄接口,我們?cè)賮碚f登錄參數(shù)。

          在上篇文章中,我們的登錄表單中的參數(shù)是 username 和 password,注意,默認(rèn)情況下,這個(gè)不能變:

          <form action="/login.html" method="post">
              <input type="text" name="username" id="name">
              <input type="password" name="password" id="pass">
              <button type="submit">
                <span>登錄</span>
              </button>
          </form>

          那么為什么是這樣呢?

          還是回到 FormLoginConfigurer 類中,在它的構(gòu)造方法中,我們可以看到有兩個(gè)配置用戶名密碼的方法:

          public FormLoginConfigurer() {
          	super(new UsernamePasswordAuthenticationFilter(), null);
          	usernameParameter("username");
          	passwordParameter("password");
          }

          在這里,首先 super 調(diào)用了父類的構(gòu)造方法,傳入了 UsernamePasswordAuthenticationFilter 實(shí)例,該實(shí)例將被賦值給父類的 authFilter 屬性。

          接下來 usernameParameter 方法如下:

          public FormLoginConfigurer<H> usernameParameter(String usernameParameter) {
          	getAuthenticationFilter().setUsernameParameter(usernameParameter);
          	return this;
          }

          getAuthenticationFilter 實(shí)際上是父類的方法,在這個(gè)方法中返回了 authFilter 屬性,也就是一開始設(shè)置的 UsernamePasswordAuthenticationFilter 實(shí)例,然后調(diào)用該實(shí)例的 setUsernameParameter 方法去設(shè)置登錄用戶名的參數(shù):

          public void setUsernameParameter(String usernameParameter) {
          	this.usernameParameter = usernameParameter;
          }

          這里的設(shè)置有什么用呢?當(dāng)?shù)卿浾?qǐng)求從瀏覽器來到服務(wù)端之后,我們要從請(qǐng)求的 HttpServletRequest 中取出來用戶的登錄用戶名和登錄密碼,怎么取呢?還是在 UsernamePasswordAuthenticationFilter 類中,有如下兩個(gè)方法:

          protected String obtainPassword(HttpServletRequest request) {
          	return request.getParameter(passwordParameter);
          }
          protected String obtainUsername(HttpServletRequest request) {
          	return request.getParameter(usernameParameter);
          }

          可以看到,這個(gè)時(shí)候,就用到默認(rèn)配置的 username 和 password 了。

          當(dāng)然,這兩個(gè)參數(shù)我們也可以自己配置,自己配置方式如下:

          .and()
          .formLogin()
          .loginPage("/login.html")
          .loginProcessingUrl("/doLogin")
          .usernameParameter("name")
          .passwordParameter("passwd")
          .permitAll()
          .and()

          配置完成后,也要修改一下前端頁面:

          <form action="/doLogin" method="post">
              <div class="input">
                  <label for="name">用戶名</label>
                  <input type="text" name="name" id="name">
                  <span class="spin"></span>
              </div>
              <div class="input">
                  <label for="pass">密碼</label>
                  <input type="password" name="passwd" id="pass">
                  <span class="spin"></span>
              </div>
              <div class="button login">
                  <button type="submit">
                      <span>登錄</span>
                      <i class="fa fa-check"></i>
                  </button>
              </div>
          </form>

          注意修改 input 的 name 屬性值和服務(wù)端的對(duì)應(yīng)。

          配置完成后,重啟進(jìn)行登錄測(cè)試。

          3.登錄回調(diào)

          在登錄成功之后,我們就要分情況處理了,大體上來說,無非就是分為兩種情況:

          • 前后端分離登錄
          • 前后端不分登錄

          兩種情況的處理方式不一樣。本文我們先來卡第二種前后端不分的登錄,前后端分離的登錄回調(diào)我在下篇文章中再來和大家細(xì)說。

          3.1 登錄成功回調(diào)

          在 Spring Security 中,和登錄成功重定向 URL 相關(guān)的方法有兩個(gè):

          • defaultSuccessUrl
          • successForwardUrl

          這兩個(gè)咋看沒什么區(qū)別,實(shí)際上內(nèi)藏乾坤。

          首先我們?cè)谂渲玫臅r(shí)候,defaultSuccessUrl 和 successForwardUrl 只需要配置一個(gè)即可,具體配置哪個(gè),則要看你的需求,兩個(gè)的區(qū)別如下:

          1. defaultSuccessUrl 有一個(gè)重載的方法,我們先說一個(gè)參數(shù)的 defaultSuccessUrl 方法。如果我們?cè)?defaultSuccessUrl 中指定登錄成功的跳轉(zhuǎn)頁面為 /index,此時(shí)分兩種情況,如果你是直接在瀏覽器中輸入的登錄地址,登錄成功后,就直接跳轉(zhuǎn)到 /index,如果你是在瀏覽器中輸入了其他地址,例如 http://localhost:8080/hello,結(jié)果因?yàn)闆]有登錄,又重定向到登錄頁面,此時(shí)登錄成功后,就不會(huì)來到 /index ,而是來到 /hello 頁面。
          2. defaultSuccessUrl 還有一個(gè)重載的方法,第二個(gè)參數(shù)如果不設(shè)置默認(rèn)為 false,也就是我們上面的的情況,如果手動(dòng)設(shè)置第二個(gè)參數(shù)為 true,則 defaultSuccessUrl 的效果和 successForwardUrl 一致。
          3. successForwardUrl 表示不管你是從哪里來的,登錄后一律跳轉(zhuǎn)到 successForwardUrl 指定的地址。例如 successForwardUrl 指定的地址為 /index ,你在瀏覽器地址欄輸入 http://localhost:8080/hello,結(jié)果因?yàn)闆]有登錄,重定向到登錄頁面,當(dāng)你登錄成功之后,就會(huì)服務(wù)端跳轉(zhuǎn)到 /index 頁面;或者你直接就在瀏覽器輸入了登錄頁面地址,登錄成功后也是來到 /index。

          相關(guān)配置如下:

          .and()
          .formLogin()
          .loginPage("/login.html")
          .loginProcessingUrl("/doLogin")
          .usernameParameter("name")
          .passwordParameter("passwd")
          .defaultSuccessUrl("/index")
          .successForwardUrl("/index")
          .permitAll()
          .and()

          「注意:實(shí)際操作中,defaultSuccessUrl 和 successForwardUrl 只需要配置一個(gè)即可。」

          3.2 登錄失敗回調(diào)

          與登錄成功相似,登錄失敗也是有兩個(gè)方法:

          • failureForwardUrl
          • failureUrl

          「這兩個(gè)方法在設(shè)置的時(shí)候也是設(shè)置一個(gè)即可」。failureForwardUrl 是登錄失敗之后會(huì)發(fā)生服務(wù)端跳轉(zhuǎn),failureUrl 則在登錄失敗之后,會(huì)發(fā)生重定向。

          4.注銷登錄

          注銷登錄的默認(rèn)接口是 /logout,我們也可以配置。

          .and()
          .logout()
          .logoutUrl("/logout")
          .logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
          .logoutSuccessUrl("/index")
          .deleteCookies()
          .clearAuthentication(true)
          .invalidateHttpSession(true)
          .permitAll()
          .and()

          注銷登錄的配置我來說一下:

          1. 默認(rèn)注銷的 URL 是 /logout,是一個(gè) GET 請(qǐng)求,我們可以通過 logoutUrl 方法來修改默認(rèn)的注銷 URL。
          2. logoutRequestMatcher 方法不僅可以修改注銷 URL,還可以修改請(qǐng)求方式,實(shí)際項(xiàng)目中,這個(gè)方法和 logoutUrl 任意設(shè)置一個(gè)即可。
          3. logoutSuccessUrl 表示注銷成功后要跳轉(zhuǎn)的頁面。
          4. deleteCookies 用來清除 cookie。
          5. clearAuthentication 和 invalidateHttpSession 分別表示清除認(rèn)證信息和使 HttpSession 失效,默認(rèn)可以不用配置,默認(rèn)就會(huì)清除。

          好了,今天就先說這么多,這塊還剩一些前后端分離交互的,松哥在下篇文章再來和大家細(xì)說。

          「如果感覺有收獲,記得點(diǎn)一下右下角在看哦」


          主站蜘蛛池模板: 精品天海翼一区二区| 91video国产一区| 无码欧精品亚洲日韩一区夜夜嗨| 国产亚洲综合精品一区二区三区| 精品国产福利在线观看一区| 国产一区二区精品久久91| 日本一区二区三区在线视频 | 精品国产亚洲一区二区三区在线观看 | 亚洲国产一区二区视频网站| 国产精品毛片a∨一区二区三区| 亚洲一区AV无码少妇电影☆| 久久久久人妻一区精品色| 亚洲丰满熟女一区二区哦| 一区 二区 三区 中文字幕| 日韩欧国产精品一区综合无码| 亚洲欧洲精品一区二区三区| 精品福利一区3d动漫| 久久久久无码国产精品一区| 日本一区二区三区四区视频| 国模无码人体一区二区| 无码一区二区三区爆白浆| 精品无码综合一区| 一区二区传媒有限公司| 久久一区二区三区免费| 无码中文字幕人妻在线一区二区三区| 国产不卡视频一区二区三区| 精品一区二区三区在线观看视频| 国产伦精品一区二区免费| 免费日本一区二区| 中文精品一区二区三区四区| 亚洲视频一区二区三区四区| 日韩av片无码一区二区三区不卡| 成人无码一区二区三区| 韩国一区二区视频| 日韩在线一区二区| a级午夜毛片免费一区二区| 久久久精品人妻一区亚美研究所 | 无码一区二区三区免费| 国产高清视频一区三区| 无码人妻精品一区二区蜜桃| 亚洲制服丝袜一区二区三区|