当前位置:   article > 正文

权限控制框架-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)

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

添加realm的实现类

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

  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文件路径配置参数

  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添加用于登录的表单

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

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

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

<link rel="icon" href="data:image/ico;base64,aWNv">
登录实现

  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. }

三。 shiro集成springboot

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

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

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

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

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

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


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/509759
推荐阅读
相关标签
  

闽ICP备14008679号