Shiro权限管理2.Shiro的HelloWorld程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shiro权限管理2.Shiro的HelloWorld程序相关的知识,希望对你有一定的参考价值。


下面来分析一个Shiro的HelloWorld程序。这段程序不是我们自己写的,而是Shiro给我们提供的一个样例代码。通过这段代码我们可以看到Shiro的大致结构。



首先通过http://shiro.apache.org/download.html下载Shiro的jar包:


【Shiro权限管理】2.Shiro的HelloWorld程序_SecurityUtils


目前Shiro的最新版本为1.3.2版本,其中包括shiro-all、shiro-core、shiro-web、shiro-aspectj、shiro-cas、shiro-ehcache、shiro-hazelcast、shiro-guice、shiro-quartz、

shiro-spring、 shiro-tools-hasher这11个jar包。


将上述jar下载完毕解压后看到的大致文件结构如下:


【Shiro权限管理】2.Shiro的HelloWorld程序_shiro_02



测试工程还需要log4j-1.2.17.jar、slf4j-api-1.7.5.jar以及slf4j-log4j12-1.7.5.jar三个jar包,是用来管理日志信息打印的。



下面在MyEclipse中新建一个名为“Shiro1”的Java工程:


【Shiro权限管理】2.Shiro的HelloWorld程序_securityManager_03


新建一个lib文件夹,加入shiro-all-1.3.2.jar、log4j-1.2.17.jar、slf4j-api-1.7.5.jar以及slf4j-log4j12-1.7.5.jar四个jar包,并加载到类路径下:


【Shiro权限管理】2.Shiro的HelloWorld程序_shiro_04



Shiro为我们提供的样例程序托管在GitHub下,地址为https://github.com/apache/shiro.git,


我们可以克隆到本地Git或者直接下载:


【Shiro权限管理】2.Shiro的HelloWorld程序_realm_05


这里先选择直接下载。下载完毕后的工程目录如下:


【Shiro权限管理】2.Shiro的HelloWorld程序_SecurityUtils_06


我们需要的样例程序在“shiro-master\\samples”下,名称为“quickstart”:


【Shiro权限管理】2.Shiro的HelloWorld程序_securityManager_07



我们将“quickstart”下的“src\\main\\resources”下的配置文件加入到测试工程“Shiro1”的src下,将“src\\main\\java”下的Quickstart.java加入创建的“com.test.shiro.helloWorld”包下:


【Shiro权限管理】2.Shiro的HelloWorld程序_SecurityUtils_08


如果看到java文件上面有打叉,是因为没有写包路径,打开文件添加包路径:


【Shiro权限管理】2.Shiro的HelloWorld程序_realm_09



下面我们来分块剖析Quickstart.java的样例代码(main方法中),在重要的地方我将原版的注释翻译了一下:


(1)创建SecurityManager部分

//下面是创建一个SecurityManager最简单的方式,使用了ini文件(shiro.ini)中配置的
//realms, users, roles 以及 permissions。我们使用了工程的模式,加载ini文件中
//的配置,进而创建出了一个SecurityManager实例。
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();

上面所指的Shiro.ini文件就是之前我们加入到测试工程的文件,其中核心的配置数据如下:


[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
lonestarr = vespa, goodguy, schwartz
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5

上面配置了root用户、guest用户、presidentskroob用户、lonestarr用户的密码以及角色,下面配置了admin以及schwartz和goodguy这三个角色的权限信息。



上面创建SecurityManager的方式以及ini的配置,仅作为测试样例的参考,在开发中不可能使用这种方式进行开发,这里大家仅作为了解即可。



(2)securityManager的一些设置

//对于这个简单的例子,我们使SecurityManager在Java虚拟机中变可访问的“单例”,


//但大部分应用都不这么做。这里为了体验快速工程,才使用这种最小开销的方式。


SecurityUtils.setSecurityManager(securityManager);


上面就是一个单例设置,日常开发中也很少这么做,这里仅作了解即可。



(3)使用Shiro的核心API

1.获取当前用户的Subject


//获取当前的Subject
Subject currentUser = SecurityUtils.getSubject();

上面的代码就是使用SecurityUtils的getSubject获取当前用户的Subject。上一此我们说到在Shiro中与SecurityManager打交道的就是Subject。



2.使用Session


//测试使用Session(即便不是Web或EJB容器下)
Session session = currentUser.getSession();//调用Subject的getSession方法
session.setAttribute("someKey", "aValue");//存入Key-Value对象
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue"))
log.info("Retrieved the correct value! [" + value + "]");

上面的代码通过调用Subject的getSession方法获取session对象来使用。



3.进行认证(登录)


//测试当前用户是否已经被认证(即是否已经登录)
if (!currentUser.isAuthenticated())
//将用户名与密码封装为UsernamePasswordToken对象
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);//记录用户
try
currentUser.login(token);//调用Subject的login方法执行登录
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?

上面的代码去校验用户是否被认证,如果没有被认证,则会将用户的账号密码封装为。UsernamePasswordToken对象,并传入Subject的login方法执行登录。如果账号密码正确,则通过认证并登录成功,如果账户没有,则会抛出UnknownAccountException异常;如果账户存在但密码错误,则会抛出IncorrectCredentialsException异常;如果用户被锁定,则会抛出LockedAccountException 异常;最后的AuthenticationException是上面所有异常的父类,在不清楚会发生何种异常时,可以直接抛出该类异常。



4.进行角色判定


//测试用户是否有某一个角色
if (currentUser.hasRole("schwartz"))
log.info("May the Schwartz be with you!");
else
log.info("Hello, mere mortal.");

通过Subject的hasRole方法来判定该用户是否含有指定的橘色。对于该样例,ini文件中配置的lonestarr用户是有“schwartz”这个角色的,所以控制台会输出“May the Schwartz be with you!”。




5.进行权限判定

//测试用户是否具备某个行为
if (currentUser.isPermitted("lightsaber:wield"))
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 arent allowed to drive the eagle5 winnebago!");

一般在系统的权限设置中,一个用户对应一个或多个角色,而一个角色下又会拥有许多个权限,来指定该角色的行为范围。这里使用Subject的isPermitted方法来判定用户是否具备某个类型下的某些行为权限。在该样例中,第一个判断就是判定用户在lightsaber类型下是否拥有wield行为权限,而在上面的ini配置文件中我们可以看到,该用户的“schwartz”角色的权限指定为“schwartz = lightsaber:*”,即是对于lightsaber类型可以执行所有行为,所以这里的wield行为是可以通过的。


后面的"winnebago:drive:eagle5"更为具体,即是“类型:行为:实例”,指可以对某类型的谋实例做某事。换一个更好理解的配置,如“user:delete:zhangsan”,即在user模块下可以对zhangsan来进行delete操作。



6.用户登出(注销)


//执行登出(注销)
currentUser.logout();

使用Subject的logout方法来执行登出操作,一般在应用中来使用户退出系统。

上面就是整个Shiro的测试样例代码的讲解。在上面的代码中,只有登录和登出的写法使我们可以在项目中直接使用的,而权限判定等一般在项目中使用声明(注解、配置)的方式来进行判定,不会使用样例程序的硬编码方式判定。

最后,执行以下main方法,观察控制台打印的日志信息(这里只列出INFO级别的日志,也就是mian中自主打印的日志),大家自己可以思考一下为什么是打印该结果:

2017-10-14 12:55:23,466 INFO [com.test.shiro.helloWorld.Quickstart] - Retrieved the correct value! [aValue] 
2017-10-14 12:55:23,469 INFO [com.test.shiro.helloWorld.Quickstart] - User [lonestarr] logged in successfully.
2017-10-14 12:55:23,469 INFO [com.test.shiro.helloWorld.Quickstart] - May the Schwartz be with you!
2017-10-14 12:55:23,470 INFO [com.test.shiro.helloWorld.Quickstart] - You may use a lightsaber ring. Use it wisely.
2017-10-14 12:55:23,470 INFO [com.test.shiro.helloWorld.Quickstart] - You are permitted to drive the winnebago with license plate (id) eagle5. Here are the keys - have fun!

Shiro+JWT 实现权限管理--Shiro

在之前的文章《权限框架Apache Shiro 和 Spring Security》中有介绍一些权限框架,当时觉得Shiro功能比较强大,也比较适合管理后台不同粒度的权限控制.

再后来的工作过程中,发现了shiro的一些不足之处--对于多端登录支持不是很友好,需要自己去实现具体功能.

经过搜索、研究发现一个新东西--JWT (JAVA WEB TOKEN),为了在网络应用环境声明而执行的一种基于 JSON 的开放标准.这玩意特别适用于分布式站点的单点登录.至于为什么选用Token,可以看下这篇文章《shiro使用(使用token预热,为什么要使用token)》

在大致研究了JWT功能后,决定对之前写的权限管理做一些优化--使用JWT做认证,配合Shrio强大的授权功能,实现更加全面、轻量化的权限管理.

之前那篇文章只写了各框架的对比,并没有详细的介绍.正好写篇文章介绍一下Shiro .But 该文只做概念介绍后面再做贴出代码吧.

 

Shiro

官方文档地址:http://shiro.apache.org/architecture.html

Shiro核心架构概念  

技术图片

 

介绍一下shiro需要关注的几个概念

SecurityManager  shiro框架的核心  shiro通过securityManager来管理内部主键实例,并通过它来提供安全管理的各种服务.

Subject  当前操作用户--请求主体  在程序中任何地方都可以使用SecurityUtils.getSubjcet()获取当前的subject   然后利用subject.getPrincipal()获取principal--subject的标示,程序通过principal来识别唯一用户,可以是定义的toekn,也可以是用户id等.

Realms  充当shiro与应用安全数据间的桥梁和连接器,当对用户执行认证和授权验证时,shiro会从应用配置的realm中查找用户及其权限信息. 当有多个realm存在时,shiro在做用户校验的时候会按照决策来决定认证是否通过.一个realm对应一个matcher用来做用户提交认证信息和realm获取的用户信息做对比.

Shiro的功能架构图

技术图片

Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被称之为应用安全的四大基石。

Authentication 用户身份识别、判断  解决--我是我

Authorization 访问权限控制  解决--我能做什么

Session Management  会话管理 支持特异性的管理

Cryptography  对数据源进行加密同时保证轻易访问

还有其他的功能支持不同应用环境下的权限安全控制,可以根据特异需求进行丰富和加强.

 

shiro认证与授权 

参考 https://www.jianshu.com/p/504af828a3ab

认证流程

技术图片

  • 调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
  • SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
  • Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
  • Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
  • Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

授权流程

技术图片

  • 首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer;
  • Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
  • 在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
  • Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。

 

以上是关于Shiro权限管理2.Shiro的HelloWorld程序的主要内容,如果未能解决你的问题,请参考以下文章

spring boot 2 + shiro 实现权限管理

shiro权限框架

shiro权限框架

shiro权限框架

Shiro学习

Shiro 安全框架详解一(概念+登录案例实现)