Springboot Shiro簡單案例(認證,授權,自定義Realm、加鹽加密)

網上有很多講Shiro的概念的博客,大家自行補腦,我這邊就講一個案例:Shiro的認證,授權,自定義Realm。

Apache Shiro與Spring Security區別簡述

總結了一句話:Shiro 比 Security 配置簡單很多,安全性上雖然沒有 Security強大,但對於一般的項目已經足夠了。

Shiro 的工作流程

百度找了一個比較權威的AV畫質的架構圖,你肯定也見過這個圖,被嚇到了的話我帶着你讀懂這個圖,請問作者你話是不是多,爲什麼講案例要這個圖?這是Shiro的架構,代碼也是靠這個實現的。

第一排的編程語言(Shiro 爲了突出他們支持很多語言)的方框上都有一個 Subject (主體),主體相當於‘用戶對象’與 中間的那層 Security Manager 打交道,Security Manager是 Shiro 的核心。這個裏面有很多的組件:

1、Authenticator(認證器):管理登入登出;

2、Authorizer(授權器):給主體授權;

3、Session Manager(Session 管理器):Shiro自己實現了一套Session機制,可以藉助於任何外部容器的情況下使用Session;

4、Session DAO:提供了對Session的一些操作如crud;

5、Cache Manager(緩存管理器):主要用戶緩存角色數據和權限數據等;

6、Realm:與數據庫(如MySql)打交道,Realm主要用於獲取數據庫的數據表裏邊的角色信息(roles),權限信息(permissions)等。

還有最右邊的Cryptographic ,字面意思加密用途。

Shiro的認證,授權,自定義Realm編碼實現

代碼是按照這個流程實現的。

POM

 <!-- Shiro 相關 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

CustomRealm.java

public class CustomRealm extends AuthorizingRealm {
    private Map<String,String > userMap = new HashMap<>();

    {
        userMap.put("admin","123456");
        super.setName("CustomRealm");
    }


    /**
     * 登錄身份驗證
     * @param authenticationToken 賬號密碼生成的token
     * @return 驗證結果
     * @throws AuthenticationException 驗證失敗
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        // 1.獲取主體中傳過來的認證信息中的用戶名
        String username = authenticationToken.getPrincipal().toString();

        // 2.通過用戶名到數據庫中的憑證
        String pwd = getPasswordByUsername(username);
        if (pwd == null){
            return null;
        }
        SimpleAuthenticationInfo AuthenticationInfo = new
                SimpleAuthenticationInfo("admin","123456","CustomRealm");
        return AuthenticationInfo;
    }
    
    /**
     * 權限驗證
     * @param principalCollection principalCollection
     * @return 驗證結果
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 1、從認證信息中獲取用戶
        String username = (String) principalCollection.getPrimaryPrincipal();
        Set<String> roles = getRolesByUsername(username);
        Set<String> permissions = getPermissionsByUsername(username);
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);

        return authorizationInfo;
    }
    
/**
 * 下面三個是模擬從數據中獲取數據的
 */
    private Set<String> getPermissionsByUsername(String username) {
        Set<String> permissionSet = new HashSet<>();
        permissionSet.add("user:delete");
        permissionSet.add("user:update");
        return permissionSet;
    }

    private String getPasswordByUsername(String username) {
        return userMap.get(username);
    }

    private Set<String> getRolesByUsername(String username) {
        Set<String> roleSet = new HashSet<>();
        roleSet.add("admin");
        roleSet.add("user");
        return roleSet;
    }
}

CustomRealmTest.java

public class CustomRealmTest {

    public static void main(String[] args) {

        CustomRealm customRealm = new CustomRealm();
        // 1、構建SecurityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(customRealm);

        //2、主體提交認證
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
        subject.login(token);
        System.out.println("=====\n"+token+"\n======");
        System.out.println("isAuthenticated: "+subject.isAuthenticated());

        subject.checkRole("admin");
        subject.checkPermission("user:delete");
        subject.checkPermission("user:update");

        subject.checkPermission("user:add");
    }

}

另外在送一個知識點,加鹽加密

在上面的 CustomRealmTest.java 構建完了SecurityManager的代碼之後加入以下代碼,就相當於是客戶端加密之後上傳加密後的密碼到服務器:

// 加鹽加密
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");
        matcher.setHashIterations(1);
        customRealm.setCredentialsMatcher(matcher);

服務器端加密在Realm 把比對的“鹽”設置一下即可:

在 AuthenticationInfo 裏的 SimpleAuthenticationInfo 下面

AuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("hello"));

記得把模擬數據也換成密文。

 

 

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