30分鍾了解Shiro與Springboot的多Realm基礎配置


寫在前面的話:

我之前寫過兩篇與shiro安全框架有關的博文,居然能夠廣受歡迎實在令人意外。說明大家在互聯網時代大伙對於安全和登錄都非常重視,無論是大型項目還是中小型業務,普遍都至少需要登錄與認證的邏輯封裝。相較於SpringSecurity而言,Shrio更輕量無過多依賴和便於獨立部署的特點更收到開發者的歡迎。本篇博客只作為前兩篇對於shiro使用的基礎補充,我只談談如何在springboot項目中配置多角色驗證。

一、場景介紹

假設在我們的項目中需要有前台用戶登錄和后台系統管理員登錄兩種驗證方式。當然在傳統的業務邏輯中用戶和管理員可以使用不同的角色加以區分,假設現在的邏輯是用戶與系統管理員分別保存在不同的表中並且也分別對應了不同的角色(role)與權限(permission)。換句話說,從業務上看相同的用戶名和密碼如果是在前台頁面登錄可能對應的是普通用戶,從后台登錄則對應某板塊的系統管理。面對這樣的需求我們可以在shiro框架中配置多個realm,再配合上認證策略來執行。

二、代碼講解

與單一realm相同,首先根據不同的登錄認證要求創建不同的realm。這里只提供作為后台系統管理員的realm代碼實例:

// 系統管理員專用
public class AdministratorRealm extends AuthorizingRealm {
    @Autowired
    private AdministratorRegisterService administratorRegisterService;
    @Autowired
    private PasswordSupport passwordSupport;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String username = (String) principals.getPrimaryPrincipal();
        Administrator administrator = administratorRegisterService.getAdministratorByName(username);
        for (Role r : administrator.getRoles()) {
            authorizationInfo.addRole(r.getRoleName());
        }
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        Administrator administrator = administratorRegisterService.getAdministratorByName(username);
        if (administrator == null) {
            throw new UnknownAccountException();
        }
        if (administrator.isLocked()) {
            throw new LockedAccountException();
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(administrator.getUsername(),
                administrator.getPassword(), ByteSource.Util.bytes(passwordSupport.credentialsSalt(administrator)),
                getName());

        return authenticationInfo;
    }

}

doGetAuthenticationInfo方法負責用戶名和密碼驗證,doGetAuthorizationInfo方法負責角色和權限的分配, passwordSupport是一個自定義的類負責password加密和生成salt功能。

/**
 * 密碼輔助方法
 * 
 * @author learnhow
 *
 */
@Component
public class PasswordSupport {
    public static final String ALGORITHM_NAME = "md5";
    public static final int HASH_ITERATIONS = 2;/**
     * 針對系統管理生成salt和加密密碼
     * 
     * @param administrator
     */
    public void encryptPassword(Administrator administrator) {
        administrator.setSalt(new SecureRandomNumberGenerator().nextBytes().toHex());
        String newPassword = new SimpleHash(ALGORITHM_NAME, administrator.getPassword(),
                ByteSource.Util.bytes(credentialsSalt(administrator)), HASH_ITERATIONS).toHex();
        administrator.setPassword(newPassword);
    }/**
     * 對Administrator的salt生成規則
     * 
     * @param administrator
     * @return
     */
    public String credentialsSalt(Administrator administrator) {
        return administrator.getSalt() + administrator.getEmail();
    }
}

AdministratorRegisterService是服務組件負責通過name從數據庫中查詢。

在Shiro中無論是單一realm還是多個realm都需要對SecurityManager進行配置。

@Configuration
public class ShiroConfig {
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(PasswordSupport.ALGORITHM_NAME);
        hashedCredentialsMatcher.setHashIterations(PasswordSupport.HASH_ITERATIONS);
        return hashedCredentialsMatcher;
    }

    @Bean
    public AdministratorRealm getAdministatorRealm() {
        AdministratorRealm realm = new AdministratorRealm();
        realm.setName("AdministratorRealm");
        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        securityManager.setAuthenticator(modularRealmAuthenticator);

        List<Realm> realms = new ArrayList<>();
        // TODO-多個realms進配置在這里 
        realms.add(getAdministatorRealm());
        securityManager.setRealms(realms);
        return securityManager;
    }
}

ModularRealmAuthenticator的setAuthenticationStrategy方法中配置認證策略。Shiro提供了三種策略:AllSuccessFulStrategy, AtLeastOneSuccessFulAtrategy, FirstSuccessFulStrategy,默認使用AtLeastOneSuccessFulAtrategy,通常不需要特別配置。

三、注意事項

1.多realm認證只會拋出AuthenticationException,因此如果要想在外部判斷到底是在認證的哪一步發生的錯誤需要自己定義一些異常類型。

2.shiro沒有提供根據條件指定realm的功能,如果需要實現這樣的功能只能通過繼承與重寫來實現,這里沒有涉及需要深入探討的同學最好根據自己的實際情況專門研究。

 

寫在后面的話:

最近有不少朋友在看了我的博客以后加我的QQ或者發郵件要求提供演示源碼,為了方便交流我索性建了一個技術交流群,今后有些源碼我可能就放群資料里面了。當然之前的一些東西還在補充中,有些問題也希望大伙能共同交流。QQ群號:960652410


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM