1. 程式人生 > >Apache Shiro安全(許可權框架)學習筆記

Apache Shiro安全(許可權框架)學習筆記

簡介:

Apache Shiro 是 Java 的一個安全(許可權)框架。
• Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在
JavaSE 環境,也可以用在 JavaEE 環境。
• Shiro 可以完成:認證、授權、加密、會話管理、與Web 整合、快取
等。
• 下載:http://shiro.apache.org/

基本功能:

Authentication:身份認證/登入,驗證使用者是不是擁有相應的身份;
Authorization:授權,即許可權驗證,驗證某個已認證的使用者是否擁有某個許可權;即判斷用
戶是否能進行什麼操作,如:驗證某個使用者是否擁有某個角色。或者細粒度的驗證某個使用者
對某個資源是否具有某個許可權;
Session Manager

:會話管理,即使用者登入後就是一次會話,在沒有退出之前,它的所有
資訊都在會話中;會話可以是普通 JavaSE 環境,也可以是 Web 環境的;
• Cryptography:加密,保護資料的安全性,如密碼加密儲存到資料庫,而不是明文儲存;
Web Support:Web 支援,可以非常容易的整合到Web 環境;
Caching:快取,比如使用者登入後,其使用者資訊、擁有的角色/許可權不必每次去查,這樣可
以提高效率;
Concurrency:Shiro 支援多執行緒應用的併發驗證,即如在一個執行緒中開啟另一個執行緒,能
• 把許可權自動傳播過去;
Testing:提供測試支援;• Run As:允許一個使用者假裝為另一個使用者(如果他們允許)的身份進行訪問;
Remember Me
:記住我,這個是非常常見的功能,即一次登入後,下次再來的話不用登
錄了

 

 

Shiro 架構 (Shiro外部來看)

 

• 從外部來看Shiro ,即從應用程式角度的來觀察如何使用 Shiro 完成
工作:

Subject:應用程式碼直接互動的物件是 Subject,也就是說 Shiro 的對外
API 核心就是 Subject。Subject 代表了當前“使用者”, 這個使用者不一定
是一個具體的人,與當前應用互動的任何東西都是 Subject,如網路爬蟲,
機器人等;與 Subject 的所有互動都會委託給 SecurityManager;
Subject 其實是一個門面,SecurityManager 才是實際的執行者;
SecurityManager

:安全管理器;即所有與安全有關的操作都會與
SecurityManager 互動;且其管理著所有 Subject;可以看出它是 Shiro
的核心,它負責與 Shiro 的其他元件進行互動,它相當於 SpringMVC 中
DispatcherServlet 的角色
Realm:Shiro 從 Realm 獲取安全資料(如使用者、角色、許可權),就是說
SecurityManager 要驗證使用者身份,那麼它需要從 Realm 獲取相應的使用者
進行比較以確定使用者身份是否合法;也需要從 Realm 得到使用者相應的角色/
許可權進行驗證使用者是否能進行操作;可以把 Realm 看成 DataSource

 

Shiro架構(Shiro內部來看)

Subject:任何可以與應用互動的“使用者”;
SecurityManager :相當於SpringMVC 中的 DispatcherServlet;是 Shiro 的心臟;
所有具體的互動都通過 SecurityManager 進行控制;它管理著所有 Subject、且負責進
行認證、授權、會話及快取的管理。
Authenticator:負責 Subject 認證,是一個擴充套件點,可以自定義實現;可以使用認證
策略(Authentication Strategy),即什麼情況下算使用者認證通過了;
Authorizer:授權器、即訪問控制器,用來決定主體是否有許可權進行相應的操作;即控
制著使用者能訪問應用中的哪些功能;
Realm:可以有 1 個或多個 Realm,可以認為是安全實體資料來源,即用於獲取安全實體
的;可以是JDBC 實現,也可以是記憶體實現等等;由使用者提供;所以一般在應用中都需要
實現自己的 Realm;
SessionManager:管理 Session 生命週期的元件;而 Shiro 並不僅僅可以用在 Web
環境,也可以用在如普通的 JavaSE 環境
CacheManager:快取控制器,來管理如使用者、角色、許可權等的快取的;因為這些資料
基本上很少改變,放到快取中後可以提高訪問的效能
Cryptography:密碼模組,Shiro 提高了一些常見的加密元件用於如密碼加密/解密。

 HelloWorld應用

加入如下 jar 包:
– shiro-all-1.3.2.jar
– log4j-1.2.15.jar
– slf4j-api-1.6.1.jar
– slf4j-log4j12-1.6.1.jar

 

package com.mikey.www;

/**
 * @author Mikey
 * @Title:
 * @Description:
 * @date 2018/11/1 21:32
 * @Version 1.0
 */
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Simple Quickstart application showing how to use Shiro's API.
 *
 * @since 0.9 RC2
 */
public class Quickstart {

    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);


    public static void main(String[] args) {

        // The easiest way to create a Shiro SecurityManager with configured
        // realms, users, roles and permissions is to use the simple INI config.
        // We'll do that by using a factory that can ingest a .ini file and
        // return a SecurityManager instance:

        // Use the shiro.ini file at the root of the classpath
        // (file: and url: prefixes load from files and urls respectively):
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        // for this simple example quickstart, make the SecurityManager
        // accessible as a JVM singleton.  Most applications wouldn't do this
        // and instead rely on their container configuration or web.xml for
        // webapps.  That is outside the scope of this simple quickstart, so
        // we'll just do the bare minimum so you can continue to get a feel
        // for things.
        SecurityUtils.setSecurityManager(securityManager);

        // Now that a simple Shiro environment is set up, let's see what you can do:

        // get the currently executing user:
        // 獲取當前的 Subject. 呼叫 SecurityUtils.getSubject();
        Subject currentUser = SecurityUtils.getSubject();

        // Do some stuff with a Session (no need for a web or EJB container!!!)
        // 測試使用 Session
        // 獲取 Session: Subject#getSession()
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("---> Retrieved the correct value! [" + value + "]");
        }

        // let's login the current user so we can check against roles and permissions:
        // 測試當前的使用者是否已經被認證. 即是否已經登入.
        // 調動 Subject 的 isAuthenticated()
        if (!currentUser.isAuthenticated()) {
            // 把使用者名稱和密碼封裝為 UsernamePasswordToken 物件
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            // rememberme
            token.setRememberMe(true);
            try {
                // 執行登入.
                currentUser.login(token);
            }
            // 若沒有指定的賬戶, 則 shiro 將會丟擲 UnknownAccountException 異常.
            catch (UnknownAccountException uae) {
                log.info("----> There is no user with username of " + token.getPrincipal());
                return;
            }
            // 若賬戶存在, 但密碼不匹配, 則 shiro 會丟擲 IncorrectCredentialsException 異常。
            catch (IncorrectCredentialsException ice) {
                log.info("----> Password for account " + token.getPrincipal() + " was incorrect!");
                return;
            }
            // 使用者被鎖定的異常 LockedAccountException
            catch (LockedAccountException lae) {
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            }
            // ... catch more exceptions here (maybe custom ones specific to your application?
            // 所有認證時異常的父類.
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
            }
        }

        //say who they are:
        //print their identifying principal (in this case, a username):
        log.info("----> User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //test a role:
        // 測試是否有某一個角色. 呼叫 Subject 的 hasRole 方法.
        if (currentUser.hasRole("schwartz")) {
            log.info("----> May the Schwartz be with you!");
        } else {
            log.info("----> Hello, mere mortal.");
            return;
        }

        //test a typed permission (not instance-level)
        // 測試使用者是否具備某一個行為. 呼叫 Subject 的 isPermitted() 方法。
        if (currentUser.isPermitted("lightsaber:weild")) {
            log.info("----> You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        //a (very powerful) Instance Level permission:
        // 測試使用者是否具備某一個行為.
        if (currentUser.isPermitted("user:delete:zhangsan")) {
            log.info("----> You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        //all done - log out!
        // 執行登出. 呼叫 Subject 的 Logout() 方法.
        System.out.println("---->" + currentUser.isAuthenticated());

        currentUser.logout();

        System.out.println("---->" + currentUser.isAuthenticated());

        System.exit(0);
    }
}
Quickstart

 日常報錯:

1.Tomcat報錯:部署專案失敗:Unknown version string [4.0]. Default version will be used.

Connected to server
[2018-11-01 10:26:13,142] Artifact Shiro_Spring_Web:war exploded: Artifact is being deployed, please wait...
十一月 01, 2018 10:26:13 下午 org.apache.catalina.deploy.WebXml setVersion
警告: Unknown version string [4.0]. Default version will be used.
十一月 01, 2018 10:26:13 下午 org.apache.catalina.startup.TldConfig execute
資訊: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
十一月 01, 2018 10:26:13 下午 org.apache.catalina.core.StandardContext startInternal
嚴重: One or more listeners failed to start. Full details will be found in the appropriate container log file
十一月 01, 2018 10:26:13 下午 org.apache.catalina.core.StandardContext startInternal
嚴重: Context [] startup failed due to previous errors
[2018-11-01 10:26:13,545] Artifact Shiro_Spring_Web:war exploded: Error during artifact deployment. See server log for details.
報錯資訊

 

報錯原因:

解決方法:

重新配置run as 的Tomcat

 

參考部落格:https://blog.csdn.net/qq_32483145/article/details/80292463