接下来的几天,咱们开讲Shiro,从入门到分析、集成、单点登陆整合等几篇。今天咱们先来认识一下Shiro吧~html
其实Shiro框架并不难,我梳理了一下,你只须要学会如下内容基本就足够了:git
接下来我会分为几篇文章分别去介绍,这篇咱们先来了解一下shiro的一些基础知识,以及登陆受权逻辑。github
在Web系统中咱们常常要涉及到权限问题,例如不一样角色的人登陆系统,他操做的功能、按钮、菜单是各不相同的,这就是所谓的权限。web
而构建一个互联网应用,权限校验管理是很重要的安全措施,这其中主要包含:面试
Shiro对以上功能都进行了很好的支持,它能够很是容易的开发出足够好的应用。Shiro能够帮助咱们完成:认证、受权、加密、会话管理、与Web集成、缓存等。并且Shiro的API也是很是简单。redis
官方源码:https://github.com/apache/shiroapache
从上图能够看出,Security Manager是Shiro的核心管理器,认证受权会话缓存等都是在其内部完成,而后会委托给具体的组件来处理,好比认证过程委托给Authenticator,受权委托给Authorizer组件。因此,整理仍是比较清晰,源代码也容易追踪。缓存
咱们来具体聊聊全部的组件:安全
Subject:主体,能够看到主体能够是任何能够与应用交互的“用户”;session
SecurityManager:Shiro的心脏;全部具体的交互都经过SecurityManager进行控制;负责全部Subject、且负责进行认证和受权、及会话、缓存的管理。
Realm:能够有1个或多个Realm,主要提供认证和受权的数据;
Session:Shiro提供一个权限的企业级Session解决方案,session的生命周期都在SessionManager中进行管理。
SessionManager:shiro的会话管理器;
SessionDAO:用于会话的CRUD,好比存储到ehcache或者redis中的会话增删改查;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;由于这些数据基本上不多去改变,放到缓存中后能够提升访问的性能
Cryptography:密码模块,Shiro提升了一些常见的加密组件用于如密码加密/解密的。
官网例子:http://shiro.apache.org/tutorial.html
刚入门Shiro的同窗,真的须要去看看这个官方例子,你能够更加深刻了解Shiro的权限校验流程。我仍是贴一下代码吧,一些同窗比较懒:
# ----------------------------------------------------------------------------- # Users and their (optional) assigned roles # username = password, role1, role2, ..., roleN # ----------------------------------------------------------------------------- [users] root = secret, admin guest = guest, guest presidentskroob = 12345, president darkhelmet = ludicrousspeed, darklord, schwartz lonestarr = vespa, goodguy, schwartz # ----------------------------------------------------------------------------- # Roles with assigned permissions # roleName = perm1, perm2, ..., permN # ----------------------------------------------------------------------------- [roles] admin = * schwartz = lightsaber:* goodguy = winnebago:drive:eagle5
上面代码中,root = secret, admin
表示,用户名root,密码secret,角色是admin;schwartz = lightsaber:*
表示角色schwartz拥有权限lightsaber:*。你其实能够把这个文件当作一个Realm,其实就是shiro默认的IniRealm。
public class Tutorial { private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class); public static void main(String[] args) { log.info("My First Apache Shiro Application"); Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); // get the currently executing user: Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) 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 + "]"); } // let's login the current user so we can check against roles and permissions: 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 more exceptions here (maybe custom ones specific to your application? catch (AuthenticationException ae) { //unexpected condition? error? } } //say who they are: //print their identifying principal (in this case, a username): log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //test a role: if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //test a typed permission (not instance-level) 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."); } //a (very powerful) Instance Level permission: 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!"); } //all done - log out! currentUser.logout(); System.exit(0); } }
从上面的实例中,咱们能够总结一下经常使用的API:
#获取当前用户 Subject currentUser = SecurityUtils.getSubject(); #判断用户已经认证 currentUser.isAuthenticated() #用户登陆凭证 UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); #记住我 token.setRememberMe(true); #登录校验 currentUser.login(token); #判断是否有角色权限 currentUser.hasRole("schwartz") #判断是否有资源操做权限 currentUser.isPermitted("lightsaber:wield") #登出 currentUser.logout();
其实稍微梳理一下,能够发现上面代码主要有两个步骤:
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); currentUser.login(token); currentUser.logout();
currentUser.hasRole("schwartz") currentUser.isPermitted("winnebago:drive:eagle5")
接下来,咱们去探讨一下shiro的认证与受权流程,并从源码层去解析一下shiro各个组件之间的关系。
上面图片中,根据序号,其实咱们大概能猜出里shiro的认证流程:
咱们从login方法开始debug一下流程,用简要方式追踪shiro源码的认证逻辑:
currentUser.login(token); | Subject subject = this.securityManager.login(this, token); | AuthenticationInfo info = this.authenticate(token); | this.authenticator.authenticate(token); | AuthenticationInfo info = this.doAuthenticate(token); | Collection<Realm> realms = this.getRealms(); doSingleRealmAuthentication(realm, token); | AuthenticationInfo info = realm.getAuthenticationInfo(token); | AuthenticationInfo info = realm.doGetAuthenticationInfo(token);
ok,一条线下来,从login到委托给authenticator,再最后调用realm的doGetAuthenticationInfo方法。
因此,从源码上来看,若是要实现shiro的认证逻辑,至少要准备一个Realm组件、和初始化securityManager组件。
从上图中,咱们能够知道受权流程以下:
追踪一下源码以下:
currentUser.hasRole("schwartz") | this.securityManager.hasRole(this.getPrincipals(), roleIdentifier) | this.authorizer.hasRole(principals, roleIdentifier) | AuthorizationInfo info = this.getAuthorizationInfo(principal); return info.getRoles().contains(roleIdentifier) | info = this.doGetAuthorizationInfo(principals);(realm)
因此shiro判断用户是否有权限首先会从realm中获取用户所拥有的权限角色信息,而后再匹配当前的角色或权限是否包含,从而断定用户是否有权限!
说到权限,不少人天然会想起权限系统,涉及到几个关键对象:
经过这几个要素,能够设计出比较合理的权限系统。
Subject subject = SecurityUtils.getSubject(); if(subject.hasRole(“admin”)) { //有权限 } else { //无权限 }
@RequiresRoles("admin") public void hello() { //有权限 }
<shiro:hasRole name="admin"> <!— 有权限 —> </shiro:hasRole>
jsp页面引入shiro标签
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
@Autowired private SessionDAO sessionDAO; //获取会话数量 int size = sessionDAO.getActiveSessions().size()
//强制退出 Session session = sessionDAO.readSession(subject.getSession().getId()); sessionDAO.delete(session); // logout,可做为强制退出 subject.logout(); Assert.isTrue(!subject.isAuthenticated());
ok,感受是高度极简的一篇文章,主要把重要的组件和登陆、受权几个流程搞清楚以后,其实shiro基本已经学会了,后面咱们再学一下shiro的几个主要内置过滤器怎么使用,如何集成SpringBoot,基本就差很少了。
推荐阅读:
分享一套SpringBoot开发博客系统源码,以及完整开发文档!速度保存!