首頁 > 軟體

jfinal中stateless模式嵌入shiro驗證的實現方式

2022-06-24 18:00:57

問題起源

在前些天的文章中,我們瞭解到困惑了我們好幾天的問題是由於jfinal新版中使用undertowServer方式啟動,其嵌入filter的方式有變動,所以導致網上檢索到的通過web.xml嵌入filter失敗。

在不考慮修改undertowServer的情況下,也就意味著我們需要找到一種在undertowServer環境下,嵌入shiro的方式。

今天,我們就來嘗試一種通過攔截器來實現的Stateless Jfinal 嵌入方式。

Stateless的理解

個人對Stateless的理解就是前後端分離,兩次請求互相獨立,通過約定的token等內容判斷是否是同一個使用者。

因此這要求,登入介面需要給使用者生成一個隨機的token,以便使用者後續存取的時候帶上。

登入介面

登入介面首先需要我們存取資料庫,以及通過特定演演算法來驗證使用者名稱與密碼是否匹配。如果匹配,則生成隨機的字串,即token,並儲存在redis中,注意,對映關係是token為key,value為使用者資訊,可以是使用者名稱,也可以是使用者id等使用者唯一標識。

@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("使用者名稱與密碼錯誤");
    }
}

另外,需要注意的有兩點:

  • 介面前呼叫@Clear,即登入介面不應該被攔截驗證
  • 系統的登入介面,與shiro中的subject.login應該注意區分,是兩個不同的概念。

自定義攔截器

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("許可權錯誤:", e);
            try {
                throw e;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
 
    /**
     * 未授權處理
     *
     * @param controller controller
     */
    private void doProcessuUnauthorization(Controller controller) {
        controller.redirect("/login/noLogin");
    }
}

上面的程式碼很長,我們重點看其中的這幾行:

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,若不為空,將其轉換為JWTToken物件,然後呼叫shiro的登入介面:s.login(jwtToken)

而shiro的login方法會觸發自定義Realm中的驗證介面:

/**
	 * 自定義認證
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
		String token = (String) auth.getCredentials();
		 // 解密獲得username,用於和資料庫進行對比
        String userName = JwtUtils.getUsername(token);
        if (userName == null || userName == "") {
            throw new AuthenticationException("token 校驗失敗");
        }
		return new SimpleAuthenticationInfo(token, token, getName());
	}

其中,JwtUtils。getUsername的具體程式碼如下,和設定token是對應的:

/**
     * @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的嵌入。

遺留問題

目前欠缺的一個問題是,不能實現shiro的註解來進行許可權驗證,這個問題我們還準備藉助ShiroPlugin來實現,由於jfinal已經升級到4.8了,而shiroPlugin目前還停留在支援jfinal 3.x的版本,所以需要我們下載jfianl-shiro-plugin原始碼做一些修改。

到此這篇關於jfinal中stateless模式嵌入shiro驗證的實現方式的文章就介紹到這了,更多相關jfinal shiro驗證內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com