網上找到一張認證的相關圖,如下

shiro第一個例子,參考quickstart的例子,相關文檔可以去shiro官網下載
首先創建一個maven的工程,pom.xml文件內容如下:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>PRovided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency> </dependencies> <build> <finalName>shiro</finalName> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <path>/${project.build.finalName}</path> </configuration> </plugin> </plugins> </build>java代碼程序:public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { //構造SecurityManager Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //獲取當前subject Subject currentUser = SecurityUtils.getSubject(); //獲取session Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String) session.getAttribute("someKey"); if (value.equals("aValue")) { log.info("Retrieved the correct value! [" + value + "]"); } //測試當前用戶是否已經登錄即執行認證 if (!currentUser.isAuthenticated()) { //用戶名、密碼封裝為UsernamePassWordToken UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); //記住我 token.setRememberMe(true); try { //登錄即執行認證 currentUser.login(token); } catch (UnknownAccountException uae) {//沒有此賬戶 log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) {//密碼錯誤 log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) {//賬戶被鎖定 log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } catch (AuthenticationException ae) { } } log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //判斷是否有某個角色 if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //判斷是否有某個權限 if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //退出登錄 currentUser.logout(); System.exit(0); }}總結:1、創建默認的securityManager,會默認注入iniRealm即表示從配置文件中獲取用戶相關信息。源碼為:
protected Realm createRealm(Ini ini) { //IniRealm realm = new IniRealm(ini); changed to support SHIRO-322 IniRealm realm = new IniRealm();//默認為inirealm realm.setName(INI_REALM_NAME); realm.setIni(ini); //added for SHIRO-322 return realm; }2、用戶通過currentUser.login(token)方法進行認證,內部調用securityManager.login(this, token),securityManager則是通過調用Authenticator的 public AuthenticationInfoauthenticate(AuthenticationToken authenticationToken) throws AuthenticationException;方法進行認證。而Authenticator是一個接口,其接口實現類有如下圖所示:

認證程序最終會去調用ModularRealmAuthenticator里的doAuthenticate(AuthenticationToken authenticationToken)方法【authenticationToken為用戶輸入的用戶名,密碼】。doAuthenticate方法源碼如下:
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);//單個realm } else { return doMultiRealmAuthentication(realms, authenticationToken);//多個realm } }之后在根據realm個數,進行不同方法的調用:
//單個realm protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) { if (!realm.supports(token)) { String msg = "Realm [" + realm + "] does not support authentication token [" + token + "]. Please ensure that the appropriate Realm implementation is " + "configured correctly or that the realm accepts AuthenticationTokens of this type."; throw new UnsupportedTokenException(msg); } AuthenticationInfo info = realm.getAuthenticationInfo(token); if (info == null) { String msg = "Realm [" + realm + "] was unable to find account data for the " + "submitted AuthenticationToken [" + token + "]."; throw new UnknownAccountException(msg); } return info; }//多個realmprotected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { AuthenticationStrategy strategy = getAuthenticationStrategy(); AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); if (log.isTraceEnabled()) { log.trace("Iterating through {} realms for PAM authentication", realms.size()); } for (Realm realm : realms) { aggregate = strategy.beforeAttempt(realm, token, aggregate); if (realm.supports(token)) { log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); AuthenticationInfo info = null; Throwable t = null; try { info = realm.getAuthenticationInfo(token); } catch (Throwable throwable) { t = throwable; if (log.isDebugEnabled()) { String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; log.debug(msg, t); } } aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); } else { log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } aggregate = strategy.afterAllAttempts(token, aggregate); return aggregate; }無論單個還是多個realm都會調用realm.getAuthenticationInfo(token),此時的realm默認為org.apache.shiro.realm.SimpleAccountRealm。代碼如下: public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info = getCachedAuthenticationInfo(token); if (info == null) { //otherwise not cached, perform the lookup: info = doGetAuthenticationInfo(token); log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info); if (token != null && info != null) { cacheAuthenticationInfoIfPossible(token, info); } } else { log.debug("Using cached authentication info [{}] to perform credentials matching.", info); } if (info != null) { assertCredentialsMatch(token, info);//默認認證方法為SimpleCredentialsMatcher } else { log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token); } return info; }
新聞熱點
疑難解答