今日目標:實現shiro的整合。
感覺比起之前折騰的這些驗證碼之類的,后面對數據庫的增刪改查反而好簡單了……
Element 'bean' cannot have character [children], because the type's content type is element-only
遇到這個報錯,(IDEA幫我檢測了,沒有等跑起來才出現),是我忘了刪一個XML注釋的尾部。
shiro的框架初步部署完畢,設計一下用戶權限吧。
四個過濾器分別過濾:
用戶有沒有拿到正確的網頁驗證碼
用戶有沒有通過郵件驗證
用戶有沒有登陸
用戶有沒有授權(刪除其他人的文章等)。
分析一下驗證碼流程:
用戶請求驗證碼會拿到一個Token ,服務器將Tk和具體驗證碼存入緩存,將帶Tk的URL返回給用戶。
用戶訪問URL拿到圖片。
用戶將驗證碼和Tk提交給服務器,Controller調用Service進行判斷并返回結果。
以上流程中,使用Tk是為了確保每個驗證碼對應一個Tk,不會出現使用別人的驗證碼登陸的bug。
假設:
用戶如果提交了正確的驗證碼和Tk,接下來請求郵箱驗證碼。
請求郵件驗證碼的過程經過了shiro的captchaFilter,Filter放行。
那么Filter負責判斷Tk是否對應驗證碼。
那么原有Controller中的方法是否保留呢?可以用于前端ajax單獨請求驗證?
心念一轉,實際上并不沖突,假設前端頁面ajax請求了驗證,然后提交Json的時候再次提交驗證確保安全。
決定放棄上述方案,將驗證碼再傳一次浪費服務器資源,直接在Controller里發一個新的Tk,Filter不訪問redis,直接在當時驗證這個Tk是否有效果,降低URL長度。
郵件也類似,Controller里發新Tk,mailFilter驗證Tk。
寫好了基本的captchaFilter,編譯運行報錯:
Failed to convert property value of type 'java.util.LinkedHashMap' to required type 'java.util.Map' for property 'filters';
(之前改了Filter名沒改XML的事就不說了)
后來發現另一個異常(Mapper XML)修改時候沒刪干凈標簽,這個解決了之前那個錯也沒了。。迷
測試一下之前的圖片驗證碼,發現shiro似乎有默認Filter去攔截/api/captcha/get/A.B.C的做法。。改成/get?token=A.B.C就沒事。。難道是什么XSS還是SQL注入的防御嗎。。
回到captchaFilter,服務器報了一個session相關的錯誤。。果然從別的地方復制來的XML都是不靠譜的
19:54:00.982 [http-apr-8080-exec-2] DEBUG o.a.s.s.mgt.DefaultSessionManager - Unable to resolve session ID from SessionKey [org.apache.shiro.web.session.mgt.WebSessionKey@44799bd]. Returning null to indicate a session could not be found.
然后客戶端返回來200,但是body是Syntax error,郵件也沒有發送。
奇怪的是,我設定了網頁驗證碼相關接口不需要Filter:
/api/captcha/** = noSessionCreation,anon
就沒問題。。
仔細查看日志,請求網頁驗證碼也會出這條Unable to resolve session ID from SessionKey的日志。
Content-Length →56
Content-Type →application/json;charset=utf-8
Date →Wed, 31 May 2017 12:24:31 GMT
Server →Apache-Coyote/1.1
實際上是被Filter攔下了,但是沒有返回正確的內容?
測試一下,把Filter里返回的Content-Type改成XML,再在Filter里下幾個斷點。
這時候因為我沒有傳tk,resultMap給的值是state:Invaild,的確取到了。
接下來成功生成了 resp這個JSONObject。
迷之返回。
那為什么是json的時候就顯示SyntaxError呢?
最后發現我基礎邏輯有問題,寫了兩次返回JSON對象的語句,而且還不是組成數組的……
我他媽是傻逼吧……
發現發郵件的接口沒有返回值。
發現我在Filter的通過認證里寫了servletResponse.getWriter().write(resp.toJSONString());
我他媽是傻逼吧*2
我他媽就是個傻逼!!!
Filter驗證通過……
發現一個新問題,在發送郵件驗證碼之后,去檢驗驗證碼是否正確這里,要不要去要求圖片驗證碼的Tk呢……
設想業務場景,用戶注冊,填完表單,前端Ajax請求驗證碼然后驗證,服務端給一個通過圖片驗證碼的TK。
接下來就是前端把Tk和發郵件的請求打包過來,服務端發郵件之后返回一個郵件Tk,然后前端把用戶填寫的驗證碼和郵件Tk發過來,Header帶一個通過圖片驗證碼的Tk。
Header的Tk通過了檢驗,進入mailController的valid路徑,進行檢驗。
這樣設計的壞處就是要帶兩個Tk,Header一個參數一個,容易混淆。
而且接下來如果注冊用戶,還要帶個Tk。。不過這時候圖片驗證碼的Tk可以廢除了,也是2個Tk。
優化一下邏輯吧,同時只能存在一個Tk,所有Tk放在連接里。
目標的邏輯是這樣的:
用戶注冊,填完表單,前端Ajax請求驗證碼,服務端給一個帶隨機Tk的連接,該連接的Content-Type是圖片。
前端將隨機Tk和驗證碼拿去訪問驗證接口,服務端給一個10分鐘有效的象征通過驗證碼的Tk。
前端把象征驗證碼通過的Tk放連接里,用POST方法提交給服務器,服務器發送郵件,成功就返回一個象征發送郵件成功并且和郵件驗證碼綁定的Tk。
前端再把象征發送郵件成功的Tk放連接里,GET提交給服務端,服務端的captchaFilter解析該TK,發現內容是發送郵件成功,判斷肯定經過驗證碼驗證,放行,進入mailController進行檢驗。
檢驗成功,則再返回一個Tk,Tk內容有通過了郵件驗證,交由mailFilter進行解析,如果通過則提交數據到user接口進行注冊。
用戶要求修改密碼,先寫驗證碼,再跳轉到輸入郵件驗證碼的界面,大致邏輯同上。
用戶要求修改密保郵箱,同樣先寫驗證碼,只不過要驗證兩次郵件內容。
寫完mailFilter去玩會osu放松下……
明天做用戶注冊(好像幾天前我就這么說過
最后優化了一波,把JSON對象生成在類里,如果過期就加上過期的提示。