• 1. Shiro使用简介
  • 2. 核心组件 Subject SecurityManager Realms
  • 3. 核心组件Subject:即“当前操作用户” SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务 Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息
  • 4. Shiro完整架构图
  • 5. 其他主要组件Authenticator :认证 ,核实用户身份 Authorizer :授权 ,访问控制 SessionManager : CacheManager :对Shiro的其他组件提供缓存支持
  • 6. Shiro认证过程
  • 7. Shiro认证过程 1、应用程序构建了一个终端用户认证信息的AuthenticationToken 实例后,调用Subject.login方法。 2、Sbuject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用securityManager.login(token)方法。 3、SecurityManager接受到token(令牌)信息后会委托内置的Authenticator的实例(通常都是ModularRealmAuthenticator类的实例)调用authenticator.authenticate(token). ModularRealmAuthenticator在认证过程中会对设置的一个或多个Realm实例进行适配,它实际上为Shiro提供了一个可拔插的认证机制。
  • 8. Shiro认证过程 4、如果在应用程序中配置了多个Realm,ModularRealmAuthenticator会根据配置的AuthenticationStrategy(认证策略)来进行多Realm的认证过程。在Realm被调用后,AuthenticationStrategy将对每一个Realm的结果作出响应。 注:如果应用程序中仅配置了一个Realm,Realm将被直接调用而无需再配置认证策略。 5、判断每一个Realm是否支持提交的token,如果支持,Realm将调用getAuthenticationInfo(token); getAuthenticationInfo 方法就是实际认证处理,我们通过覆盖Realm的doGetAuthenticationInfo方法来编写我们自定义的认证处理。
  • 9. 认证代码Subject myadmin=SecurityUtils.getSubject(); if(!myadmin.isAuthenticated()){ UsernamePasswordToken token = new UsernamePasswordToken(account, password); token.setRememberMe(true); try { myadmin.login(token); } catch ( UnknownAccountException uae ) { message="用户名不正确"; return ERROR; }
  • 10. 认证代码catch ( IncorrectCredentialsException ice ) { message="密码错误"; return ERROR; } catch ( LockedAccountException lae ) { message="用户被锁"; return ERROR; } catch ( ExcessiveAttemptsException eae ) { message="用户名或密码错误"; return ERROR; } catch ( AuthenticationException ae ) { message="用户名或密码错误"; return ERROR; } } currentUser.logout(); //退出代码
  • 11. Shiro授权 授权有着三个核心元素:权限、角色和用户。
  • 12. 权限Shiro权限声明通常是使用以冒号分隔的表达式。 下面以实例来说明权限表达式。 可查询用户数据 User:view 可查询或编辑用户数据 User:view,edit 可对用户数据进行所有操作 User:* 或 User 可编辑id为123的用户数据 User:edit:123
  • 13. 角色 Shiro支持两种角色模式: 1、传统角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。 2、权限角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。
  • 14. 授权实现 Shiro支持三种方式实现授权过程: 1编码实现 2注解实现 3JSP Taglig实现
  • 15. Shiro授权的内部处理机制
  • 16. 授权流程1、在应用程序中调用授权验证方法(Subject的isPermitted*或hasRole*等) 2、Sbuject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用相应的isPermitted*或hasRole*方法。 3、接下来SecurityManager会委托内置的Authorizer的实例(默认是ModularRealmAuthorizer 类的实例,类似认证实例,它同样支持一个或多个Realm实例认证)调用相应的授权方法。 4、每一个Realm将检查是否实现了相同的 Authorizer 接口。然后,将调用Reaml自己的相应的授权验证方法。 。
  • 17. 授权流程当使用多个Realm时,不同于认证策略处理方式,授权处理过程中: 1、当调用Realm出现异常时,将立即抛出异常,结束授权验证。 2、只要有一个Realm验证成功,那么将认为授权成功,立即返回,结束认证 Shiro有3中认证策略的具体实现: AtLeastOneSuccessfulStrategy只要有一个(或更多)的Realm验证成功,那么认证将被视为成功FirstSuccessfulStrategy第一个Realm验证成功,整体认证将被视为成功,且后续Realm将被忽略AllSuccessfulStrategy所有Realm成功,认证才视为成功
  • 18. 基于传统角色授权实现 Subject currentUser = SecurityUtils.getSubject(); if (currentUser.hasRole("administrator")) { } else { }
  • 19. 相关验证方法 1.hasRole(String roleName)当用户拥有指定角色时,返回true 2.hasRoles(List roleNames)按照列表顺序返回相应的一个boolean值数组 3.hasAllRoles(Collection roleNames)如果用户拥有所有指定角色时,返回true
  • 20. 断言支持 Shiro还支持以断言的方式进行授权验证。断言成功,不返回任何值,程序继续执行;断言失败时,将抛出异常信息。使用断言,可以使我们的代码更加简洁 。 Subject currentUser = SecurityUtils.getSubject(); currentUser.checkRole("bankTeller"); openBankAccount();
  • 21. 断言的相关方法 Subject方法 描述 checkRole(String roleName) 断言用户是否拥有指定角色 checkRoles(Collection roleNames) 断言用户是否拥有所有指定角色 checkRoles(String... roleNames) 对上一方法的方法重载
  • 22. 基于权限角色授权实现 相比传统角色模式,基于权限的角色模式耦合性要更低些,它不会因角色的改变而对源代码进行修改,因此,基于权限的角色模式是更好的访问控制方式。 它的代码实现有以下几种实现方式:
  • 23. 基于权限对象的实现 Permission printPermission = new PrinterPermission("laserjet4400n", "print"); Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isPermitted(printPermission)) { //show the Print button } else { //don't show the button? Grey it out? } Permission printPermission = new PrinterPermission("laserjet4400n", "print"); Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isPermitted(printPermission)) { //show the Print button } else { //don't show the button? Grey it out? }
  • 24. 相关方法1.isPermitted(Permission p)Subject拥有制定权限时,返回treu 2.isPermitted(List perms)返回对应权限的boolean数组 3.isPermittedAll(Collection perms)Subject拥有所有制定权限时,返回true
  • 25. 基于字符串的实现 Subject currentUser = SecurityUtils.getSubject(); if(currentUser.isPermitted("printer:print:laserjet4400n")) { //show the Print button } else { //don't show the button? Grey it out? }
  • 26. 断言实现 Subject currentUser = SecurityUtils.getSubject(); Permission p = new AccountPermission("open"); currentUser.checkPermission(p); openBankAccount(); Subject currentUser = SecurityUtils.getSubject(); currentUser.checkPermission("account:open"); openBankAccount();
  • 27. 断言实现的相关方法 1.checkPermission(Permission p)断言用户是否拥有制定权限 2.checkPermission(String perm)断言用户是否拥有制定权限 3.checkPermissions(Collection perms)断言用户是否拥有所有指定权限 4.checkPermissions(String... perms)断言用户是否拥有所有指定权限
  • 28. 基于注解的授权实现 @ RequiresAuthentication @ RequiresGuest @RequiresPermissions("account:create") @RequiresRoles @ RequiresUser
  • 29. 基于JSP TAG的授权实现 <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> Hello, , how are you today?
  • 30. .
  • 31. Realm实现在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.
  • 32. 认证实现 Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。 该方法主要执行以下操作: 1、检查提交的进行认证的令牌信息 2、根据令牌信息从数据源(通常为数据库)中获取用户信息 3、对用户信息进行匹配验证。 4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。 5、验证失败则抛出AuthenticationException异常信息。
  • 33. Realm代码@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; Admin user =(Admin) adminDao.query().is("account", token.getUsername()).result(); if (user != null) { return new SimpleAuthenticationInfo(user.getAccount(), user.getPassword(), getName()); } else { return null; } }
  • 34. 授权实现 而授权实现则与认证实现非常相似,在我们自定义的Realm中,重载doGetAuthorizationInfo()方法,重写获取用户权限的方法即可。
  • 35. Realm代码@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String name=getName(); Iterator iterator=principals.fromRealm(name).iterator(); String userName=null; if(iterator.hasNext()){ userName = (String) iterator.next(); } Admin user =(Admin) adminDao.findOne("account", userName); if(user.getRoleSet()!=null){ BuguMapper.fetchCascade(user, "roleSet"); } if (user != null&&user.getRoleSet()!=null) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); for (Role group : user.getRoleSet()) { info.addRole(group.getName()); if(group.getPerssionSet()!=null){ BuguMapper.fetchCascade(group, "perssionSet"); Iterator it=group.getPerssionSet().iterator(); while(it.hasNext()){ info.addStringPermission(((Permission)it.next()).getName()); } } } return info; } else { return null; }
  • 36. Shiro配置Apache Shiro的配置主要分为四部分: 对象和属性的定义与配置 URL的过滤器配置 静态用户配置 静态角色配置 其中,由于用户、角色一般由后台进行操作的动态数据,因此Shiro配置一般仅包含前两项的配置。 Apache Shiro的大多数组件是基于POJO的,因此我们可以使用POJO兼容的任何配置机制进行配置,例如:Java代码、Sping XML、YAML、JSON、ini文件等等。
  • 37. 基于spring的配置
  • 38. 基于spring的配置 /admins/news/indexRebuild**=authc,rolesAny["系统常规管理,超级管理员"] /admins/product/indexRebuild**=authc,rolesAny["系统常规管理,超级管理员"] /admins/member/indexRebuild**=authc,rolesAny["系统常规管理,超级管理员"]
  • 39. 基于ini文件的配置[main] shiro.loginUrl=/login/admin_login.jsp authc.successUrl=/admins/ roles.unauthorizedUrl=/shows/error/no_permission.jsp perms.unauthorizedUrl=/shows/error/no_permission.jsp inirealm=org.apache.shiro.realm.text.IniRealm inirealm.resourcePath=classpath:shiro.ini myrealm=cn.eyes.commons.utils.MongodbRealm adminDao=cn.eyes.core.admin.AdminDao myrealm.adminDao=$adminDao securityManager.realms =$inirealm, $myrealm sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager securityManager.sessionManager=$sessionManager rolesAny=cn.eyes.commons.context.RolesAnyAuthorizationFilter securityManager.sessionManager.sessionListeners=$rolesAny [users] admin =1234567, *
  • 40. 基于ini文件的配置 [roles] admin = * [urls] /admins/news/indexRebuild**=authc,perms[indexrebuild:*] /admins/news/**.jsp=authc,roles[资讯管理],roles[超级管理员] /admins/news/**=authc,roles[资讯管理],roles[超级管理员] /admins/survey/**=authc,rolesAny["资讯管理,超级管理员"] /admins/survey/**.jsp=authc,roles[资讯管理],roles[超级管理员] /admins/**=authc
  • 41. URL过滤器配置说明URL_Ant_Path_Expression = Path_Specific_Filter_Chain [urls] /index.html = anon /user/create = anon /user/** = authc /admin/** = authc, roles[administrator] /rest/** = authc, rest /remoting/rpc/** = authc, perms["remote:invoke"]
  • 42. URL表达式说明 1、URL目录是基于HttpServletRequest.getContextPath()此目录设置 2、URL可使用通配符,**代表任意子目录 3、Shiro验证URL时,URL匹配成功便不再继续匹配查找。所以要注意配置文件中的URL顺序,尤其在使用通配符时。
  • 43. Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过 3、部分过滤器可指定参数,如perms,roles
  • 44. Shiro内置的FilterChain anon org.apache.shiro.web.filter.authc.AnonymousFilter authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter port org.apache.shiro.web.filter.authz.PortFilter rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter ssl org.apache.shiro.web.filter.authz.SslFilter user org.apache.shiro.web.filter.authc.UserFilter
  • 45. 内置过滤器详解rest: 例如/admins/user/**=authc,rest[user] 根据请求的方法,想当与/admins/user/**=authc,perms[user:method] port:例如/admins/user/**=authc,prot[8081] 当请求的rul端口不是8081时,跳转到 http://serverName:8081?queryString perms:例如/admins/user/**=authc,perms[“user:add:*”]
  • 46. 内置过滤器详解prems参数可以写多个,当写多个时要加上双引号,并且参数之间用逗号分割 roles例如/admins/user/**=authc,roles[admin] 参数可以写多个,当写多个时要加上双引号,并且参数之间用逗号分割 anon例如/admins/**=anon 没有参数,表示可以匿名使用
  • 47. 内置过滤器详解authc:例如/admins/user/**=authc 表示需要认证才能使用,没有参数 authcBasic:例如/admins/user/**= authcBasic没有参数,表示httpBasic认证,没有参数 ssl:例如/admins/user/**=ssl 没有参数,表示安全url请求,https user:例如/admins/user/**=user 没有参数,表示存在用户,当登入操作时不做检查
  • 48. 会话管理可在任何应用或架构层一致地使用Session API.那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。
  • 49. 代码Session session = subject.getSession(); Session session = subject.getSession(boolean create); session.setAttribute("key", someValue); session.getAttribute(“key”) Date timestamp = session.getLastAccessTime(); session.setTimeout(millis);
  • 50. 加密
  • 51. cache
  • 52. 自定义filterpublic class RolesAnyAuthorizationFilter extends AuthorizationFilter { public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { Subject subject = getSubject(request, response); String[] rolesArray = (String[]) mappedValue; if (rolesArray == null || rolesArray.length == 0) { return true; } for(int i=0;i