这里使用官方提供的demo进行调试,进入源码分析。
官方demo地址:https://github.com/apache/shiro/tree/master/samples/quickstart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main (String[] args) { Factory<SecurityManager> factory = new IniSecurityManagerFactory ("classpath:shiro.ini" ); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); Subject currentUser = SecurityUtils.getSubject(); 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 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. 从Demo中的Subject.login方法开始
Subject接口实现类如下,这里demo不是Web环境,所以使用的实现类是DelegatingSubject:
2. SecurityManager.login() SecurityManager接口的实现类
3. Authenticator认证器 认证器的实现类,SecurityMananger也继承自Authenticator,通过查看AuthenticatingSecurityManager源码其实就是Authenticator的代理。而真正实现认证功能的Authenticator实现类只有一个ModularRealmAuthenticator,从类的名字可以看出这个认证器的实现原理——模块化认证器:一个Realm就是一个认证模块。
认证器源码实现:
模块化认证器:
单模块认证 官方提供的这个例子就是单模块认证(IniRealm,用户、角色、权限等信息保存在ini配置文件中)。
单模块认证很简单,它直接调用realm.getAuthenticationInfo方法。
多模块认证
4. 多模块认证策略AuthenticationStrategy Shiro提供了三种认证策略
| AuthenticationStrategy
类 | 描述 | | —————————————- | —————————————- | | AtLeastOneSuccessfulStrategy
| 如果一个(或多个)Realm认证成功,才被认为是成功的。如果没有任何一个验证成功,则认证失败。 | | FirstSuccessfulStrategy
| 只有从第一个成功验证的领域返回的信息将被使用,所有后面的Realm的认证信息将被忽略。如果没有任何一个验证成功,则认证失败。 | | AllSuccessfulStrategy
| 所有配置的Realm都必须认证成功,才能被认为是成功的。如果有任何一个认证不成功,则认证失败。 |
ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy 。
如果想修改认证策略可以在ini文件中配置:
1 2 3 4 5 6 7 [main] ... authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategysecurityManager.authenticator.authenticationStrategy = $authcStrategy ...
5. 认证模块Realm的实现 Shiro提供了如下的认证模块实现类,在官方的这个Demo中,由于使用的是Ini配置文件的方式,所以使用的Realm是IniRealm。
通常我们的用户权限信息存储在数据库中,需要我们继承AuthenticatingRealm,并重载它的doGetAuthenticationInfo
方法来从数据库中获取用户身份认证信息。但是大多数情况我们会继承AuthorizingRealm,因为它不仅仅包括认证,还包括授权过程,通过重载它的doGetAuthorizationInfo
方法实现授权。
授权过程 授权主要在调用Subject.hasRole或Subject.isPermitted等检查角色或权限的方法时触发。
官方也提供了一张图来描述授权的过程:
授权过程与身份认证的过程代码很类似(其实更简单),我就不在文章中具体贴图了,感兴趣的读者可以自行调试。
本作品采用 知识共享署名 4.0 国际许可协议 进行许可。
转载时请注明原文链接 :https://blog.hufeifei.cn/2018/01/Java/Shiro%E8%AE%A4%E8%AF%81%E4%B8%8E%E6%8E%88%E6%9D%83%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/