JFianl整合Shiro

原文:https://michaelzx.github.io/2016/jfinal-shiro-integration/html

入門必看

英文好的能夠直接看官網教程,英文很差的能夠看下開濤的博客《跟我學Shiro》系列java

在看教程以前,最好了解想一些shiro的概念:Apache Shiro Terminologygit

本身入門時踩的坑

一開始的時候,我本身搞了一個JFinal的全局Interceptor ,用來作Shiro 的權限判斷。緣由是一開始不瞭解Shiro,不知道運用Shiro Filter,理所固然地從JFinal的層面進行思考。github

固然,在JFinal Interceptor 中也是能夠作一些權限的判斷的,不過順序上面先要通過ShiroFilter,而後纔會到JFinalFilter,真正進入JFinalweb

現成方案

能夠直接拿來用,能夠作參考,我在實踐的時候,也參考了很多ajax

JFinalShiroPlugin

項目地址:http://git.oschina.net/myaniu/jfinalshiroplugin數據庫

JFinal_Authority

項目地址:http://git.oschina.net/jayqqaa12/JFinal_Authorityexpress

Dreampie/jfinal-shiro

項目地址:https://github.com/Dreampie/jfinal-shiroapache

現成方案之我的感覺

部分現成方案中使用了Plugin、Interceptor、Annotation的方式來集成Shrio,我的不是很是喜歡,我的感受有2個痛點:緩存

一、須要好多代碼去實現Plugin、Interceptor、Annotation,雖然別人寫好了,可是你仍是得去了解源碼,萬一有個坑呢:),畢竟不是什麼大而成熟且運用普遍的解決方案

二、你項目裏有這麼多代碼,想一想一下

  • 某一天你想知道哪一個Controller或Action裏面加了Shiro鑑權,哪些沒有加
  • 某一天你想知道各個加了Shrio鑑權的Controller或Action所對應的權限表達式是什麼
  • 某一天你想知道加一個權限判斷,或去掉一個

你得去找,去改,從新編譯,從新部署。無論你瘋不瘋,反正我感受我會瘋的。

個人選擇

既然Shiro是專業的那就讓它作好該作的。

  1. 在shiro.ini裏面,配置自定義Realm+自定義filters+url表達式,來實現自定義的驗證方式
  2. 用web來配置:資源、角色、用戶、權限

版本

我如今使用是

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.2.4</version>
</dependency>

配置web.xml

<listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>
  • <listener> 節點定義了一個ServletContextListener,在web應用程序啓動的生時候啓動Shiro環境(包括shiro的SecurityManager)默認狀況下, 這個listener會自動去找咱們的WEB-INF/classes/shiro.ini。

  • <filter> 節點定義了主要的ShiroFilter.這個filter被要求去過濾全部進入web應用程序的請求,所以shiro能夠在一個請求到達應用程序以前進行必要的身份驗證和訪問控制。

  • <filter-mapping> 節點確保全部請求類型經過被ShiroFilterare提出(filed)filter-mapping節點通常是不指定dispatcher元素的,可是shiro須要它們都被定義,以便它可以過濾全部可能被web應用執行的不一樣請求類型。

添加shiro.ini文件

如下是我經常使用的shiro.ini

[main]
#sessionId相關設定
sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie
sessionIdCookie.name=jshop-admin-web
sessionIdCookie.path=/
sessionIdCookie.maxAge=1800
sessionIdCookie.httpOnly=true

#sessionManager
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionIdCookie = $sessionIdCookie
sessionManager.sessionIdCookieEnabled = true
sessionManager.globalSessionTimeout = 3600000

#Realm
dbRealm = com.yourdomain.module.shiro.Realm

#Cache
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager

#---------------
securityManager.sessionManager=$sessionManager
securityManager.realm = $dbRealm
securityManager.cacheManager = $cacheManager

#[filters] 這個已經被取消,併入main中
#自定義驗證過濾器
app_auth= com.yourdomain.module.shiro.AuthorizeFilter
#登陸頁面
app_auth.loginUrl = /auth/login
#未受權頁面
app_auth.unauthorizedUrl=/auth/unauthorized

[urls]
/test/** = anon
/public/** = anon
/uploads/** = anon
/passport/* = anon
/** = app_auth

sessionIdCookie、sessionManager

這倆個彷佛沒啥好說的。

cacheManager

它定義了一個新的緩存管理實例. 緩存在Shiro的構架體系中是一個很是重要的部分 - 它減小了和數據存貯之間持續往返的通信。這個例子是使用了在單個JVM上比較好使的MemoryConstrainedCacheManager。若是對你的應用是部署在多個服務器(好比服務器集羣)的話,你將會想使用一個集羣緩存管理器的實現來替代。

realm

它是很關鍵的一個地方,這是是須要本身實現的。它做爲shiro的一個組件,可讓shiro訪問到你的系統中的用戶、角色、權限等數據。

[urls]

這是很是重要的一個節點,來配置哪些路徑映射哪些過濾器來進行鑑權,能夠用逗號分開,配置多個過濾器。

這裏的anon是shiro內置的一個過濾器,表示不須要進行鑑權。固然仍是不少的shiro內置鑑權過濾器.在後面簡單介紹下

[filters]

1.2之後,filters被併入[main]節點,若是繼續保留也沒事兒,只是會出個警告而已。app_auth是我本身實現的一個filter系統中主要使用這個filter進行鑑權。

shiro的內置filters

  • anon
    org.apache.shiro.web.filter.authc.AnonymousFilter
  • authc
    org.apache.shiro.web.filter.authc.FormAuthenticationFilter
  • authcBasic
    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
  • logout
    org.apache.shiro.web.filter.authc.LogoutFilter
  • noSessionCreation
    org.apache.shiro.web.filter.session.NoSessionCreationFilter
  • perms
    org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
  • port
    org.apache.shiro.web.filter.authz.PortFilter
  • rest
    org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
  • roles
    org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
  • ssl
    org.apache.shiro.web.filter.authz.SslFilter
  • user
    org.apache.shiro.web.filter.authc.UserFilter

自定義realm

com.yourdomain.module.shiro.Realm

須要繼承shiro的AuthorizingRealm

public class Realm extends AuthorizingRealm

須要實現2個抽象方法

doGetAuthenticationInfo

主要是在登陸的時候,進行用戶身份驗證

/**
 * 獲取用戶驗證信息
 * @param authcToken 所需驗證的token
 * @return null or 身份信息
 * @throws AuthenticationException 驗證異常
 */
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
    SysAdmin admin = AdminService.getByUsername(token.getUsername());
    if (admin != null) {
        if(!admin.getPassword().equals(String.valueOf(token.getPassword()))){
            throw new AuthenticationException("密碼錯誤");
        }
        Db.update("update sys_admin set loginTime=?,loginCount=loginCount+1 where id=?",new Date(),admin.getId());
        return new SimpleAuthenticationInfo(admin, admin.getPassword(),admin.getUsername());
    } else {
        throw new AuthenticationException("用戶不存在");
    }

}

doGetAuthorizationInfo

在第一次鑑權的時候進行調用,獲取並保存到chache中(沒有配置cache是否是每次都得調用?)

/**
 * 獲取用戶受權信息
 * @param principals 用戶身份
 * @return null or 受權信息
 */
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    SysAdmin userInPrincipal = (SysAdmin) principals.getPrimaryPrincipal();
    //根據用戶獲取權限
    List<String> stringPermissions = AdminService.getPermissions(userInPrincipal.getId());
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //stringPermissions結構:
    //user
    //user:list
    //user:add
    //user:edit
    //...
    info.addStringPermissions(stringPermissions);
    return info;
}

我這裏返回的:字符串權限表達式(字符串通配符權限),對於各自所對應的資源(主要就是url路徑),我是保存在數據庫中,方便進行配置,而後再加上緩存。在自定義filter中將url轉換成對應的表達式,而後進行鑑權。

登陸、註銷

登陸

public void login() {
    if(Boolean.FALSE.equals(validateCaptcha("captcha"))){
        renderJson(CommonService.ajaxError("驗證碼錯誤"));
        return;
    }
    String username = getPara("username");
    String password = HashKit.md5(getPara("password"));
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    try {
        subject.login(token);
        renderJson(CommonService.ajaxSuccess());
    } catch (AuthenticationException e) {
    	//雖然在realm中有具體的錯誤信息,可是安全起見,統一返回登陸失敗
        renderJson(CommonService.ajaxError("登錄失敗"));
    }
}

註銷

public void logout() {
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
    redirect("/passport/login");
}

是否是感受很easy:)

自定義filter

com.yourdomain.module.shiro.AuthorizeFilter

繼承shiro的AuthorizationFilter

public class AuthorizeFilter extends AuthorizationFilter

實現抽象方法isAccessAllowed

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    //-----------------用戶驗證------------------
    Subject currentUser = getSubject(request, response);
    if (!currentUser.isAuthenticated())
        return false;
    //-----------------獲取資源權限表達式-------------
    SysAdmin user = (SysAdmin) currentUser.getPrincipal();
    //request中加入attribute便於controller調用admin的信息
    request.setAttribute("admin",user);
    // 根據actionKey分析出權限表達式
    HttpServletRequest hsr = ((HttpServletRequest) request);
    String root = hsr.getContextPath();
    String URI = hsr.getRequestURI();
    String actionKey = URI.replace(root,"");
    if("".equals(actionKey))
        actionKey="/";
    RoleService roleService = new RoleService();
    String expression = roleService.getActionKeyExpression(actionKey);
    //-----------------進行鑑權-------------
    if (user==null)
        return false;
    else if(user.getStr("username").equals("superadmin")){
    	//超級管理員具備全部權限
        return true;
    }else if(expression==null){
        return false;
    }else if(currentUser.isPermitted(expression)){
    	//鑑權
        return true;
    }else{
        return false;
    }

}
  1. 在這裏,我根據actionKey分析出所對應的權限表達式
  2. 對於用戶superadmin,默認具備全部權限,等同於,不須要鑑權
  3. 其餘用戶根據權限表達式,來進行鑑權

關於用戶、角色、url資源及對應的權限表達式

你能夠按照你本身的方式來構建一套,我相信對於大多數人應該不成問題,由於這個已經不屬於shiro的範疇了。本身搞幾個表,搞幾個配置界面,作下緩存策略等等。

模板引擎中使用擴展函數(標籤)

有時候你的應用也許須要在界面上進行鑑權,好比按鈕啥的,這時候就可能須要擴展模板引擎的函數或者標籤。

能夠參考下 《beetl 和 shrio 結合》 http://my.oschina.net/xiandafu/blog/143109

只要解決如何擴展模板引擎的函數或者標籤,其餘,我想應該都是雷同的吧。

相關文章
相關標籤/搜索