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; @Overrideprotected 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