权限控制框架-shiro

一。 shiro简介

Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:

  • 认证 - 用户身份识别,常被称为用户“登录”;
  • 授权 - 访问控制;
  • 密码加密 - 保护或隐藏数据防止被偷窥;
  • 会话管理 - 每用户相关的时间敏感的状态。
Shiro还支持一些辅助特性,如Web应用安全、单元测试和多线程,它们的存在强化了上面提到的四个要素

从2003年至今,框架选择方面的情况已经改变了不少,但今天仍有令人信服的理由让你选择Shiro。其实理由相当多,Apache Shiro:

  • 易于使用 - 易用性是这个项目的最终目标。应用安全有可能会非常让人糊涂,令人沮丧,并被认为是“必要之恶”【译注:比喻应用安全方面的编程。】。若是能让它简化到新手都能很快上手,那它将不再是一种痛苦了。
  • 广泛性 - 没有其他安全框架可以达到Apache Shiro宣称的广度,它可以为你的安全需求提供“一站式”服务。
  • 灵活性 - Apache Shiro可以工作在任何应用环境中。虽然它工作在Web、EJB和IoC环境中,但它并不依赖这些环境。Shiro既不强加任何规范,也无需过多依赖。
  • Web能力 - Apache Shiro对Web应用的支持很神奇,允许你基于应用URL和Web协议(如REST)创建灵活的安全策略,同时还提供了一套控制页面输出的JSP标签库。
  • 可插拔 - Shiro干净的API和设计模式使它可以方便地与许多的其他框架和应用进行集成。你将看到Shiro可以与诸如Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin这类第三方框架无缝集成。
支持 - Apache Shiro是Apache软件基金会成员,这是一个公认为了社区利益最大化而行动的组织。项目开发和用户组都有随时愿意提供帮助的友善成员。像Katasoft这类商业公司,还可以给你提供需要的专业支持和服务。

核心概念:Subject,SecurityManager和Realms 

Subject 

       Subject一词是一个安全术语,其基本意思是“当前的操作用户”。称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。在代码的任何地方,你都能轻易的获得Shiro Subject,

SecurityManager

Subject的“幕后”推手是SecurityManager。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。它是Shiro框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦SecurityManager及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的所有时间都花在Subject API调用上。
那么,如何设置SecurityManager呢?嗯,这要看应用的环境。例如,Web应用通常会在Web.xml中指定一个Shiro Servlet Filter,这会创建SecurityManager实例,如果你运行的是一个独立应用,你需要用其他配置方式,但有很多配置选项。
一个应用几乎总是只有一个SecurityManager实例。它实际是应用的Singleton(尽管不必是一个静态Singleton)。跟Shiro里的几乎所有组件一样,SecurityManager的缺省实现是POJO,而且可用POJO兼容的任何配置机制进行配置 - 普通的Java代码、Spring XML、YAML、.properties和.ini文件等。基本来讲,能够实例化类和调用JavaBean兼容方法的任何配置形式都可使用。
为此,Shiro借助基于文本的INI配置提供了一个缺省的“公共”解决方案。INI易于阅读、使用简单并且需要极少依赖。你还能看到,只要简单地理解对象导航,INI可被有效地用于配置像SecurityManager那样简单的对象图。注意,Shiro还支持Spring XML配置及其他方式,但这里只我们只讨论INI。
Realms
Shiro的第三个也是最后一个概念是Realm。Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当切实与像用户帐户这类安全相关数据进行交互,执行认证(登录)和授权(访问控制)时,Shiro会从应用配置的Realm中查找很多内容。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现

文字摘自 (http://www.infoq.com/cn/articles/apache-shiro)

shiro架构 


除前面所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括: 

  • Authenticator :认证就是核实用户身份的过程。这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。 
  • Authorizer :授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。 
  • SessionManager :在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。 
  • CacheManager :对Shiro的其他组件提供缓存支持。 
  • Cryptography 提供安全的支持

常用的操作
ini配置权限信息(参考http://shiro.apache.org/configuration.html)

[html]  view plain  copy
  1. # =======================  
  2. # Shiro INI configuration  
  3. # =======================  
  4.   
  5. [main]  
  6.   
  7. [users]  
  8. # 设置用户信息  
  9. # 语法是 username = password, roleName1, roleName2, …, roleNameN  
  10. jiaozi = 123456,role1  
  11.   
  12. [roles]  
  13. # 角色信息和角色拥有的权限  
  14. #语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN  
  15. #权限的语法 * 表示所有权限  一般语法是 权限类型.权限动作.权限的资源id  比如  user:delete:1 表示拥有删除1号用户的权限  user:delete:*表示删除所有用户权限  
  16. admin = *  
  17. role1 = user:query:*, user:delete:1  
  18.   
  19. [urls]  
  20. # web中的url过滤  
maven项目添加支持
[html]  view plain  copy
  1.     <dependency>  
  2.         <groupId>org.apache.shiro</groupId>  
  3.         <artifactId>shiro-core</artifactId>  
  4.         <version>1.4.0</version>  
  5.     </dependency>  
  6.     <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->  
  7. <dependency>  
  8.     <groupId>commons-logging</groupId>  
  9.     <artifactId>commons-logging</artifactId>  
  10.     <version>1.2</version>  
  11. </dependency>  
测试验证权限过程(参考http://shiro.apache.org/10-minute-tutorial.html)
[html]  view plain  copy
  1. package shiro;  
  2.   
  3. import org.apache.shiro.SecurityUtils;  
  4. import org.apache.shiro.authc.UsernamePasswordToken;  
  5. import org.apache.shiro.config.IniSecurityManagerFactory;  
  6. import org.apache.shiro.session.Session;  
  7. import org.apache.shiro.subject.Subject;  
  8. import org.apache.shiro.util.Factory;  
  9.   
  10. public class TestShiro {  
  11.     public static void main(String[] args) {  
  12.         testIni();  
  13.     }  
  14.     public static void testIni() {  
  15.         //从配置文件中读取用户的权限信息  
  16.         Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");  
  17.         org.apache.shiro.mgt.SecurityManager securityManager = (org.apache.shiro.mgt.SecurityManager)factory.getInstance();  
  18.         SecurityUtils.setSecurityManager(securityManager);  
  19.         //获取登录用户信息  
  20.         Subject currentUser = SecurityUtils.getSubject();  
  21.         Session session = currentUser.getSession();  
  22.         session.setAttribute( "保存数据的键", "保存数据的值" );  
  23.         /**  
  24.          * 用户包括两部分   
  25. //       *    principals and credentials  
  26.          *     principals(本人)表示用户的标识信息 比如用户名 用户地址等  
  27.          *     credentials(凭证)表示用户用于登录的凭证 比如密码 证书等  
  28.          */  
  29.         //isAuthenticated判断是否登录过  
  30.         if ( !currentUser.isAuthenticated() ) {  
  31.             //令牌 用户名和密码 其实就是credentials  
  32.             UsernamePasswordToken token = new UsernamePasswordToken("jiaozi", "123456");  
  33.             token.setRememberMe(true);  
  34.             //开始登录操作  操作后 isAuthenticated就是true  
  35.             currentUser.login(token);  
  36.             System.out.println("登录成功");  
  37.             //判断是否有某个角色  
  38.             if(currentUser.hasRole("role1")){  
  39.                 System.out.println("拥有角色role1");  
  40.             }  
  41.             if(currentUser.isPermitted("user:query:1")){  
  42.                 System.out.println("拥有查询1号用户权限");  
  43.             }  
  44.         }  
  45.           
  46.         currentUser.logout();  
  47.           
  48.     }  
  49.       
  50. }  
一般权限信息存在于是数据库 我这里模拟放在内存变量map中 

添加realm的实现类

[html]  view plain  copy
  1. package shiro;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.HashSet;  
  5. import java.util.Map;  
  6. import java.util.Set;  
  7.   
  8. import org.apache.shiro.authc.AuthenticationException;  
  9. import org.apache.shiro.authc.AuthenticationInfo;  
  10. import org.apache.shiro.authc.AuthenticationToken;  
  11. import org.apache.shiro.authc.SimpleAccount;  
  12. import org.apache.shiro.authc.UsernamePasswordToken;  
  13. import org.apache.shiro.authz.AuthorizationInfo;  
  14. import org.apache.shiro.authz.SimpleAuthorizationInfo;  
  15. import org.apache.shiro.realm.AuthorizingRealm;  
  16. import org.apache.shiro.subject.PrincipalCollection;  
  17. /**  
  18.  * 自定义realm的实现  
  19.  * @author jiaozi  
  20.  *  
  21.  */  
  22. public class MyRealm extends AuthorizingRealm {  
  23.     //用于存放用户信息  
  24.     static Map<String,String> userList=null;  
  25.     //用于存放角色信息  
  26.     static Map<String,String> roleList=null;  
  27.     //每个realm都有一个名字  
  28.     static String REALM_NAME="myrealm";  
  29.     static{  
  30.         //这里也可以从数据库读取  
  31.         //模拟用户  
  32.         userList=new HashMap();  
  33.         userList.put("zs", "123456,role2,role3");  
  34.         //模拟权限  
  35.         roleList=new HashMap();  
  36.         roleList.put("role2","user:query:*");  
  37.         roleList.put("role3", "user:*");  
  38.     }  
  39.     /**  
  40.      * 支持哪种令牌   
  41.      */  
  42.     @Override  
  43.     public boolean supports(AuthenticationToken token) {  
  44.         // TODO Auto-generated method stub  
  45.         return token instanceof UsernamePasswordToken;  
  46.     }  
  47.     /**  
  48.      * 获取权限过程  
  49.      */  
  50.     @Override  
  51.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  52.         //获取用户名  
  53.         String userName=principals.getPrimaryPrincipal().toString();  
  54.         //构建权限的类  
  55.         SimpleAuthorizationInfo sai=new SimpleAuthorizationInfo();  
  56.         Set<String> proleList=new HashSet<String>();  
  57.         Set<String> stringPermissions=new HashSet<String>();  
  58.         if(userList.containsKey(userName)){  
  59.             String[] roles=userList.get(userName).toString().split(",");  
  60.             for(int i=1;i<roles.length;i++){  
  61.                 proleList.add(roles[i]);  
  62.                 String pp=roleList.get(roles[i]);  
  63.                 String[] ppArry=pp.split(",");  
  64.                 for(int j=0;j<ppArry.length;j++){  
  65.                     stringPermissions.add(ppArry[j]);  
  66.                 }  
  67.             }  
  68.         }  
  69.         sai.setRoles(proleList);  
  70.         sai.setStringPermissions(stringPermissions);  
  71.         return sai;  
  72.     }  
  73.     /**  
  74.      * 认证过程  
  75.      */  
  76.     @Override  
  77.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
  78.         UsernamePasswordToken upt=(UsernamePasswordToken)token;  
  79.         String userName=token.getPrincipal().toString();  
  80.         String password=String.valueOf(upt.getPassword());  
  81.         if(userList.containsKey(userName)){  
  82.             String realPwd=userList.get(userName).toString().split(",")[0];  
  83.             if(realPwd.equals(password)){  
  84.                 SimpleAccount sa=new SimpleAccount(userName,password,"REALM_NAME");  
  85.                 return sa;  
  86.             }  
  87.         }  
  88.         return null;  
  89.     }  
  90.   
  91. }  
ini配置文件中配置该realm  main的部分添加
[html]  view plain  copy
  1. [main]  
  2. # 定义securityManager, Realms and 其他  
  3. myRealmshiro.MyRealm  
  4. securityManager.realms=$iniRealm , $myRealm  
  5. #iniRealm是内置的默认realm读取ini文件的传入 如果自定义了realm该realm被替代 如果还想使用可以直接引用  
  6. #如果有多个realm 策略是所有的realm都要验证通过 还是只需要一个验证通过  我这里只需要一个验证通过 默认是所有  
  7. #参考http://shiro.apache.org/authentication.html  
  8. authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy  
  9. securityManager.authenticator.authenticationStrategy = $authcStrategy  
同上测试  只要ini或者自定义ream存在的用户都能登录  

二。 shiro web

集成web集成 参考 http://shiro.apache.org/web.html#programmatic-support

在之前例子基础上进行拓展 添加war项目 添加shiro-web依赖

[html]  view plain  copy
  1. <dependency>  
  2.     <groupId>org.apache.shiro</groupId>  
  3.     <artifactId>shiro-web</artifactId>  
  4.     <version>1.4.0</version>  
  5. </dependency>  

web.xml添加shiro支持的过滤器和ini文件路径配置参数

[html]  view plain  copy
  1. <context-param>  
  2.     <param-name>shiroConfigLocations</param-name>  
  3.     <param-value>/WEB-INF/shiro.ini</param-value>  
  4.   </context-param>  
  5.   <listener>  
  6.     <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>  
  7.   </listener>  
  8.   <filter>  
  9.     <filter-name>ShiroFilter</filter-name>  
  10.     <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>  
  11.   </filter>  
  12.   <filter-mapping>  
  13.     <filter-name>ShiroFilter</filter-name>  
  14.     <url-pattern>/*</url-pattern>  
  15.     <dispatcher>REQUEST</dispatcher>  
  16.     <dispatcher>FORWARD</dispatcher>  
  17.     <dispatcher>INCLUDE</dispatcher>  
  18.     <dispatcher>ERROR</dispatcher>  
  19.   </filter-mapping>  

添加几个html和jsp用于测试

login.html添加用于登录的表单

[html]  view plain  copy
  1. <form action="loginServlet" method="post">  
  2.      用戶名 :<input type="text" name="userName"/>  
  3.    密碼:<input type="text" name="password"/>  
  4.    <input type="submit">  
  5.   </form>  
添加query.jsp用于当登录成功后跳转的页面 
[html]  view plain  copy
  1. <body>  
  2.   這是測試查詢頁面  
  3. </body>  
添加add.jsp用于测试拥有某个角色才能进入的页面
[html]  view plain  copy
  1. <body>  
  2.   這是測試新增頁面  
  3. </body>  
添加一个检测没有角色失败页面 un.jsp
[html]  view plain  copy
  1. <body>  
  2.   没有权限  
  3. </body  
t添加一个登录的servlet
web.xml
[html]  view plain  copy
  1. <servlet>  
  2.    <description></description>  
  3.    <display-name>LoginServlet</display-name>  
  4.    <servlet-name>LoginServlet</servlet-name>  
  5.    <servlet-class>cn.et.web.LoginServlet</servlet-class>  
  6.  </servlet>  
  7.  <servlet-mapping>  
  8.    <servlet-name>LoginServlet</servlet-name>  
  9.    <url-pattern>/loginServlet</url-pattern>  
  10.  </servlet-mapping>  
servlet类
[html]  view plain  copy
  1. package cn.et.web;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import javax.servlet.ServletException;  
  6. import javax.servlet.http.HttpServlet;  
  7. import javax.servlet.http.HttpServletRequest;  
  8. import javax.servlet.http.HttpServletResponse;  
  9.   
  10. import org.apache.shiro.SecurityUtils;  
  11. import org.apache.shiro.authc.AuthenticationException;  
  12. import org.apache.shiro.authc.UsernamePasswordToken;  
  13. import org.apache.shiro.subject.Subject;  
  14.   
  15. public class LoginServlet extends HttpServlet {  
  16.     private static final long serialVersionUID = 1L;  
  17.   
  18.     /**  
  19.      * 登录进入方法  
  20.      */  
  21.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  22.         Subject subject = SecurityUtils.getSubject();  
  23.         UsernamePasswordToken upt=new UsernamePasswordToken(request.getParameter("userName"), request.getParameter("password"));  
  24.         try {  
  25.             subject.login(upt);  
  26.             request.getRequestDispatcher("/query.jsp").forward(request, response);  
  27.         } catch (AuthenticationException e) {  
  28.             request.getRequestDispatcher("/login.html").forward(request, response);  
  29.         }  
  30.     }  
  31.   
  32.     /**  
  33.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)  
  34.      */  
  35.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  36.         // TODO Auto-generated method stub  
  37.         doGet(request, response);  
  38.     }  
  39.   
  40. }  
修改shiro.ini添加路径过滤
[html]  view plain  copy
  1. # =======================  
  2. # Shiro INI configuration  
  3. # =======================  
  4.   
  5. [main]  
  6. # 定义securityManager, Realms and 其他  
  7. myRealmshiro.MyRealm  
  8. securityManager.realms=$iniRealm , $myRealm  
  9. #如果有多个realm 策略是所有的realm都要验证通过 还是只需要一个验证通过  我这里只需要一个验证通过 默认是所有  
  10. authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy  
  11. securityManager.authenticator.authenticationStrategy = $authcStrategy  
  12. authc.loginUrl = /login.html  
  13. #如果沒有登錄 會先跳轉到loginUrl 如果登錄了沒權限 跳轉到unauthorizedUrl指定頁面  
  14. roles.loginUrl= /login.html  
  15. roles.unauthorizedUrl= /un.html  
  16.   
  17. [users]  
  18. # 设置用户信息  
  19. # 语法是 username = password, roleName1, roleName2, …, roleNameN  
  20. jiaozi = 123456,role1  
  21.   
  22. [roles]  
  23. # 角色信息和角色拥有的权限  
  24. #语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN  
  25. #权限的语法 * 表示所有权限  一般语法是 权限类型.权限动作.权限的资源id  比如  user:delete:1 表示拥有删除1号用户的权限  user:delete:*表示删除所有用户权限  
  26. admin = *  
  27. role1 = user:query:*, user:delete:1  
  28.   
  29. [urls]  
  30. # web中的url过滤    
  31. #語法是  某個路徑  = 怎麼樣過濾1,過濾2   常用的過濾有  
  32. #  anon 匿名用戶   
  33. #  authc 表示用戶和密碼驗證過濾 類 org.apache.shiro.web.filter.authc.FormAuthenticationFilter 沒有登錄自動跳轉到登錄頁  
  34. #  perms 是否擁有某些權限過濾 類 org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 用法 perms["remote:invoke"]  
  35. #   roles是否擁有某個角色   org.apache.shiro.web.filter.authz.RolesAuthorizationFilter  用法roles[administrator]  
  36. #  user 是否是某個用戶 org.apache.shiro.web.filter.authc.UserFilter   
  37. # 也可以在main中自定義filter url就可以應用 參考http://shiro.apache.org/web.html#programmatic-support  
  38.   
  39. /login.html = anon  
  40. /loginServlet = anon  
  41. /query.jsp = authc  
  42. /add.jsp = roles[role2]  
测试不登录直接访问 query.jsp或者add.jsp直接跳转到login.html中
测试登录ini中的jiaozi用户登录成功后可以访问query.jsp访问add.jsp自动跳转到un.jsp 因为jiaozi用户没有role2这个juese

测试登录自定义relam的zs用户 发现都可以进入

shiro通过自定义过滤器 定制自己的过滤规则 比如url信息全部位于数据库 需要所有的除了控制层的路径都经过自定义的url过滤可以添加类 继承自AuthorizationFilter实现isAccessAllowed方法 

[html]  view plain  copy
  1. package cn.et.conf;  
  2.   
  3. import javax.servlet.ServletRequest;  
  4. import javax.servlet.ServletResponse;  
  5.   
  6. import org.apache.shiro.web.filter.authz.AuthorizationFilter;  
  7.   
  8. public class MyFilter extends AuthorizationFilter {  
  9.   
  10.     /*  
  11.      * 返回true表示允许访问 false表示不允许访问  
  12.          */  
  13.     @Override  
  14.     protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)  
  15.             throws Exception {  
  16.         return false;  
  17.     }  
  18.   
  19. }  
在main中定义该filter 直接在url中引用即可
[html]  view plain  copy
  1. myfilter=cn.et.conf.MyFilter  
  2. /** myfilter  

当用户访问了某个页面 通过authc过滤跳转到登录页面 当用户登录后 希望自动跳转到之前的页面 如果是直接进入的登录页就进入默认的页面 的实现过程
偶尔 有些无法跳转 结果下载了fav.ico 因为有时一个请求到页面 会顺带一个获取图标的请求  如果图标请求在后面 shiro记录的前面的途径就是这个图标的请求 此时在html添加 禁止网页图标的请求

[html]  view plain  copy
  1. <link rel="icon" href="data:image/ico;base64,aWNv">  
登录实现

[html]  view plain  copy
  1. @RequestMapping("loginBlog")  
  2.     public String login(String userName,String password,HttpServletRequest request){  
  3.         Subject subject = SecurityUtils.getSubject();  
  4.         UsernamePasswordToken upt=new UsernamePasswordToken(userName,password);  
  5.         try {  
  6.             subject.login(upt);  
  7.             subject.getSession().setAttribute("userInfo", userDao.queryByContent(userName, password).get(0));  
  8.                         //关键代码 shiro自动保存了上一次请求的地址  只需要获取并重新跳转即可  
  9.                         //可以使用WebUtils.issueRedirect重定向跳转  
  10.             SavedRequest savedRequest = WebUtils.getSavedRequest(request);  
  11.             if(savedRequest!=null)  
  12.                 return "redirect:"+savedRequest.getRequestURI()+"?"+savedRequest.getQueryString();  
  13.             return "/queryBlog";  
  14.         } catch (AuthenticationException e) {  
  15.             return "redirect:/login.html";  
  16.         }  
  17.           
  18.     }  

三。 shiro集成springboot

参考官网 spring集成 只是换成springboot (http://shiro.apache.org/spring.html)
添加maven的jsp支持和shiro-spring和springboot

[html]  view plain  copy
  1. <parent>  
  2.     <groupId>org.springframework.boot</groupId>  
  3.     <artifactId>spring-boot-starter-parent</artifactId>  
  4.     <version>1.5.9.RELEASE</version>  
  5.   </parent>  
  6.   <dependencies>  
  7.    <dependency>  
  8.         <groupId>org.springframework.boot</groupId>  
  9.         <artifactId>spring-boot-starter-web</artifactId>  
  10.     </dependency>  
  11.     <dependency>  
  12.             <groupId>org.apache.tomcat.embed</groupId>  
  13.             <artifactId>tomcat-embed-jasper</artifactId>  
  14.         </dependency>  
  15.     <dependency>  
  16.         <groupId>org.apache.shiro</groupId>  
  17.         <artifactId>shiro-core</artifactId>  
  18.         <version>1.4.0</version>  
  19.     </dependency>  
  20.     <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->  
  21. <dependency>  
  22.     <groupId>org.apache.shiro</groupId>  
  23.     <artifactId>shiro-web</artifactId>  
  24.     <version>1.4.0</version>  
  25. </dependency>  
  26.     <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->  
  27. <dependency>  
  28.     <groupId>org.apache.shiro</groupId>  
  29.     <artifactId>shiro-spring</artifactId>  
  30.     <version>1.4.0</version>  
  31. </dependency>  
  32.       
  33.     <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->  
  34. <dependency>  
  35.     <groupId>commons-logging</groupId>  
  36.     <artifactId>commons-logging</artifactId>  
  37.     <version>1.2</version>  
  38. </dependency>  
  39.       
  40.   </dependencies>  
html和realm还是之前的 ini文件被替换成在spring中配置  添加配置的bean
[html]  view plain  copy
  1. package cn.et.boot;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.LinkedHashSet;  
  5. import java.util.Map;  
  6.   
  7. import org.apache.shiro.realm.Realm;  
  8. import org.apache.shiro.spring.LifecycleBeanPostProcessor;  
  9. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;  
  10. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;  
  11. import org.springframework.beans.factory.annotation.Autowired;  
  12. import org.springframework.boot.web.servlet.FilterRegistrationBean;  
  13. import org.springframework.context.annotation.Bean;  
  14. import org.springframework.context.annotation.Configuration;  
  15. import org.springframework.web.filter.DelegatingFilterProxy;  
  16.   
  17. import shiro.MyRealm;  
  18.   
  19. @Configuration  
  20. public class Config {  
  21.     /**  
  22.      * 等價于 web.xml配置  
  23.      * 自動 將 /*的請求 委託給spring容器中 bean名字和filter-name一致的bean處理  
  24.      *   
  25.        <filter>  
  26.         <filter-name>shiroFilter</filter-name>  
  27.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  28.         <init-param>  
  29.             <param-name>targetFilterLifecycle</param-name>  
  30.             <param-value>true</param-value>  
  31.         </init-param>  
  32.         </filter>  
  33.         <filter-mapping>  
  34.             <filter-name>shiroFilter</filter-name>  
  35.             <url-pattern>/*</url-pattern>  
  36.         </filter-mapping>  
  37.      * @return  
  38.      */  
  39.     @Bean  
  40.     public FilterRegistrationBean webShiroFilter(){  
  41.         FilterRegistrationBean frb=new FilterRegistrationBean();  
  42.         DelegatingFilterProxy dfp=new DelegatingFilterProxy();  
  43.         frb.setFilter(dfp);  
  44.         frb.setName("shiroFilter");  
  45.         LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();  
  46.         linkedHashSet.add("/*");  
  47.         frb.setUrlPatterns(linkedHashSet);  
  48.           
  49.           
  50.         Map<String, String> initParameters=new HashMap<String, String>();  
  51.         initParameters.put("targetFilterLifecycle", "true");  
  52.         frb.setInitParameters(initParameters);  
  53.         return frb;  
  54.     }  
  55.     /**  
  56.      * 配置我的realm  
  57.      * @return  
  58.      */  
  59.     @Bean  
  60.     public Realm myRealm(){  
  61.         return new MyRealm();  
  62.     }  
  63.     /**  
  64.      * 定義默認的securityManager  
  65.      * @return  
  66.      */  
  67.     @Bean  
  68.     public DefaultWebSecurityManager securityManager(@Autowired Realm myRealm){  
  69.         DefaultWebSecurityManager dwm=new DefaultWebSecurityManager();  
  70.         dwm.setRealm(myRealm);  
  71.         return dwm;  
  72.     }  
  73.       
  74.     /**  
  75.      * 定義和過濾器一致名字的ShiroFilterFactoryBean  
  76.      */  
  77.     @Bean  
  78.     public ShiroFilterFactoryBean shiroFilter(@Autowired org.apache.shiro.mgt.SecurityManager securityManager){  
  79.         ShiroFilterFactoryBean sffb=new ShiroFilterFactoryBean();  
  80.         sffb.setSecurityManager(securityManager);  
  81.         sffb.setLoginUrl("/login.html");  
  82.         sffb.setUnauthorizedUrl("/un.jsp");  
  83.         Map<String, String> urls=new HashMap<String, String>();  
  84.         /*  
  85.          *    定义url  
  86.             /login.html = anon  
  87.             /loginServlet = anon  
  88.             /query.jsp = authc  
  89.             /add.jsp = roles[role2]  
  90.          * */  
  91.           
  92.         urls.put("/login.html", "anon");  
  93.         urls.put("/loginServlet", "anon");  
  94.         urls.put("/query.jsp", "authc");  
  95.         urls.put("/add.jsp", "roles[role1]");  
  96.         sffb.setFilterChainDefinitionMap(urls);  
  97.         return sffb;  
  98.     }  
  99.     /**  
  100.      * 定義後置處理器  
  101.      * @return  
  102.      */  
  103.     @Bean  
  104.     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){  
  105.         return new LifecycleBeanPostProcessor();  
  106.     }  
  107. }  
将之前servlet替换成springmvc的controller
[html]  view plain  copy
  1. package cn.et.boot;  
  2.   
  3. import org.apache.shiro.SecurityUtils;  
  4. import org.apache.shiro.authc.AuthenticationException;  
  5. import org.apache.shiro.authc.UsernamePasswordToken;  
  6. import org.apache.shiro.subject.Subject;  
  7. import org.springframework.stereotype.Controller;  
  8. import org.springframework.web.bind.annotation.RequestMapping;  
  9. @Controller  
  10. public class LoginController {  
  11.     @RequestMapping("loginServlet")  
  12.     public String login(String userName,String password){  
  13.         Subject subject = SecurityUtils.getSubject();  
  14.         UsernamePasswordToken upt=new UsernamePasswordToken(userName,password);  
  15.         try {  
  16.             subject.login(upt);  
  17.             return "/query.jsp";  
  18.         } catch (AuthenticationException e) {  
  19.             return "redirect:/login.html";  
  20.         }  
  21.           
  22.     }  
  23. }  
添加一个springboot启动类 just run
[html]  view plain  copy
  1. package cn.et.boot;  
  2.   
  3. import org.springframework.boot.SpringApplication;  
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;  
  5.   
  6. @SpringBootApplication  
  7. public class Main {  
  8.   
  9.     public static void main(String[] args) {  
  10.         SpringApplication.run(Main.class, args);  
  11.     }  
  12.   
  13. }  
通过  http://localhost:8080/login.html 访问
 测试 输入错误的密码
 测试使用zs登录 访问 add.jsp发现跳转到un.jsp 

四。自定义filter实现动态配置url拦截

  如果在spring中配置静态的url拦截 添加一个新的路径 每次都需要修改配置文件 如果使用springboot 每次都需要修改urls这个map 这个map是初始化bean

的时候创建 后面再往里面加是没有效果的 所以需要在拦截时自己手工调用对应的filter逻辑 

[html]  view plain  copy
  1. @Bean  
  2.     public ShiroFilterFactoryBean shiroFilter(@Autowired org.apache.shiro.mgt.SecurityManager securityManager){  
  3.         ShiroFilterFactoryBean sffb=new ShiroFilterFactoryBean();  
  4.         sffb.setSecurityManager(securityManager);  
  5.         sffb.setLoginUrl("/login.html");  
  6.         sffb.setUnauthorizedUrl("/un.jsp");  
  7.         Map<String, String> urls=new HashMap<String, String>();  
  8.         /*  
  9.          *    定义url  
  10.             /login.html = anon  
  11.             /loginServlet = anon  
  12.             /query.jsp = authc  
  13.             /add.jsp = roles[role2]  
  14.          * */  
  15.           
  16.         urls.put("/login.html", "anon");  
  17.         urls.put("/loginServlet", "anon");  
  18.         urls.put("/query.jsp", "authc");  
  19.         urls.put("/add.jsp", "roles[role1]");  
  20.         sffb.setFilterChainDefinitionMap(urls);  
  21.         return sffb;  
  22.     }  
自定义过滤器的代码是 注意添加到spring容器中 添加@Component注解
[html]  view plain  copy
  1. package cn.et.conf;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5. import java.util.Set;  
  6. import java.util.regex.Pattern;  
  7.   
  8. import javax.servlet.Filter;  
  9. import javax.servlet.ServletRequest;  
  10. import javax.servlet.ServletResponse;  
  11. import javax.servlet.http.HttpServletRequest;  
  12.   
  13. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;  
  14. import org.apache.shiro.subject.Subject;  
  15. import org.apache.shiro.util.CollectionUtils;  
  16. import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;  
  17. import org.apache.shiro.web.filter.authz.AuthorizationFilter;  
  18. import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;  
  19. import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;  
  20. import org.springframework.beans.factory.annotation.Autowired;  
  21. import org.springframework.stereotype.Component;  
  22. @Component  
  23. public class MyFilter extends AuthorizationFilter {  
  24.     static Map<String, String> urls=new HashMap<String, String>();  
  25.       
  26.     @Autowired  
  27.     private ShiroFilterFactoryBean sffb;  
  28.     /**  
  29.      * 匹配指定过滤器规则的url  
  30.      * @param regex  
  31.      * @param url  
  32.      * @return  
  33.      */  
  34.     public static boolean matchUrl(String regex,String url){  
  35.         regex=regex.replaceAll("/+", "/");  
  36.         if(regex.equals(url)){  
  37.             return true;  
  38.         }  
  39.         regex=regex.replaceAll("\\.", "\\\\.");  
  40.         // /login.html  /l*.html  
  41.         regex=regex.replaceAll("\\*", ".*");  
  42.         // /**/login.html  /a/b/login.html  
  43.         if(regex.indexOf("/.*.*/")>=0){  
  44.             regex=regex.replaceAll("/\\.\\*\\.\\*/", "((/.*/)+|/)");  
  45.         }  
  46.         System.out.println(regex+"----"+url);  
  47.         return Pattern.matches(regex, url);  
  48.     }  
  49.     /**  
  50.      * 测试  
  51.      * @param args  
  52.      */  
  53.     public static void main(String[] args) {  
  54.         System.out.println(matchUrl("/**/s*.html","/t/g/login.html"));  
  55.     }  
  56.     /**  
  57.      * 在map中模拟 这个也可以将来定义在数据库中  
  58.      */  
  59.     static{  
  60.         urls.put("/login.html", "anon");  
  61.         urls.put("/loginBlog", "anon");  
  62.         urls.put("/un.jsp", "anon");  
  63.         urls.put("/queryBlogByToken", "anon");  
  64.         urls.put("/query.jsp", "authc");  
  65.         urls.put("/add.jsp", "roles[role1]");  
  66.     }  
  67.     /**  
  68.      * isAccessAllowed用于判断当前url的请求是否能验证通过  如果验证失败 调用父类的onAccessDenied决定跳转到登录失败页还是授权失败页面  
  69.      */  
  70.     @Override  
  71.     protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)  
  72.             throws Exception {  
  73.         HttpServletRequest req=(HttpServletRequest)request;  
  74.         String url=req.getRequestURI();  
  75.         //通过url获取授权类型  
  76.         String urlAuth=urls.get(url);  
  77.         if(urlAuth==null){  
  78.             return false;  
  79.         }  
  80.         //配置的过滤器是anon 直接放过  
  81.         if(urlAuth.startsWith("anon")){  
  82.             return true;  
  83.         }  
  84.         //配置的是authc 判断当前用户是否认证通过  
  85.         Subject subject = getSubject(request, response);  
  86.         if(urlAuth.startsWith("authc")){  
  87.             return subject.isAuthenticated();  
  88.         }  
  89.         //授权认证 也需要判断是否登录 没有登录返回 登录继续下面的验证  
  90.         boolean ifAuthc=subject.isAuthenticated();  
  91.         if(!ifAuthc)  
  92.             return ifAuthc;  
  93.         //如果是定义的roles过滤器  获取所有的roles 一般是roles[a,b]  
  94.         if(urlAuth.startsWith("roles")){  
  95.             String[] rolesArray=urlAuth.split("roles\\[")[1].split("\\]")[0].split(",");  
  96.             if (rolesArray == null || rolesArray.length == 0) {  
  97.                 return true;  
  98.             }  
  99.             Set<String> roles = CollectionUtils.asSet(rolesArray);  
  100.             return subject.hasAllRoles(roles);  
  101.         }  
  102.         if(urlAuth.startsWith("perms")){  
  103.             String[] perms=urlAuth.split("perms\\[")[1].split("\\]")[0].split(",");  
  104.             boolean isPermitted = true;  
  105.             if (perms != null && perms.length > 0) {  
  106.                 if (perms.length == 1) {  
  107.                     if (!subject.isPermitted(perms[0])) {  
  108.                         isPermitted = false;  
  109.                     }  
  110.                 } else {  
  111.                     if (!subject.isPermittedAll(perms)) {  
  112.                         isPermitted = false;  
  113.                     }  
  114.                 }  
  115.             }  
  116.   
  117.             return isPermitted;  
  118.         }  
  119.         return false;  
  120.     }  
  121.   
  122. }  
修改springboot的Config类 将所有的请求都交给自定义的过滤器处理
[html]  view plain  copy
  1. @Bean  
  2.     public ShiroFilterFactoryBean shiroFilter(@Autowired org.apache.shiro.mgt.SecurityManager securityManager){  
  3.         ShiroFilterFactoryBean sffb=new ShiroFilterFactoryBean();  
  4.         sffb.setSecurityManager(securityManager);  
  5.         sffb.setLoginUrl("/login.html");  
  6.         sffb.setUnauthorizedUrl("/un.jsp");  
  7.         Map<String, String> urls=new HashMap<String, String>();  
  8.         urls.put("/**", "myFilter");          
  9.         sffb.setFilterChainDefinitionMap(urls);  
  10.         return sffb;  
  11.     }  
添加一个过滤匹配的策略 定义多个relms 只要有一个realm的数据通过则通过 (这里只有一个realm不配置也可 留着做记录 将来怕要用)
[html]  view plain  copy
  1. /**  
  2.      * <span style="font-size:13.3333px;">多个relms 只要有一个realm的数据通过则通过</span>  
  3.      * @return  
  4.      */  
  5.     @Bean  
  6.     public ModularRealmAuthenticator authentictor(){  
  7.         org.apache.shiro.authc.pam.ModularRealmAuthenticator mra=new org.apache.shiro.authc.pam.ModularRealmAuthenticator();  
  8.         mra.setAuthenticationStrategy(new org.apache.shiro.authc.pam.FirstSuccessfulStrategy());  
  9.         return mra;  
  10.     }  
  11.     /**  
  12.      * 定義默認的securityManager  
  13.      * @return  
  14.      */  
  15.     @Bean  
  16.     public DefaultWebSecurityManager securityManager(@Autowired Realm myRealm,@Autowired ModularRealmAuthenticator authentictor){  
  17.         DefaultWebSecurityManager dwm=new DefaultWebSecurityManager();  
  18.         dwm.setRealm(myRealm);  
  19.         authentictor.setRealms(dwm.getRealms());  
  20.         dwm.setAuthenticator(authentictor);  
  21.         return dwm;  
  22.     }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值