当前位置: 首页 > news >正文

福州 哈尔滨网站建设 网络服务海外代理服务器 免费

福州 哈尔滨网站建设 网络服务,海外代理服务器 免费,乐清新闻综合频道,网站建设服务581.Shiro简介 1.1.基本功能点 Shiro 可以非常容易的开发出足够好的应用#xff0c;其不仅可以用在 JavaSE 环境#xff0c;也可以用在 JavaEE 环境。Shiro 可以帮助我们完成#xff1a;认证、授权、加密、会话管理、与 Web 集成、缓存等。 Authentication#xff1a;身份…1.Shiro简介 1.1.基本功能点 Shiro 可以非常容易的开发出足够好的应用其不仅可以用在 JavaSE 环境也可以用在 JavaEE 环境。Shiro 可以帮助我们完成认证、授权、加密、会话管理、与 Web 集成、缓存等。 Authentication身份认证 / 登录验证用户是不是拥有相应的身份Authorization授权即权限验证验证某个已认证的用户是否拥有某个权限即判断用户是否能做事情常见的如验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限Session Manager会话管理即用户登录后就是一次会话在没有退出之前它的所有信息都在会话中会话可以是普通 JavaSE 环境的也可以是如 Web 环境的Cryptography加密保护数据的安全性如密码加密存储到数据库而不是明文存储Web SupportWeb 支持可以非常容易的集成到 Web 环境Caching缓存比如用户登录后其用户信息、拥有的角色 / 权限不必每次去查这样可以提高效率Concurrencyshiro 支持多线程应用的并发验证即如在一个线程中开启另一个线程能把权限自动传播过去Testing提供测试支持Run As允许一个用户假装为另一个用户如果他们允许的身份进行访问Remember Me记住我这个是非常常见的功能即一次登录后下次再来的话不用登录了。 记住一点Shiro 不会去维护用户、维护权限这些需要我们自己去设计 / 提供然后通过相应的接口注入给 Shiro 即可。 1.2.Shiro的架构 1.2.1.外部 我们从外部来看 Shiro 即从应用程序角度的来观察如何使用 Shiro 完成工作。 应用代码直接交互的对象是 Subject也就是说 Shiro 的对外 API 核心就是 Subject其每个 API 的含义 Subject 主体代表了当前 “用户”这个用户不一定是一个具体的人与当前应用交互的任何东西都Subject如网络爬虫机器人等即一个抽象概念所有 Subject 都绑定到 SecurityManager与 Subject 的所有交互都会委托给 SecurityManager可以把 Subject 认为是一个门面SecurityManager 才是实际的执行者 SecurityManager 安全管理器即所有与安全有关的操作都会与 SecurityManager 交互且它管理着所有 Subject可以看出它是 Shiro 的核心它负责与后边介绍的其他组件进行交互如果学习过 SpringMVC你可以把它看成 DispatcherServlet 前端控制器 Realm 域Shiro 从 Realm 获取安全数据如用户、角色、权限就是说 SecurityManager 要验证用户身份那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作可以把 Realm 看成 DataSource即安全数据源。 也就是说对于我们而言最简单的一个 Shiro 应用 应用代码通过 Subject 来进行认证和授权而 Subject 又委托给 SecurityManager我们需要给 Shiro 的 SecurityManager 注入 Realm从而让 SecurityManager 能得到合法的用户及其权限进行判断。 从以上也可以看出Shiro 不提供维护用户 / 权限而是通过 Realm 让开发人员自己注入。 1.2.2.内部 接下来我们来从 Shiro 内部来看下 Shiro 的架构 Subject 主体可以看到主体可以是任何可以与应用交互的 “用户” SecurityManager 相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher是 Shiro 的心脏所有具体的交互都通过 SecurityManager 进行控制它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。 Authenticator 认证器负责主体认证的这是一个扩展点如果用户觉得 Shiro 默认的不好可以自定义实现其需要认证策略Authentication Strategy即什么情况下算用户认证通过了 Authrizer 授权器或者访问控制器用来决定主体是否有权限进行相应的操作即控制着用户能访问应用中的哪些功能 Realm 可以有 1 个或多个 Realm可以认为是安全实体数据源即用于获取安全实体的可以是 JDBC 实现也可以是 LDAP 实现或者内存实现等等由用户提供注意Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储所以我们一般在应用中都需要实现自己的 Realm SessionManager 如果写过 Servlet 就应该知道 Session 的概念Session 呢需要有人去管理它的生命周期这个组件就是 SessionManager而 Shiro 并不仅仅可以用在 Web 环境也可以用在如普通的 JavaSE 环境、EJB 等环境所有呢Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据这样的话比如我们在 Web 环境用刚开始是一台 Web 服务器接着又上了台 EJB 服务器这时想把两台服务器的会话数据放到一个地方这个时候就可以实现自己的分布式会话如把数据放到 Memcached 服务器 SessionDAO DAO 大家都用过数据访问对象用于会话的 CRUD比如我们想把 Session 保存到数据库那么可以实现自己的 SessionDAO通过如 JDBC 写到数据库比如想把 Session 放到 Memcached 中可以实现自己的 Memcached SessionDAO另外 SessionDAO 中可以使用 Cache 进行缓存以提高性能 CacheManager 缓存控制器来管理如用户、角色、权限等的缓存的因为这些数据基本上很少去改变放到缓存中后可以提高访问的性能 Cryptography 密码模块Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。 2.shiro组件 2.1.身份验证 身份验证 即在应用中谁能证明他就是他本人。一般提供如他们的身份 ID 一些标识信息来表明他就是他本人如提供身份证用户名 / 密码来证明。 在 shiro 中用户需要提供 principals 身份和 credentials证明给 shiro从而应用能验证用户身份 principals 身份即主体的标识属性可以是任何东西如用户名、邮箱等唯一即可。一个主体可以有多个 principals但只有一个 Primary principals一般是用户名 / 密码 / 手机号。 credentials 证明 / 凭证即只有主体知道的安全值如密码 / 数字证书等。 最常见的 principals 和 credentials 组合就是用户名 / 密码了。接下来先进行一个基本的身份认证。 另外两个相关的概念是之前提到的 Subject 及 Realm分别是主体及验证主体的数据源。 2.1.1.maven依赖配置 propertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingshiro.version1.2.2/shiro.version /propertiesdependenciesdependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.12/versionscopetest/scope/dependency!-- log4j --dependencygroupIdorg.slf4j/groupIdartifactIdslf4j-log4j12/artifactIdversion1.7.2/version/dependency!-- shiro相关依赖 --dependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-web/artifactIdversion${shiro.version}/version/dependencydependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-ehcache/artifactIdversion${shiro.version}/version/dependencydependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring/artifactIdversion${shiro.version}/version/dependencydependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-quartz/artifactIdversion${shiro.version}/version/dependencydependencygroupIdcommons-logging/groupIdartifactIdcommons-logging/artifactIdversion1.1.3/version/dependency /dependencies2.1.2.登录/退出 1、准备一些用户身份 [users] zhang123 wang123此处使用 ini 配置文件通过 [users] 指定了两个主体zhang/123、wang/123。 Testpublic void testLoginLoginout(){//1、获取SecurityManager工厂此处使用Ini配置文件初始化SecurityManagerFactoryorg.apache.shiro.mgt.SecurityManager factory new IniSecurityManagerFactory(classpath:shiro.ini);//2、得到SecurityManager实例 并绑定给SecurityUtilsorg.apache.shiro.mgt.SecurityManager securityManager factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//3、得到Subject及创建用户名/密码身份验证Token即用户身份/凭证Subject subject SecurityUtils.getSubject();UsernamePasswordToken token new UsernamePasswordToken(zhang, 123);try {//4、登录即身份验证subject.login(token);} catch (AuthenticationException e) {//5、身份验证失败}System.out.println(subject.isAuthenticated());//6、退出subject.logout();}首先通过 new IniSecurityManagerFactory 并指定一个 ini 配置文件来创建一个 SecurityManager 工厂接着获取 SecurityManager 并绑定到 SecurityUtils这是一个全局设置设置一次即可通过 SecurityUtils 得到 Subject其会自动绑定到当前线程如果在 web 环境在请求结束时需要解除绑定然后获取身份验证的 Token如用户名 / 密码调用 subject.login 方法进行登录其会自动委托给 SecurityManager.login 方法进行登录如果身份验证失败请捕获 AuthenticationException 或其子类常见的如 DisabledAccountException禁用的帐号、LockedAccountException锁定的帐号、UnknownAccountException错误的帐号、ExcessiveAttemptsException登录失败次数过多、IncorrectCredentialsException 错误的凭证、ExpiredCredentialsException过期的凭证等具体请查看其继承关系对于页面的错误消息展示最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”防止一些恶意用户非法扫描帐号库最后可以调用 subject.logout 退出其会自动委托给 SecurityManager.logout 方法退出。 从如上代码可总结出身份验证的步骤 收集用户身份 / 凭证即如用户名 / 密码调用 Subject.login 进行登录如果失败将得到相应的 AuthenticationException 异常根据异常提示用户错误信息否则登录成功最后调用 Subject.logout 进行退出操作。 2.1.3.身份认证流程 流程如下 首先调用 Subject.login(token) 进行登录其会自动委托给 Security Manager调用之前必须通过 SecurityUtils.setSecurityManager() 设置SecurityManager 负责真正的身份验证逻辑它会委托给 Authenticator 进行身份验证Authenticator 才是真正的身份验证者Shiro API 中核心的身份认证入口点此处可以自定义插入自己的实现Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证Authenticator 会把相应的 token 传入 Realm从 Realm 获取身份验证信息如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm将按照相应的顺序及策略进行访问。 2.1.4.Realm Realm域Shiro 从从 Realm 获取安全数据如用户、角色、权限就是说 SecurityManager 要验证用户身份那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作可以把 Realm 看成 DataSource即安全数据源。如我们之前的 ini 配置方式将使用 org.apache.shiro.realm.text.IniRealm。 2.1.4.1.单 Realm 配置 1、自定义 Realm 实现需要实现四个方法 public class MyRealm extends AuthorizingRealm {/*** 获取角色与权限*doGetAuthorizationInfo执行时机有三个如下* 1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”)自己去调用这个是否有什么角色或者是否有什么权限的时候* 2、RequiresRoles(admin) 在方法上加注解的时候* 3、shiro.hasPermission name admin/shiro.hasPermissiondustin:test在页面上加shiro标签的时候即进这个页面的时候扫描到有这个标签的时候。* param principals* return*/Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return null;}/*** 登录信息验证* 1.doGetAuthenticationInfo执行时机如下* 当调用Subject currentUser SecurityUtils.getSubject();* currentUser.login(token);* param token* return* throws AuthenticationException*/Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username (String)token.getPrincipal();String password new String((char[])token.getCredentials());if(!zhang.equals(username)) {throw new UnknownAccountException(); //如果用户名错误}if(!123.equals(password)) {throw new IncorrectCredentialsException(); //如果密码错误}return new SimpleAuthenticationInfo(username, password, getName());}Overridepublic void clearCachedAuthorizationInfo(PrincipalCollection principals) {super.clearCachedAuthorizationInfo(principals);}Overridepublic void clearCachedAuthenticationInfo(PrincipalCollection principals) {super.clearCachedAuthenticationInfo(principals);}Overridepublic void clearCache(PrincipalCollection principals) {super.clearCache(principals);}public void clearAllCachedAuthorizationInfo() {getAuthorizationCache().clear();}public void clearAllCachedAuthenticationInfo() {getAuthenticationCache().clear();}public void clearAllCache() {clearAllCachedAuthenticationInfo();clearAllCachedAuthorizationInfo();} }2、ini 配置文件指定自定义 Realm 实现 (shiro-realm.ini) #声明一个realm myRealmnet.wanho.realm.MyRealm #指定securityManager的realms实现 securityManager.realms$myRealm3、测试 只需要把之前的 shiro.ini 配置文件改成 shiro-realm.ini 即可。 Testpublic void testLoginLoginout(){//1、获取SecurityManager工厂此处使用Ini配置文件初始化SecurityManagerFactoryorg.apache.shiro.mgt.SecurityManager factory new IniSecurityManagerFactory(classpath:shiro-realm.ini);//2、得到SecurityManager实例 并绑定给SecurityUtilsorg.apache.shiro.mgt.SecurityManager securityManager factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//3、得到Subject及创建用户名/密码身份验证Token即用户身份/凭证Subject subject SecurityUtils.getSubject();UsernamePasswordToken token new UsernamePasswordToken(zhang, 123);try {//4、登录即身份验证subject.login(token);} catch (AuthenticationException e) {//5、身份验证失败}System.out.println(subject.isAuthenticated());//6、退出subject.logout();}2.1.4.2.多Realm 配置 1、ini 配置文件shiro-multi-realm.ini #声明一个realm myRealm1net.wanho.realm.MyRealm1 myRealm2net.wanho.realm.MyRealm2 #指定securityManager的realms实现 securityManager.realms$myRealm1,$myRealm2注securityManager 会按照 realms 指定的顺序进行身份认证。此处我们使用显示指定顺序的方式指定了 Realm 的顺序如果删除 “securityManager.realmsmyRealm1,myRealm2”那么securityManager 会按照 realm 声明的顺序进行使用即无需设置 realms 属性其会自动发现当我们显示指定 realm 后其他没有指定 realm 将被忽略如 “securityManager.realms$myRealm1”那么 myRealm2 不会被自动设置进去。 2、测试 测试方法同单 Realm 配置方法一致 2.1.4.3.Shiro 默认提供的 Realm 其中主要默认实现如下 org.apache.shiro.realm.text.IniRealm[users] 部分指定用户名 / 密码及其角色[roles] 部分指定角色即权限信息 org.apache.shiro.realm.text.PropertiesRealm user.usernamepassword,role1,role2 指定用户名 / 密码及其角色role.role1permission1,permission2 指定角色及权限信息 **org.apache.shiro.realm.jdbc.JdbcRealm**通过 sql 查询相应的信息如 “select password from users where username ?” 获取用户密码“select password, password_salt from users where username ?” 获取用户密码及盐“select role_name from user_roles where username ?” 获取用户角色“select permission from roles_permissions where role_name ?” 获取角色对应的权限信息也可以调用相应的 api 进行自定义 sql 2.1.4.4.JDBC Realm 使用 1、数据库及依赖 使用 mysql 数据库及 druid 连接池 dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.18/version /dependency dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.0.11/version/dependency2、到数据库 shiro 下建三张表users用户名 / 密码、user_roles用户 / 角色、roles_permissions角色 / 权限并添加一个用户记录用户名 / 密码为 username/password 3、ini 配置shiro-jdbc-realm.ini jdbcRealmorg.apache.shiro.realm.jdbc.JdbcRealm dataSourcecom.alibaba.druid.pool.DruidDataSource dataSource.driverClassNamecom.mysql.jdbc.Driver dataSource.urljdbc:mysql://localhost:3306/shiro dataSource.usernameroot dataSource.password11111 jdbcRealm.dataSource$dataSource securityManager.realms$jdbcRealm注 变量名 全限定类名会自动创建一个类实例 全限定类名会自动创建一个类实例变量名. 属性 值 自动调用相应的 setter 方法进行赋值$ 变量名 引用之前的一个对象实例 4、测试 Testpublic void testLoginLoginout(){//1、获取SecurityManager工厂此处使用Ini配置文件初始化SecurityManagerFactoryorg.apache.shiro.mgt.SecurityManager factory new IniSecurityManagerFactory(classpath:shiro-jdbc-realm.ini);//2、得到SecurityManager实例 并绑定给SecurityUtilsorg.apache.shiro.mgt.SecurityManager securityManager factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//3、得到Subject及创建用户名/密码身份验证Token即用户身份/凭证Subject subject SecurityUtils.getSubject();UsernamePasswordToken token new UsernamePasswordToken(zhang, 123);try {//4、登录即身份验证subject.login(token);} catch (AuthenticationException e) {//5、身份验证失败}System.out.println(subject.isAuthenticated());//6、退出subject.logout();}2.1.5.Authenticator 及 AuthenticationStrategy Authenticator 的职责是验证用户帐号是 Shiro API 中身份验证核心的入口点, 如果验证成功将返回 AuthenticationInfo 验证信息此信息中包含了身份及凭证如果验证失败将抛出相应的AuthenticationException 实现。 SecurityManager 接口继承了 Authenticator另外还有一个 ModularRealmAuthenticator 实现其委托给多个 Realm 进行验证验证规则通过 AuthenticationStrategy 接口指定默认提供的实现 FirstSuccessfulStrategy 只要有一个 Realm 验证成功即可只返回第一个 Realm 身份验证成功的认证信息其他的忽略 AtLeastOneSuccessfulStrategy 只要有一个 Realm 验证成功即可和 FirstSuccessfulStrategy 不同返回所有 Realm 身份验证成功的认证信息 AllSuccessfulStrategy 所有 Realm 验证成功才算成功且返回所有 Realm 身份验证成功的认证信息如果有一个失败就失败了。 注 ModularRealmAuthenticator 默认使用 AtLeastOneSuccessfulStrategy 策略。 假设我们有三个 realm myRealm1 用户名 / 密码为 zhang/123 时成功且返回身份 / 凭据为 zhang/123 myRealm2 用户名 / 密码为 wang/123 时成功且返回身份 / 凭据为 wang/123 myRealm3 用户名 / 密码为 zhang/123 时成功且返回身份 / 凭据为 zhang163.com/123和 myRealm1 不同的是返回时的身份变了 1、ini 配置文件 (shiro-authenticator-all-success.ini) #指定securityManager的authenticator实现 authenticatororg.apache.shiro.authc.pam.ModularRealmAuthenticator securityManager.authenticator$authenticator #指定securityManager.authenticator的authenticationStrategy allSuccessfulStrategyorg.apache.shiro.authc.pam.AllSuccessfulStrategy securityManager.authenticator.authenticationStrategy$allSuccessfulStrategymyRealmnet.wanho.realm.MyRealm myRealm2net.wanho.realm.MyRealm2 myRealm3net.wanho.realm.MyRealm3 securityManager.realms$myRealm,$myRealm32、新建realm MyRealm2 public class MyRealm2 extends AuthorizingRealm {/*** 获取角色与权限*doGetAuthorizationInfo执行时机有三个如下* 1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”)自己去调用这个是否有什么角色或者是否有什么权限的时候* 2、RequiresRoles(admin) 在方法上加注解的时候* 3、shiro.hasPermission name admin/shiro.hasPermissiondustin:test在页面上加shiro标签的时候即进这个页面的时候扫描到有这个标签的时候。* param principals* return*/Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return null;}/*** 登录信息验证** 1.doGetAuthenticationInfo执行时机如下* 当调用Subject currentUser SecurityUtils.getSubject();* currentUser.login(token);* param token* return* throws AuthenticationException*/Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username (String)token.getPrincipal();String password new String((char[])token.getCredentials());if(!wang.equals(username)) {throw new UnknownAccountException(); //如果用户名错误}if(!123.equals(password)) {throw new IncorrectCredentialsException(); //如果密码错误}return new SimpleAuthenticationInfo(username, password, getName());}Overridepublic void clearCachedAuthorizationInfo(PrincipalCollection principals) {super.clearCachedAuthorizationInfo(principals);}Overridepublic void clearCachedAuthenticationInfo(PrincipalCollection principals) {super.clearCachedAuthenticationInfo(principals);}Overridepublic void clearCache(PrincipalCollection principals) {super.clearCache(principals);}public void clearAllCachedAuthorizationInfo() {getAuthorizationCache().clear();}public void clearAllCachedAuthenticationInfo() {getAuthenticationCache().clear();}public void clearAllCache() {clearAllCachedAuthenticationInfo();clearAllCachedAuthorizationInfo();} }MyRealm3 public class MyRealm extends AuthorizingRealm {/*** 获取角色与权限*doGetAuthorizationInfo执行时机有三个如下* 1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”)自己去调用这个是否有什么角色或者是否有什么权限的时候* 2、RequiresRoles(admin) 在方法上加注解的时候* 3、shiro.hasPermission name admin/shiro.hasPermissiondustin:test在页面上加shiro标签的时候即进这个页面的时候扫描到有这个标签的时候。* param principals* return*/Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return null;}/*** 登录信息验证** 1.doGetAuthenticationInfo执行时机如下* 当调用Subject currentUser SecurityUtils.getSubject();* currentUser.login(token);* param token* return* throws AuthenticationException*/Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username (String)token.getPrincipal();String password new String((char[])token.getCredentials());if(!zhang.equals(username)) {throw new UnknownAccountException(); //如果用户名错误}if(!123.equals(password)) {throw new IncorrectCredentialsException(); //如果密码错误}return new SimpleAuthenticationInfo(zhang163.com, password, getName());}Overridepublic void clearCachedAuthorizationInfo(PrincipalCollection principals) {super.clearCachedAuthorizationInfo(principals);}Overridepublic void clearCachedAuthenticationInfo(PrincipalCollection principals) {super.clearCachedAuthenticationInfo(principals);}Overridepublic void clearCache(PrincipalCollection principals) {super.clearCache(principals);}public void clearAllCachedAuthorizationInfo() {getAuthorizationCache().clear();}public void clearAllCachedAuthenticationInfo() {getAuthenticationCache().clear();}public void clearAllCache() {clearAllCachedAuthenticationInfo();clearAllCachedAuthorizationInfo();} }3、测试 首先通用化登录逻辑 private void login(String configFile) {//1、获取SecurityManager工厂此处使用Ini配置文件初始化SecurityManagerFactoryorg.apache.shiro.mgt.SecurityManager factory new IniSecurityManagerFactory(configFile);//2、得到SecurityManager实例 并绑定给SecurityUtilsorg.apache.shiro.mgt.SecurityManager securityManager factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//3、得到Subject及创建用户名/密码身份验证Token即用户身份/凭证Subject subject SecurityUtils.getSubject();UsernamePasswordToken token new UsernamePasswordToken(zhang, 123);subject.login(token); }/*** 测试验证规则可在配置文件中修改** FirstSuccessfulStrategy只要有一个 Realm 验证成功即可只返回第一个 Realm 身份验证成功的认证信息其他的忽略AtLeastOneSuccessfulStrategy只要有一个 Realm 验证成功即可和 FirstSuccessfulStrategy 不同返回所有 Realm 身份验证成功的认证信息AllSuccessfulStrategy所有 Realm 验证成功才算成功且返回所有 Realm 身份验证成功的认证信息如果有一个失败就失败了。*/Testpublic void testAllSuccessfulStrategyWithSuccess() {login(classpath:shiro-authenticator-all-success.ini);Subject subject SecurityUtils.getSubject();//得到一个身份集合其包含了Realm验证成功的身份信息PrincipalCollection principalCollection subject.getPrincipals();for (Object principal : principalCollection) {System.out.println(principal.toString());}}4、自定义AuthenticationStrategy实现首先看其API //在所有Realm验证之前调用 AuthenticationInfo beforeAllAttempts( Collection? extends Realm realms, AuthenticationToken token) throws AuthenticationException; //在每个Realm之前调用 AuthenticationInfo beforeAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException; //在每个Realm之后调用 AuthenticationInfo afterAttempt( Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException; //在所有Realm之后调用 AuthenticationInfo afterAllAttempts( AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException; 自定义实现时一般继承org.apache.shiro.authc.pam.AbstractAuthenticationStrategy即可 2.2.授权 授权也叫访问控制即在应用中控制谁能访问哪些资源如访问页面/编辑数据/页面操作等。在授权中需了解的几个关键对象主体Subject、资源Resource、权限Permission、角色Role。 主体 主体即访问应用的用户在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。 资源 在应用中用户可以访问的任何东西比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。 权限 安全策略中的原子授权单位通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源如 访问用户列表页面 查看/新增/修改/删除用户数据即很多时候都是 CRUD增查改删式权限控制 打印文档等等。。。 如上可以看出权限代表了用户有没有操作某个资源的权利即反映在某个资源上的操作允不允许不反映谁去执行这个操作。所以后续还需要把权限赋予给用户即定义哪个用户允许在某个资源上做什么操作权限Shiro 不会去做这件事情而是由实现人员提供。 Shiro 支持粗粒度权限如用户模块的所有权限和细粒度权限操作某个用户的权限即实例级别的后续部分介绍。 角色 角色代表了操作集合可以理解为权限的集合一般情况下我们会赋予用户角色而不是权限即这样用户可以拥有一组权限赋予权限时比较方便。典型的如项目经理、技术总监、CTO、开发工程师等都是角色不同的角色拥有一组不同的权限。 隐式角色 即直接通过角色来验证用户有没有操作权限如在应用中 CTO、技术总监、开发工程师可以使用打印机假设某天不允许开发工程师使用打印机此时需要从应用中删除相应代码再如在应用中 CTO、技术总监可以查看用户、查看权限突然有一天不允许技术总监查看用户、查看权限了需要在相关代码中把技术总监角色从判断逻辑中删除掉即粒度是以角色为单位进行访问控制的粒度较粗如果进行修改可能造成多处代码修改。 显示角色 在程序中通过权限控制谁能访问某个资源角色聚合一组权限集合这样假设哪个角色不能访问某个资源只需要从角色代表的权限集合中移除即可无须修改多处代码即粒度是以资源/实例为单位的粒度较细。 2.2.1.授权方式 Shiro 支持三种方式的授权 1.编程式通过写 if/else 授权代码块完成 Subject subject SecurityUtils.getSubject(); if(subject.hasRole(“admin”)) {//有权限 } else {//无权限 };2.注解式通过在执行的 Java 方法上放置相应的注解完成 RequiresRoles(admin) public void hello() {//有权限 };3.SP/GSP 标签在 JSP/GSP 页面通过相应的标签完成 shiro:hasRole nameadmin !— 有权限 — /shiro:hasRole;2.2.1.1.基于角色的访问控制隐式角色 1、在 ini 配置文件配置用户拥有的角色shiro-role.ini [users] zhang123,role1,role2 wang123,role1规则即“用户名密码,角色1角色2”如果需要在应用中判断用户是否有相应角色就需要在相应的 Realm 中返回角色信息也就是说 Shiro 不负责维护用户-角色信息需要应用提供Shiro 只是提供相应的接口方便验证。 2、测试 登录方法 private Subject login(String configFile,String userName,String password) {//1、获取SecurityManager工厂此处使用Ini配置文件初始化SecurityManagerFactoryorg.apache.shiro.mgt.SecurityManager factory new IniSecurityManagerFactory(configFile);//2、得到SecurityManager实例 并绑定给SecurityUtilsorg.apache.shiro.mgt.SecurityManager securityManager factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//3、得到Subject及创建用户名/密码身份验证Token即用户身份/凭证Subject subject SecurityUtils.getSubject();UsernamePasswordToken token new UsernamePasswordToken(userName, password);subject.login(token);return subject; }Shiro提供了hasRole/hasRole用于判断用户是否拥有某个角色/某些权限 Testpublic void testHasRole() {Subject subject login(classpath:shiro-role.ini, zhang, 123);//判断拥有角色role1System.out.println(subject.hasRole(role1));//判断拥有角色role1 and role2System.out.println(subject.hasAllRoles(Arrays.asList(role1, role2)));//判断拥有角色role1 and role2 and !role3boolean[] result subject.hasRoles(Arrays.asList(role1, role2, role3));System.out.println(result[0]);System.out.println(result[1]);System.out.println(result[2]);}Shiro提供的checkRole/checkRoles和hasRole/hasAllRoles不同的地方是它在判断为假的情况下会抛出UnauthorizedException异常。 Test(expected UnauthorizedException.class)public void testCheckRole() {Subject subject login(classpath:shiro-role.ini, zhang, 123);//判断是否拥有角色role1subject.checkRole(role1);//判断是否拥有角色role1 and role3 失败抛出异常subject.checkRoles(role1, role3);}注 到此基于角色的访问控制即隐式角色就完成了这种方式的缺点就是如果很多地方进行了角色判断但是有一天不需要了那么就需要修改相应代码把所有相关的地方进行删除这就是粗粒度造成的问题。 2.2.1.2.基于角色的访问控制显式角色 1、在ini配置文件配置用户拥有的角色及角色-权限关系shiro-permission.ini [users] zhang123,role1,role2 wang123,role1 [roles] role1user:create,user:update role2user:create,user:delete 规则“用户名密码角色1角色2”“角色权限1权限2”即首先根据用户名找到角色然后根据角色再找到权限即角色是权限集合Shiro同样不进行权限的维护需要我们通过Realm返回相应的权限信息。只需要维护“用户——角色”之间的关系即可。 2、测试 Shiro提供了isPermitted和isPermittedAll用于判断用户是否拥有某个权限或所有权限 Testpublic void testIsPermitted() {Subject subject login(classpath:shiro-permission.ini, zhang, 123);//判断拥有权限user:createSystem.out.println(subject.isPermitted(user:create));//判断拥有权限user:update and user:deleteSystem.out.println(subject.isPermittedAll(user:update, user:delete));//判断没有权限user:viewSystem.out.println(subject.isPermitted(user:view));}Test(expected UnauthorizedException.class) public void testCheckPermission () { Subject subject login(classpath:shiro-permission.ini, zhang, 123);//断言拥有权限user:create subject.checkPermission(user:create); //断言拥有权限user:delete and user:update subject.checkPermissions(user:delete, user:update); //断言拥有权限user:view 失败抛出异常 subject.checkPermissions(user:view); } 失败的情况下会抛出UnauthorizedException异常 到此基于资源的访问控制显示角色就完成了也可以叫基于权限的访问控制这种方式的一般规则是“资源标识符操作”即是资源级别的粒度这种方式的好处就是如果要修改基本都是一个资源级别的修改不会对其他模块代码产生影响粒度小。但是实现起来可能稍微复杂点需要维护“用户——角色角色——权限资源操作”之间的关系。 2.2.2.Permission权限 字符串通配符权限 规则“资源标识符操作对象实例ID” 即对哪个资源的哪个实例可以进行什么操作。其默认支持通配符权限字符串“:”表示资源/操作/实例的分割“,”表示操作的分割“*”表示任意资源/操作/实例。 2.2.2.1.单个资源单个权限 subject.checkPermissions(system:user:update); 用户拥有资源“system:user”的“update”权限。 2.2.2.2.单个资源多个权限 1.ini配置文件 (两种写法) role41system:user:update,system:user:delete #或者 role41system:user:update,delete2.然后通过如下代码判断 subject.checkPermissions(system:user:update, system:user:delete); //或者 subject.checkPermissions(system:user:update,delete);通过“system:user:update,delete”验证system:user:update, system:user:delete是没问题的但是反过来是规则不成立。 2.2.2.3.单个资源全部权限 1.ini配置文件 (两种写法) role51system:user:create,update,delete,view #或者 role51system:user:* 2.然后通过如下代码判断 subject.checkPermissions(system:user:create,delete,update:view); //或者 subject.checkPermissions(system:user:*); subject.checkPermissions(system:user); 通过“system:user:*”验证“system:user:create,delete,update:view”可以但是反过来是不成立的。 2.2.2.4.所有资源全部权限 1.ini配置文件 role61*:view 2.然后通过如下代码判断 subject.checkPermissions(user:view); 用户拥有所有资源的“view”所有权限。假设判断的权限是“system:user:view”那么需要“role5* ?:view”这样写才行。 2.2.2.5.单个实例单个权限 1.ini配置文件 role71user:view:1 对资源user的1实例拥有view权限。 2.然后通过如下代码判断 subject.checkPermissions(user:view:1); 2.2.2.6.单个实例多个权限 1.ini配置文件 role72user:update,delete:1 2.然后通过如下代码判断 subject.checkPermissions(user:delete,update:1); subject.checkPermissions(user:update:1, user:delete:1); 2.2.2.7.单个实例所有权限 1.ini配置文件 role73user:*:1 2.然后通过如下代码判断 subject.checkPermissions(user:update:1, user:delete:1, user:view:1); 2.2.2.8.所有实例单个权限 1.ini配置文件 role74user:auth:*2.然后通过如下代码判断 subject.checkPermissions(user:auth:1, user:auth:2); 2.2.2.9.所有实例所有权限 1.ini配置文件 role75user:*:* 2.然后通过如下代码判断 subject.checkPermissions(user:view:1, user:auth:2); 2.2.2.10.Shiro对权限字符串缺失部分的处理 如“user:view”等价于“user:view:”而“organization”等价于“organization:”或者“organization::”。可以这么理解这种方式实现了前缀匹配。 另外如“user:”可以匹配如“user:delete”、“user:delete”可以匹配如“user:delete:1”、“user::1”可以匹配如“user:view:1”、“user”可以匹配“user:view”或“user:view:1”等。即可以匹配所有不加可以进行前缀匹配但是如“:view”不能匹配“system:user:view”需要使用“::view”即后缀匹配必须指定前缀多个冒号就需要多个来匹配。 2.2.2.11.性能问题 通配符匹配方式比字符串相等匹配来说是更复杂的因此需要花费更长时间但是一般系统的权限不会太多且可以配合缓存来提供其性能如果这样性能还达不到要求我们可以实现位操作算法实现性能更好的权限匹配。另外实例级别的权限验证如果数据量太大也不建议使用可能造成查询权限及匹配变慢。可以考虑比如在sql查询时加上权限字符串之类的方式在查询时就完成了权限匹配。 2.2.3.授权流程 流程如下 1、首先调用Subject.isPermitted*/hasRole接口其会委托给SecurityManager而SecurityManager接着会委托给Authorizer 2、Authorizer是真正的授权者如果我们调用如isPermitted(“user:view”)其首先会通过PermissionResolver把字符串转换成相应的Permission实例 3、在进行授权之前其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限 4、Authorizer会判断Realm的角色/权限是否和传入的匹配如果有多个Realm会委托给ModularRealmAuthorizer进行循环判断如果匹配如isPermitted/hasRole*会返回true否则返回false表示授权失败。 ModularRealmAuthorizer进行多Realm匹配流程 1、首先检查相应的Realm是否实现了实现了Authorizer 2、如果实现了Authorizer那么接着调用其相应的isPermitted*/hasRole*接口进行匹配 3、如果有一个Realm匹配那么将返回true否则返回false。 如果Realm进行授权的话应该继承AuthorizingRealm其流程是 1.1、如果调用hasRole*则直接获取AuthorizationInfo.getRoles()与传入的角色比较即可 1.2、首先如果调用如isPermitted(“user:view”)首先通过PermissionResolver将权限字符串转换成相应的Permission实例默认使用WildcardPermissionResolver即转换为通配符的WildcardPermission 2、通过AuthorizationInfo.getObjectPermissions()得到Permission实例集合通过AuthorizationInfo. getStringPermissions()得到字符串集合并通过PermissionResolver解析为Permission实例然后获取用户的角色并通过RolePermissionResolver解析角色对应的权限集合默认没有实现可以自己提供 3、接着调用Permission. implies(Permission p)逐个与传入的权限比较如果有匹配的则返回true否则false。
http://www.ho-use.cn/article/10820883.html

相关文章:

  • 企石网站仿做潍坊专业输送带产品介绍
  • 帮公司做网站简单网站系统
  • 超酷网站seo关键词
  • 网站搭建中114514wordpress页脚菜单
  • 服务器网站过多对排名wordpress移动端模板
  • 苏州网站建设推广咨询平台专门给别人做网站
  • 西安制作网站软件加盟创业商机网
  • 为啥做网站开发公司房价制定
  • 郑州手机网站网app开发
  • dw怎么做百度网站个人信息怎么在百度推广
  • 网站app用什么语言开发哪些网站可以做设计赚钱
  • 郑州网站建设兼职1534939978姐的微信德惠市
  • 网站建设公司权威排名厦门百度seo排名
  • 开发网站公司如何运营乌海网站开发
  • 怎么开发网站卡地亚官方网站制作需要多少钱
  • 化工企业网站模板成都规划网站
  • 自己做网站需要服务器吗电子商务有限公司有哪些
  • 网站营销外包公司网站建设的标签指的是
  • 仓山区城乡建设局网站宣传片制作标准参数
  • 彩票网站维护需要几天点击链接即可进入
  • 上海自助建站上海网站建设自己做视频类网站用哪个cms
  • 全屏网站 欣赏不懂见网站怎么办
  • 小生意是做网站还是公众号手机网站网页设计
  • 开发公司产品部课件商丘市网络优化公司地址
  • 兼职网站制作做网站怎么调用栏目
  • 万网网站开发拓者设计吧 现代风格
  • 医疗公司网站建设项目背景南山做网站行业
  • 新网 网站建立wordpress nginx apache
  • 中国字体设计网站广告设计公司投标书范文
  • 企业营销型网站建设企查查在线查询网页版