jfinal中stateless模式嵌入shiro驗(yàn)證的實(shí)現(xiàn)方式
問(wèn)題起源
在前些天的文章中,我們了解到困惑了我們好幾天的問(wèn)題是由于jfinal新版中使用undertowServer方式啟動(dòng),其嵌入filter的方式有變動(dòng),所以導(dǎo)致網(wǎng)上檢索到的通過(guò)web.xml嵌入filter失敗。
在不考慮修改undertowServer的情況下,也就意味著我們需要找到一種在undertowServer環(huán)境下,嵌入shiro的方式。
今天,我們就來(lái)嘗試一種通過(guò)攔截器來(lái)實(shí)現(xiàn)的Stateless Jfinal 嵌入方式。
Stateless的理解
個(gè)人對(duì)Stateless的理解就是前后端分離,兩次請(qǐng)求互相獨(dú)立,通過(guò)約定的token等內(nèi)容判斷是否是同一個(gè)用戶。
因此這要求,登錄接口需要給用戶生成一個(gè)隨機(jī)的token,以便用戶后續(xù)訪問(wèn)的時(shí)候帶上。
登錄接口
登錄接口首先需要我們?cè)L問(wèn)數(shù)據(jù)庫(kù),以及通過(guò)特定算法來(lái)驗(yàn)證用戶名與密碼是否匹配。如果匹配,則生成隨機(jī)的字符串,即token,并保存在redis中,注意,映射關(guān)系是token為key,value為用戶信息,可以是用戶名,也可以是用戶id等用戶唯一標(biāo)識(shí)。
@Clear public void Login() { String name = getPara("name"); String password = getPara("password"); if ("admin".equals(name)) { // TODO 判斷密碼與用戶名是否正確 Cache cache = Redis.use(); String token = StrKit.getRandomUUID(); cache.set("TOKEN:" + token, name); renderText(token); } else { renderText("用戶名與密碼錯(cuò)誤"); } }
另外,需要注意的有兩點(diǎn):
- 接口前調(diào)用@Clear,即登錄接口不應(yīng)該被攔截驗(yàn)證
- 系統(tǒng)的登錄接口,與shiro中的subject.login應(yīng)該注意區(qū)分,是兩個(gè)不同的概念。
自定義攔截器
package com.holdoa.core.interceptor; import com.holdoa.core.controller.BaseController; import com.holdoa.core.filter.JWTToken; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import com.jfinal.core.Controller; import com.jfinal.kit.LogKit; import com.jfinal.kit.StrKit; import org.apache.shiro.SecurityUtils; import org.apache.shiro.aop.MethodInvocation; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.aop.AnnotationsAuthorizingMethodInterceptor; import org.apache.shiro.subject.Subject; import java.lang.reflect.Method; public class MyShiroInterceptor extends AnnotationsAuthorizingMethodInterceptor implements Interceptor { public MyShiroInterceptor() { getMethodInterceptors(); } public void intercept(final Invocation inv) { try { String token = inv.getController().getHeader("token"); if (StrKit.isBlank(token)) { BaseController b = (BaseController) inv.getController(); b.renderAppError("缺少token"); return; } else { Subject s = SecurityUtils.getSubject(); JWTToken jwtToken = new JWTToken(token); s.login(jwtToken); inv.invoke(); } } catch (Throwable e) { if (e instanceof AuthorizationException) { doProcessuUnauthorization(inv.getController()); } LogKit.warn("權(quán)限錯(cuò)誤:", e); try { throw e; } catch (Throwable throwable) { throwable.printStackTrace(); } } } /** * 未授權(quán)處理 * * @param controller controller */ private void doProcessuUnauthorization(Controller controller) { controller.redirect("/login/noLogin"); } }
上面的代碼很長(zhǎng),我們重點(diǎn)看其中的這幾行:
String token = inv.getController().getHeader("token"); if (StrKit.isBlank(token)) { BaseController b = (BaseController) inv.getController(); b.renderAppError("缺少token"); return; } else { Subject s = SecurityUtils.getSubject(); JWTToken jwtToken = new JWTToken(token); s.login(jwtToken); inv.invoke(); }
邏輯可以描述為:獲取token,若不為空,將其轉(zhuǎn)換為JWTToken對(duì)象,然后調(diào)用shiro的登錄接口:s.login(jwtToken)
。
而shiro的login方法會(huì)觸發(fā)自定義Realm中的驗(yàn)證接口:
/** * 自定義認(rèn)證 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { String token = (String) auth.getCredentials(); // 解密獲得username,用于和數(shù)據(jù)庫(kù)進(jìn)行對(duì)比 String userName = JwtUtils.getUsername(token); if (userName == null || userName == "") { throw new AuthenticationException("token 校驗(yàn)失敗"); } return new SimpleAuthenticationInfo(token, token, getName()); }
其中,JwtUtils。getUsername的具體代碼如下,和設(shè)置token是對(duì)應(yīng)的:
/** * @return token中包含的用戶名 */ public static String getUsername(String token) { Cache cache = Redis.use(); String username = (String)cache.get(RedisKeyPreFix.NEW_OA_MANAGE_TOKEN_PREFIX + token); return username; }
如此,便做到了shiro的嵌入。
遺留問(wèn)題
目前欠缺的一個(gè)問(wèn)題是,不能實(shí)現(xiàn)shiro的注解來(lái)進(jìn)行權(quán)限驗(yàn)證,這個(gè)問(wèn)題我們還準(zhǔn)備借助ShiroPlugin來(lái)實(shí)現(xiàn),由于jfinal已經(jīng)升級(jí)到4.8了,而shiroPlugin目前還停留在支持jfinal 3.x的版本,所以需要我們下載jfianl-shiro-plugin源碼做一些修改。
到此這篇關(guān)于jfinal中stateless模式嵌入shiro驗(yàn)證的實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)jfinal shiro驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 如何從list中刪除符合條件的數(shù)據(jù)
這篇文章主要介紹了Java 如何從list中刪除符合條件的數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11mybatisplus的連表增強(qiáng)插件mybatis plus join
本文主要介紹了mybatisplus的連表增強(qiáng)插件mybatis plus join,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06JAVA格式化時(shí)間日期的簡(jiǎn)單實(shí)例
這篇文章主要介紹了JAVA格式化時(shí)間日期的簡(jiǎn)單實(shí)例,有需要的朋友可以參考一下2013-11-11java final 和instanceof 關(guān)鍵字的區(qū)別
這篇文章介紹了java final 和instanceof 關(guān)鍵字的區(qū)別,有需要的朋友可以參考一下2013-09-09如何基于SpringBoot實(shí)現(xiàn)人臉識(shí)別功能
人工智能時(shí)代的到來(lái),相信大家已耳濡目染,虹軟免費(fèi),離線開放的人臉識(shí)別SDK,正推動(dòng)著全行業(yè)進(jìn)入刷臉時(shí)代,下面這篇文章主要給大家介紹了關(guān)于如何基于SpringBoot實(shí)現(xiàn)人臉識(shí)別功能的相關(guān)資料,需要的朋友可以參考下2022-05-05Spring復(fù)雜對(duì)象創(chuàng)建的方式小結(jié)
這篇文章主要介紹了Spring復(fù)雜對(duì)象創(chuàng)建的三種方式,現(xiàn)在使用Spring如何創(chuàng)建這種類型的對(duì)象?Spring中提供了三種方法來(lái)創(chuàng)建復(fù)雜對(duì)象,需要的朋友可以參考下2022-01-01Java使用注解實(shí)現(xiàn)防止重復(fù)提交實(shí)例
這篇文章主要介紹了Java使用注解實(shí)現(xiàn)防止重復(fù)提交實(shí)例,在一些項(xiàng)目中由于用戶誤操作,多次點(diǎn)擊表單提交按鈕,會(huì)產(chǎn)生很多次的數(shù)據(jù)交互,為了解決這一問(wèn)題,本文使用注解來(lái)實(shí)現(xiàn)防止重復(fù)提交,需要的朋友可以參考下2023-07-07源碼分析Java中ThreadPoolExecutor的底層原理
這篇文章主要帶大家從源碼分析一下Java中ThreadPoolExecutor的底層原理,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考一下2023-05-05js中去除字符串中所有的html標(biāo)簽代碼實(shí)例
這篇文章主要介紹了js中去除字符串中所有的html標(biāo)簽代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08