设为首页 收藏本站
查看: 816|回复: 0

[经验分享] Apache Shiro 示例

[复制链接]

尚未签到

发表于 2016-12-30 07:36:43 | 显示全部楼层 |阅读模式
  第一个Apache Shiro程序
  
  如果你是初次接触Apache Shiro,该文章将指导你创建一个初级的非常简单的使用Apache Shiro进行安全认证的程序,同时我们将讨论Shiro的核心理念以帮助你熟悉Shiro的设计方式和API
  
  如果你确实不想按照该示例一步一步地编写代码,你可以从下面地址下载一个基本上完全相同的程序作为参考,选择下载位置:
   Apache Shiro的版本库中: https://svn.apache.org/repos/asf/shiro/trunk/samples/quickstart/
   Apache Shiro的源码发布的samples/quickstart目录中,源码发布在 Downloadhttp://shiro.apache.org/download.html)页面。
  
  Setup
  在这个简单示例中,我们将建立一个非常简单的命令行程序,你可以从中感受一下ShiroAPI
  注意:任何程序
  Apache Shrio从设计之初就是为了支持所有程序--从最小的命令行程序到大型的集群的web程序,虽然我们在这个向导中只使用了一个简单的程序,但要知道无论你的程序如何创建发布到何处,这种方式都适用。
  
  该示例需要Java1.5及更高版本,同时用Apache Maven作为建造工具,但这不是Apache Shiro所必须的。你可以获得Shirojar并以你喜欢的任何方式将其加入你的程序中,例如你可以使用antivy
  在该示例中,请确定你使用的是Maven2.2.1或更高版本,你可以在命令窗口中执行“mvn --version”看到和下面类似的输出:
  
  Testing Maven Installation
  hazlewood:~/shiro-tutorial$ mvn --version
  Apache Maven 2.2.1 (r801777; 2009-08-06 12:16:01-0700)
  Java version: 1.6.0_24
  Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
  Default locale: en_US, platform encoding: MacRoman
  OS name: "mac os x" version: "10.6.7" arch: "x86_64" Family: "mac"
  
  现在,创建一个新的目录,例如shiro-tutorial 并将下面的Mavenpom.xml文件保存在同一目录下:
  pom.xml
  <?xml version="1.0" encoding="UTF-8"?>
  <project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.shiro.tutorials</groupId>
  <artifactId>shiro-tutorial</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <name>First Apache Shiro Application</name>
  <packaging>jar</packaging>
  
  <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <build>
  <plugins>
  <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>2.0.2</version>
  <configuration>
  <source>1.5</source>
  <target>1.5</target>
  <encoding>${project.build.sourceEncoding}</encoding>
  </configuration>
  </plugin>
  
  <!-- This plugin is only to test run our little application. It is not
  needed in most Shiro-enabled applications: -->
  <plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.1</version>
  <executions>
  <execution>
  <goals>
  <goal>java</goal>
  </goals>
  </execution>
  </executions>
  <configuration>
  <classpathScope>test</classpathScope>
  <mainClass>Tutorial</mainClass>
  </configuration>
  </plugin>
  </plugins>
  </build>
  
  <dependencies>
  <dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.1.0</version>
  </dependency>
  <!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding
  in this example app. See http://www.slf4j.org for more info. -->
  <dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.6.1</version>
  z <scope>test</scope>
  </dependency>
  </dependencies>
  
  </project>
  
  示例类文件
  我们准备运行的是一个命令行程序,所以我们需要创建一个带有public static void main(String[] args)函数的Java类。
  在pom.xml同目录里,创建一个src/main/java子目录,在该子目录里创建一个Tutorial.java文件,内容如下:
  src/main/java/Tutorial.java
  import org.apache.shiro.SecurityUtils;
  import org.apache.shiro.authc.*;
  import org.apache.shiro.config.IniSecurityManagerFactory;
  import org.apache.shiro.mgt.SecurityManager;
  import org.apache.shiro.session.Session;
  import org.apache.shiro.subject.Subject;
  import org.apache.shiro.util.Factory;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class Tutorial {
  
  private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
  
  public static void main(String[] args) {
  log.info("My First Apache Shiro Application");
  System.exit(0);
  }
  }
  
  先不要考虑里面的import,我们马上就会获取它们,现在,我们已经有了一个典型的命令行程序'shell',这个程序所做的事情就是输出文字“My First Apache Shiro Application”然后退出。
  
  运行
  在你的该示例项目根目录里(例如shiro-tutorial)执行下面的命令行来运行程序:
  mvn compile exec:java
  你可以看到我们的这个小程序运行并且退出,你将看到类似于下面这样的输出住处(注意加粗字体,标示着我们的输出)
  Run the Application
  lhazlewood:~/projects/shiro-tutorial$ mvn compile exec:java
  
  ... a bunch of Maven output ...
  
  1 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
  lhazlewood:~/projects/shiro-tutorial\$
  
  我们验证程序已经成功运行--现在,让我们加上Apache Shiro,今后,你可以在我们加上任何代码之后运行mvn compile exec:java命令查看我们更改的结果。
  
  使用Shiro
  使用shiro要理解的第一件事情是shiro几乎所有的事情都和一个中心组件SecurityManager有关,对于那些熟悉Java security的人请注意:这和java.lang.SecurityManager不是一回事。
  
  我们将在Architecture章节详细描述shiro的设计,但现在有必要知道Shrio SecurityManager是程序中Shiro的核心,每一个程序都必定会存在一个SecurityManager,所以,在我们这个示例程序中必须做的第一件事情是建立一个SecurityManager实例。
  
  配置
  虽然我们可以直接对SecurityManager实例化,但在Java代码中对ShiroSecurityManager所须的选项和内部组件进行配置会让人感觉有点小痛苦--而将这些SecurityManager配置用一个灵活的配置文件实现就会简单地多。
  为此,Shiro默认提供了一个基本的INI配置文件的解决方案,人们已经对庞大的XML文件有些厌倦了,而一个INI文件易读易用,而且所依赖的组件很少,稍后你就会通过一个简单易懂的示例明白INI在对简单对象进行配置的时候是非常有效率的,比如SecurityManager
  
  多种配置选择
  ShiroSecurityManager的实现和其所依赖的组件都是JavaBean,所以可以用多种形式对Shiro进行配置,比如XMLSpring,JBoss, Guice, 等等),YAML, JSON, Groovy Builder markup,及其它,INI只是Shiro一种最基本的配置方式,使得其可以在任何环境中进行配置比如在那些没有以上配置形式的环境中。
  
  shiro.ini
  在这个示例中我们使用一个INI文件来配置Shiro SecurityManager,首先,在pom.xml同目录中创建一个src/main/resources子目录,在该子目录中创建一个shiro.ini文件,内容如下:
  src/main/resources/shiro.ini
  # =============================================================================
  # Tutorial INI configuration
  #
  # Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
  # =============================================================================
  
  # -----------------------------------------------------------------------------
  # Users and their (optional) assigned roles
  # username = password, role1, role2, ..., roleN
  # -----------------------------------------------------------------------------
  [users]
  root = secret, admin
  guest = guest, guest
  presidentskroob = 12345, president
  darkhelmet = ludicrousspeed, darklord, schwartz
  lonestarr = vespa, goodguy, schwartz
  
  # -----------------------------------------------------------------------------
  # Roles with assigned permissions
  # roleName = perm1, perm2, ..., permN
  # -----------------------------------------------------------------------------
  [roles]
  admin = *
  schwartz = lightsaber:*
  goodguy = winnebago:drive:eagle5
  
  可以看到,在该配置文件中最基础地配置了几个静态的帐户,对我们这一个程序已经足够了,在以后的章节中,将会看到如何使用更复杂的用户数据比如数据库、LDAP和活动目录等。
  
  引用配置文件
  现在我们已经定义了一个INI文件,我们可以在我们的示例程序中创建SecurityManager实例了,将main函数中的代码进行如下调整:
  public static void main(String[] args) {
  
  log.info("My First Apache Shiro Application");
  
  //1.
  Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
  
  //2.
  SecurityManager securityManager = factory.getInstance();
  
  //3.
  SecurityUtils.setSecurityManager(securityManager);
  
  System.exit(0);
  }
  
  这就是我们要做的--仅仅使用三行代码就把Shiro加进了我们的程序,就是这么简单。
  
  执行mvn compile exec:java 可以看到程序成功的运行(由于Shiro默认在debug或更底层才记录日志,所以你不会看到任何Shiro的日志住处--只要运行时没有错误提示,你就可以知道已经成功了)。
  
  上面所加入的代码做了下面的事情:
  1. 使用ShiroIniSecurityManagerFactory加载了我们的shiro.ini文件,该文件存在于classpath根目录里。这个执行动作反映出shiro支持FactoryMethod Design Patternclasspath:资源的指示前缀,告诉shiro从哪里加载ini文件(其它前缀,如url:file:也被支持)。
  2.factory.getInstance()方法被调用,该方法分析INI文件并根据配置文件返回一个SecurityManager实例。
  3.在这个简单示例中,我们将SecurityManager设置成了static (memory) singleton,可以通过JVM访问,注意如果你在一个JVM中加载多个使用shiro的程序时不要这样做,在这个简单示例中,这是可以的,但在其它成熟的应用环境中,通常会将SecurityManager放在程序指定的memory(如在web中的ServletContexct或者SpringGuiceJBoss DI 容器实例)中。
  
  使用Shiro
  现在我们的SecurityManager已经准备好了,我们可以开始进行我们真正关心的事情--执行安全操作了。
  
  为了保护我们的程序安全,我们或许问自己最多的问题就是“谁是当前的用户?”或者“当前用户是否允许做某件事?”通常我们会在写代码或者设计用户接口的时候问这些问题:程序通常建立在用户基础上,程序功能展示(和安全)也基于每一个用户。所以,通常我们考虑我们程序安全的方法也建立在当前用户的基础上,ShiroAPI提供了'thecurrent user'概念,即它的Subject
  在几乎所有的环境中,你可以通过如下语句得到当前用户的信息:
  Subject currentUser = SecurityUtils.getSubject();
  
  使用SecurityUtils.getSubject(),我们可以获取当前执行的SubjectSubject是一个安全术语意思是“当前运行用户的指定安全视图(a security-specificview of the currently executing user)”,这里并不称之为“User”因为“User”这个词通常和一个人相关,但在安全认证中,“Subject”可以认为是一个人,也可以认为是第三方进程、时钟守护任务、守护进程帐户或者其它。它可简单描述为“当前和软件进行交互的事件”,在大多数情况下,你可以认为它是一个“人(User)”。
  
  在一个独立的程序中调用getSubject()会在程序指定位置返回一个基于用户数据的Subject,在服务器环境(如web程序)中,它将获取一个和当前线程或请求相关的基于用户数据的Subject
  现在你得到了Subject,你可以利用它做什么呢?
  如果你针对该用户希望一些事情在程序当前会话期内可行,你可以获取他们的session
  Session session = currentUser.getSession();
  session.setAttribute( "someKey", "aValue" );
  
  Sessionshiro指定的一个实例,提供基本上所有HttpSession的功能,但具备额外的好处和不同:它不需要一个HTTP环境!
  
  如果发布到一个web程序中,默认情况下Session将会使用HttpSession作为基础,但是,在一个非web程序中,比如该简单示例程序中,Shiro将自动默认使用它的EnterpriseSession Management,这意味着你可以在任何程序中使用相同的API,而根本不需要考虑发布环境!这打开了一个全新的世界,从此任何需要session的程序不再需要强制使用HttpSession或者EJBStateful Session,并且,终端可以共享session数据。
  
  现在你可以获取一个Subject和它们的Session,真正填充有用的代码如检测其是否被允许做某些事情如何?比如检查其角色和权限?
  
  我们只能对一个已知用户做这些检测,如上我们获取Subject实例表示当前用户,但是当前用户是认证,嗯,他们是任何人--直到他们至少登录一次,我们现在就做这件事情:
  if ( !currentUser.isAuthenticated() ) {
  //collect user principals and credentials in a gui specific manner
  //such as username/password html form, X509 certificate, OpenID, etc.
  //We'll use the username/password example here since it is the most common.
  UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
  
  //this is all you have to do to support 'remember me' (no config - built in!):
  token.setRememberMe(true);
  
  currentUser.login(token);
  }
  
  就是这样,不能再简单了。
  但如果登录失败了呢,你可以捕获所有异常然后按你期望的方式去处理:
  try {
  currentUser.login( token );
  //if no exception, that's it, we're done!
  } catch ( UnknownAccountException uae ) {
  //username wasn't in the system, show them an error message?
  } catch ( IncorrectCredentialsException ice ) {
  //password didn't match, try again?
  } catch ( LockedAccountException lae ) {
  //account for that username is locked - can't login. Show them a message?
  }
  ... more types exceptions to check if you want ...
  } catch ( AuthenticationException ae ) {
  //unexpected condition - error?
  }
  
  这里有许多不同类别的异常你可以检测到,也可以抛出你自己异常。
  
  小贴士:
  最好的方式是将普通的失败信息反馈给用户,你总不会希望帮助黑客来攻击你的系统吧。
  
  好,到现在为止,我们有了一个登录用户,接下来我们还可以做什么?
  
  让我们显示他们是谁:
  //print their identifying principal (in this case, a username):
  log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );
  
  我们也可以判断他们是否拥有某个特定动作或入口的权限:
  if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
  log.info("You may use a lightsaber ring. Use it wisely.");
  } else {
  log.info("Sorry, lightsaber rings are for schwartz masters only.");
  }
  
  同样,我们还可以执行非常强大的实例级别的权限检测,检测用户是否具备访问某个类型特定实例的权限:
  if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
  log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " +
  "Here are the keys - have fun!");
  } else {
  log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
  }
  
  非常容易的事,对吗?
  
  最后,当用记不再使用系统,可以退出登录:
  currentUser.logout(); //removes all identifying information and invalidates their session too.
  
  最终代码
  在加入上述代码后,下面的就是我们完整的文件,你可以自由编辑和运行它,可以尝试改变安全检测(以及INI配置):
  Final src/main/java/Tutorial.java
  import org.apache.shiro.SecurityUtils;
  import org.apache.shiro.authc.*;
  import org.apache.shiro.config.IniSecurityManagerFactory;
  import org.apache.shiro.mgt.SecurityManager;
  import org.apache.shiro.session.Session;
  import org.apache.shiro.subject.Subject;
  import org.apache.shiro.util.Factory;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class Tutorial {
  
  private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
  
  
  public static void main(String[] args) {
  log.info("My First Apache Shiro Application");
  
  Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
  SecurityManager securityManager = factory.getInstance();
   SecurityUtils.setSecurityManager(securityManager);
  
  
  // get the currently executing user:
  Subject currentUser = SecurityUtils.getSubject();
  
  // Do some stuff with a Session (no need for a web or EJB container!!!)
  Session session = currentUser.getSession();
  session.setAttribute("someKey", "aValue");
  String value = (String) session.getAttribute("someKey");
  if (value.equals("aValue")) {
  log.info("Retrieved the correct value! [" + value + "]");
  }
  
  // let's login the current user so we can check against roles and permissions:
  if (!currentUser.isAuthenticated()) {
  UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
   token.setRememberMe(true);
  try {
  currentUser.login(token);
  } catch (UnknownAccountException uae) {
  log.info("There is no user with username of " + token.getPrincipal());
  } catch (IncorrectCredentialsException ice) {
  log.info("Password for account " + token.getPrincipal() + " was incorrect!");
  } catch (LockedAccountException lae) {
  log.info("The account for username " + token.getPrincipal() + " is locked. " +
  "Please contact your administrator to unlock it.");
  }
  // ... catch more exceptions here (maybe custom ones specific to your application?
  catch (AuthenticationException ae) {
  //unexpected condition? error?
  }
  }
  
  //say who they are:
  //print their identifying principal (in this case, a username):
  log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
  
  //test a role:
  if (currentUser.hasRole("schwartz")) {
  log.info("May the Schwartz be with you!");
  } else {
  log.info("Hello, mere mortal.");
  }
  
  //test a typed permission (not instance-level)
  if (currentUser.isPermitted("lightsaber:weild")) {
  log.info("You may use a lightsaber ring. Use it wisely.");
  } else {
  log.info("Sorry, lightsaber rings are for schwartz masters only.");
  }
  
  //a (very powerful) Instance Level permission:
  if (currentUser.isPermitted("winnebago:drive:eagle5")) {
  log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
  "Here are the keys - have fun!");
  } else {
  log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
  }
  
  //all done - log out!
  currentUser.logout();
  
  System.exit(0);
  }
  }
  
  总结
  非常希望这示例介绍能帮助你理解如何在基础程序中加入Shiro,并理解Shiro的设计理念,SubjectSecurityManager
  但这个程序太简单了,你可能会问自己,“如果我不想使用INI用户帐号,而希望连接更为复杂的用户数据源呢?”
  解决这些问题需要更深入地了解shiro的架构和配置机制,我们将在下一节Architecture中介绍。
  原文地址:http://shiro.apache.org/tutorial.html

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-321252-1-1.html 上篇帖子: apache负载均衡 下篇帖子: apache 模块编写
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表