springboot 整合shiro之Authentication

shiro目錄:

  1. springboot 整合shiro之shiroconfig配置文件
  2. springboot 整合shiro之Authentication

shior作爲輕量級的權限管理框架,相較於SpringSecurity框架,沒有其強大的功能,但在是學習和實際使用中,shiro可以滿足基本的使用,包括有身份驗證、授權、加密的功能

shiro的功能介紹,分爲以下幾塊(官方介紹,看不懂 的可以稍微瞅一眼,然後基礎代碼看,回頭再看更加容易理解)

 

Authentication:身份認證/登錄,驗證用戶是不是擁有相應的身份;

Authorization:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能做事情,常見的如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;

Session Manager:會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通 JavaSE 環境的,也可以是如 Web 環境的;

Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;

Web Support:Web 支持,可以非常容易的集成到 Web 環境;

Caching:緩存,比如用戶登錄後,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;

Concurrency:shiro 支持多線程應用的併發驗證,即如在一個線程中開啓另一個線程,能把權限自動傳播過去;

Testing:提供測試支持;

Run As:允許一個用戶假裝爲另一個用戶(如果他們允許)的身份進行訪問;

Remember Me:記住我,這個是非常常見的功能,即一次登錄後,下次再來的話不用登錄

 

詳解:

  1. Authentication:
  • 官方定義:Authentication: Sometimes referred to as ‘login’, this is the act of proving a user is who they say they are.

翻譯:身份驗證,通常是在登錄是,通過對用戶的信息(賬號、密碼)進行驗證,如果登錄失敗會拋出異常

  • 流程圖:

 

Subject:表示“用戶”,表示當前執行的用戶。Subject 實例全部都綁定到了一個 SecurityManager 上,當和 Subject 交互時,它是委託給 SecurityManager 去執行的

SecurityManager:Shiro 結構的心臟,協調它內部的安全組件(如登錄,授權,數據源等)。當整個應用配置好了以後,大多數時候都是直接和 Subject 的 API 打交道。

Realm:數據源,也就是抽象意義上的 DAO 層。它負責和安全數據交互(比如存儲在數據庫的賬號、密碼,權限等信息),包括獲取和驗證。Shiro 支持多個 Realm,但是至少也要有一個。Shiro 自帶了很多開箱即用的 Reams,比如支持 LDAP、關係數據庫(JDBC)、INI 和 properties 文件等。但是很多時候我們都需要實現自己的 Ream 去完成獲取數據和判斷的功能

登錄流程:Subject 執行 login 方法,傳入登錄的用戶名和密碼,然後 SecurityManager將這個 login 操作委託給內部的登錄模塊,登錄模塊就調用 Realm 去獲取安全的用戶名和密碼,然後對比,一致則登錄,不一致則登錄拋出異常

  1. 核心代碼
UsernamePasswordToken token = new UsernamePasswordToken(username, password);

 

  • 通過用戶名和密碼生成token,調用Subject.login 進行登錄驗證(調用shiro的Realm 類進行驗證)
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;

public class ShiroLogin {

    public static String login(UsernamePasswordToken token){
        // 從SecurityUtils裏邊創建一個 subject
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
        } catch (UnknownAccountException uae) {
            return "未知賬戶";
        } catch (IncorrectCredentialsException ice) {
            return "密碼不正確";
        } catch (LockedAccountException lae) {
            return "賬戶已鎖定";
        } catch (ExcessiveAttemptsException eae) {
            return "用戶名或密碼錯誤次數過多";
        } catch (AuthenticationException ae) {
            return "用戶名或密碼不正確!";
        }
        if (subject.isAuthenticated()) {
            return "登錄成功";
        } else {
            token.clear();
            return "登錄失敗";
        }
    }
}

 

  • Realm類包含兩個方法doGetAuthorizationInfo和doGetAuthenticationInfo
    • doGetAuthorizationInfo方法用來獲取用戶權限信息
    • doGetAuthenticationInfo方法是獲取用戶登錄憑證信息
      • 代碼如下:
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;


public class CustomRealm extends AuthorizingRealm {
    private static final transient Logger log = LoggerFactory.getLogger(CustomRealm.class);
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("------獲取用戶身份信息------");
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> stringSet = new HashSet<>();
        stringSet.add("user:show");
        stringSet.add("user:admin");
        info.setStringPermissions(stringSet);
        return info;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("-------身份認證方法--------");
        String userName = (String) authenticationToken.getPrincipal();
        String userPwd = new String((char[]) authenticationToken.getCredentials());
        //模擬 根據用戶名從數據庫獲取密碼
        String password = "2415b95d3203ac901e287b76fcef640b";
        if (userName == null) {
            throw new AccountException("用戶名不正確");
        } else if (!userPwd.equals(userPwd)) {
            throw new AccountException("密碼不正確");
        }
        //交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配
        String name = getName();
        System.out.println("name:"+name);
        return new SimpleAuthenticationInfo(userName, password,
                ByteSource.Util.bytes(userName + "salt"), getName());
    }

}

執行登錄操作:

 /**
     * 登錄操作
     * @param username
     * @param password
     * @return
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
        
        // 在認證提交前準備 token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 執行認證登陸
        return shiroLogin.login(token);
    }

 

測試:

1.輸入密碼錯誤

2.輸入密碼正確

 

歡迎留言一起討論

github地址:shiro

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章