學習oauth認證以前先回顧一下經過sessionid的會話過程html
關於session與cookie的請戳:http://www.javashuo.com/article/p-ttnbgalf-gy.html前端
那麼這種利用session的會話方式會引起哪些問題呢node
1.安全問題ios
常見保存會話方式:cookie,session以及token等等,這裏咱們將這三種方式的安全性能都簡單的分析一下嘿嘿~git
cookie:咱們瞭解cookie的安全問題首先從客戶端頒發cookie開始,而後經過set-cookie響應給客戶端,客戶端保存以後下次通訊會以默認的方式攜帶cookie,這種東西是程序員不可控的,這時候會發生一個什麼問題,當網頁中的一段link代碼發生XSS攻擊時,會獲取到本程序員
地cookie。那麼問題來了。爲啥?由於cookie默認寫到http的headers裏,無法控制。那麼好,咱們退出登陸,這時候服務器對cookie怎麼操做的,經過set-cookie的方式清空cookie並返回到客戶端。這樣致使一個問題,已經被劫持了的cookie還能用,這就是爲啥cookie不github
安全的緣由。web
session:首先咱們要知道,session究竟是個啥,個人理解是session就是cookie的另外一種形式,一樣session是服務器頒發給客戶端的,一樣經過響應中的set-cookie,可是與此同時服務器還會保存這個session id,本質上都是經過cookie傳遞,本質上均可以被上訴攻擊方式攻擊,可是好處在哪,當退出登陸的時候會服務器對session id會有個刪除操做。這樣被劫持的session id其實就沒用了,可是無論怎樣,承擔的風險方式與cookie是同樣的。數據庫
token:session與cookie都存在風險,那麼更好的解決方案是什麼呢,就是token,放在後面說。express
2.服務器壓力過大
session的特色之一就是存在服務器端,請求過多的時候session也會增多,這就會致使服務器的壓力過大。
3.分佈式session管理困難
如今幾乎全部的web應用程序都會採用分佈式的處理方案,那麼每次一塊兒請求不必定發送到哪一個服務器,好比此次請求發送的Server A上而且保存了session,下一個請求我發Server B上了,Server B沒有啊,怎麼校驗,這就會引起另外一個問題,分佈式下如何共享session。固然如今有好多的方式好比:Session Replication方式(session複製),緩存集中方式管理(將Session集中放在一個服務器上,咱們也能夠稱其session服務器),基於Redis進行session共享等等,很明顯服務器壓力很大,並且並很差管理。
4.跨域問題
首先啥叫跨域?域名不一樣,協議不一樣,或者端口號不一樣統統稱爲跨域,跨域產生於瀏覽器的同源策略,請求發出去了,可是響應被攔截了。攜帶headers裏面的cookie是不能夠跨域的,可是authorization能夠啊,token存在哪?token就存在authorization裏啊,emmmm~好處顯而易見。
說了這麼多session的缺點,時代老是向前發展的,技術也同樣呀,因而乎token誕生了~
什麼是token
token能夠理解成一個帶有User基本信息的令牌,例如每一次會話調用服務器的API時,服務器能夠經過token判斷是否有權限。
token流程
token種類
id token(不知道幹啥用的,沒用過,見過存session id的)
access token(用來作權限認證,生命週期相對比較短)
refresh token(用來生成新的access token,生命週期相對比較長)
token特色有哪些
1.安全性能更好
以前列舉了cookie和session的安全性能問題,那麼token爲啥就安全了?首先token是程序員本身寫入http headers裏的,並非像cookie同樣自動寫入http的headers裏的,若是不必的咱們不寫入,XSS攻擊方式是獲取不到token的。
2.無狀態(多個服務共享,減輕服務器的鴨梨)
token是無狀態的,也就是說token並不存入服務器中(若是想存,請便)當服務器認證事後,會生成一個token返回給用戶,而且存入瀏覽器緩存裏(localstorage等),接下來的請求會咱們會獲取這個token並放入authorization內,服務器接收到請求,並解析token來判斷當前請求是否有權限調用api,這裏引出另外一個概念鑑權。
3.跨程序調用(避開同源策略)
當token變成無狀態,只要一個token,就能夠在任何一個服務器上認證(解析方式必須一致,其實都是代碼層面的東西)因而咱們能夠採用一種設計方式叫,分離認證服務與業務任務。當咱們能夠經過認證服務器來獲取token,而後發送給業務服務器的時候校驗token,這時會出現兩種方式,第一種是在業務服務器校驗token,另外一種是毎一次請求經過中間層再發送給認證服務器進行校驗,兩種設計方式各有優缺點,無論怎樣都是實現了跨程序共享token,咦沒有跨域問題哦(固然須要服務器配合嚶嚶嚶~)到這裏就引出另外一個概念,OAuth認證。
什麼是OAuth認證
OAuth是一個關於受權(authorization)的開放網絡標準,目前的版本是2.0版,即OAuth2.0
OAuth的應用場景
一個公司每每會有不少系統,好比HR系統,你的請假吧,好比公司的內部員工網站,得有業餘活動吧,再好比公司好多產品,可是會有個產品是能夠登陸全部網站的吧,總不能好多產品分紅好多認證系統吧,能用就一個會節約多少人力物力呢~這裏會引出一個概念SSO(單點登陸)SSO實際上是一種解決方案,俺們公司的叫AOS系統。這個經過一個系統登陸定向到各個產品的受權過程就是OAuth認證。
OAuth認證設計方式(這裏列兩種,高大上的我也不知道):
業務服務器校驗token
優勢:相對於第二種不用每一次請求都要經過鑑權服務器,能夠保證token的新鮮度。
缺點:效率低。
認證服務器校驗token
優勢:效率高
缺點:若是是個第三方的鑑權服務token信息更新不及時
整個鑑權過程都是經過access token,因爲有效時間比較短,若是我想要半個月登陸一次呢,也就說半個月以內不須要從新登陸,怎麼辦refresh token的做用就產生了。
refresh token作了些什麼事兒呢
經過獲取access token的有效時間,判斷當access token立刻過時的時候,這時候我能夠經過refresh token從權限服務器獲取新的access token,這均可以偷摸的作了,反正使用者不會知道。
1.客戶端發送refresh token請求
2.服務器發送refresh token
token的實現方式
現有的token解析方式其實並不惟一,可是有個很出名的那就是jwt(json web token)
什麼是jwt
能夠理解jwt是token的一種規則
jwt的組成
1.Header 2.Payload 3.Signature
jwt的特色(來自阮一峯)
JWT 默認是不加密,但也是能夠加密的。生成原始 Token 之後,能夠用密鑰再加密一次。
JWT 不加密的狀況下,不能將祕密數據寫入 JWT。
JWT 不只能夠用於認證,也能夠用於交換信息。有效使用 JWT,能夠下降服務器查詢數據庫的次數。
JWT 的最大缺點是,因爲服務器不保存 session 狀態,所以沒法在使用過程當中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發了,在到期以前就會始終有效,除非服務器部署額外的邏輯。
JWT 自己包含了認證信息,一旦泄露,任何人均可以得到該令牌的全部權限。爲了減小盜用,JWT 的有效期應該設置得比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。
爲了減小盜用,JWT 不該該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。
栗子(github登陸本地站點)
技術棧:nodejs
獲取github的token過程
1.註冊github應用
找到settings
找到OAuths Apps
註冊成功
2.獲取github受權碼
這裏咱們須要將註冊過得client id跟回調函數當作參數傳入
handleGitHubLogin() { let path = `https://github.com/login/oauth/authorize?response_type=code&redirect_uri=${CommonUtil.Config.github.redirect_uri}&scope=user%2Crepo&client_id=${CommonUtil.Config.github.client_id}`; location.href = path; }
這時候會跳到github的受權頁面
受權成功後會返回一個code(受權碼)
經過code clientId clientSecret獲取accessToken
router.post("/oAuthValidate", (req, res) => { let { clientId, clientSecret, code } = req.body; axios({ method: "post", url: "https://github.com/login/oauth/access_token?" + `client_id=${clientId}&` + `client_secret=${clientSecret}&` + `code=${code}`, headers: { accept: "application/json" } }) .then(tokenResponse => { let accessToken = tokenResponse.data.access_token; getGitHubToken(accessToken, res); }) .catch(e => { console.log(e); }); });
因爲整個應用程序採用SAP,因此整個流程經過前臺獲取最合理,但前臺獲取token必然會出現一個問題就是跨域,那麼如何解決前端跨域資源共享問題呢,這裏提供一個解決方案就是Gatekeeper,github本身去找吧,使用方式以後補上。
經過accessToken獲取User信息
function getGitHubToken(accessToken, res) { axios({ method: "get", url: `https://api.github.com/user`, headers: { accept: "application/json", Authorization: `token ${accessToken}` } }) .then(result => { let token = jwt.sign(result.data, "my_token", { expiresIn: "1h" }); util.responseClient(res, 200, 0, "獲取github token成功", { profileInfo: result.data, accessToken: token }); }) .catch(e => { util.responseClient(res, 500, 0, "get github token failed.", { message: e }); }); }
這裏爲了使用jwt,我並無使用github的accessToken,而是在本身的應用程序裏使用了jwt,能夠自行選擇。
返回給瀏覽器而且保存到緩存裏
再次請求的時候進行token鑑權
app.use( expressjwt({ secret: "my_token", credentialsRequired: true, //若是false 則authoriaztion爲空時也經過。 getToken: function fromHeaderOrQuerystring(req) { if ( req.headers.authorization && req.headers.authorization.split(" ")[0] === "Bearer" ) { var token = req.headers.authorization.split(" ")[1]; return token; } else if (req.query && req.query.token) { return req.query.token; } return null; } }).unless({ path: util.whiteList }) ); function checkPromisition(req, res, next) { if (1) { return next(); } else { util.responseClient(res, 500, 0, "delete github token failed.", { log: "delete github token failed with http code 401." }); } } app.use(function (err, req, res, next) { if (err.name === "UnauthorizedError") { util.responseClient(res, 403, 0, "invalid token...", {}); } }); app.use("/leavemessage",checkPromisition,require("./leavemessage"));
jwt使用方式
引入jwt中間件
const jwt = require("jsonwebtoken");
jwt生成
let token = jwt.sign(result.data, "my_token", { expiresIn: "1h" });
鑑權部分
引入中間件
const expressjwt = require("express-jwt");
具體細節看上面代碼吧
這裏有些問題首先github並無給我refresh token,這裏是我沒找到嗎,請知道的大佬指點一二,其次github沒有暴露鑑權的接口嗎
撤銷github的accesstoken方式
1.清緩存
2.手動本身上去清吧
時間不早了,先寫到這裏吧,若是哪裏理解錯的歡迎指正,我會很感激涕零的。
你的關注是對我最大的支持~蟹蟹~