30分鐘瞭解Shiro與Springboot的多Realm基礎配置

寫在前面的話:spring

我以前寫過兩篇與shiro安全框架有關的博文,竟然可以廣受歡迎實在使人意外。說明你們在互聯網時代大夥對於安全和登陸都很是重視,不管是大型項目仍是中小型業務,廣泛都至少須要登陸與認證的邏輯封裝。相較於SpringSecurity而言,Shrio更輕量無過多依賴和便於獨立部署的特色更收到開發者的歡迎。本篇博客只做爲前兩篇對於shiro使用的基礎補充,我只談談如何在springboot項目中配置多角色驗證。數據庫

1、場景介紹安全

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

2、代碼講解框架

與單一realm相同,首先根據不一樣的登陸認證要求建立不一樣的realm。這裏只提供做爲後臺系統管理員的realm代碼實例:dom

// 系統管理員專用
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功能。ide

/**
 * 密碼輔助方法
 * 
 * @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進行配置。spa

@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,一般不須要特別配置。code

3、注意事項

1.多realm認證只會拋出AuthenticationException,所以若是要想在外部判斷究竟是在認證的哪一步發生的錯誤須要本身定義一些異常類型。

2.shiro沒有提供根據條件指定realm的功能,若是須要實現這樣的功能只能經過繼承與重寫來實現,這裏沒有涉及須要深刻探討的同窗最好根據本身的實際狀況專門研究。

 

寫在後面的話:

最近有很多朋友在看了個人博客之後加個人QQ或者發郵件要求提供演示源碼,爲了方便交流我索性建了一個技術交流羣,從此有些源碼我可能就放羣資料裏面了。固然以前的一些東西還在補充中,有些問題也但願大夥能共同交流。QQ羣號:960652410

相關文章
相關標籤/搜索