授权:也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。

主体:即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。

资源:在应用中用户可以访问的 URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

权限:安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源。

角色:代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。

隐式角色:即直接通过角色来验证用户有没有操作权限,如在应用中CTO、技术总监、开发工程师可以使用打印机,假设某天不允许开发工程师使用打印机,此时需要从应用中删除相应代码;再如在应用中CTO、技术总监可以查看用户、查看权限;突然有一天不允许技术总监查看用户、查看权限了,需要在相关代码中把技术总监角色从判断逻辑中删除掉;即粒度是以角色为单位进行访问控制的,粒度较粗;如果进行修改可能造成多处代码修改。

显示角色:在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设哪个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无须修改多处代码;即粒度是以资源/实例为单位的;粒度较细。

Shiro 支持三种方式的授权:

编程式:通过写if/else授权代码块完成:

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}

注解式:通过在执行的Java方法上放置相应的注解完成:

@RequiresRoles("admin")
public void hello() {
//有权限
}

JSP/GSP标签:在JSP/GSP页面通过相应的标签完成:

<shiro:hasRole name="admin">
<!— 有权限 —>
</shiro:hasRole>

基于角色的访问控制(隐式角色)

1、在ini配置文件配置用户拥有的角色(shiro-role.ini) 

[users]
zhang=123,role1,role2
wang=123,role1

规则即:“用户名=密码,角色1,角色2”,如果需要在应用中判断用户是否有相应角色,就需要在相应的Realm中返回角色信息,也就是说Shiro不负责维护用户-角色信息,需要应用提供,Shiro只是提供相应的接口方便验证,后续会介绍如何动态的获取用户角色。

2、测试用例

//Shiro提供了用于判断用户是否拥有某个角色的方法;
@Test
public void testHasRole() {
login("classpath:shiro-role.ini", "zhang", "123");
//判断拥有角色:role1
Assert.assertTrue(subject().hasRole("role1"));
//判断拥有角色:role1 and role2
Assert.assertTrue(subject().hasAllRoles(Arrays.asList("role1", "role2")));
//判断拥有角色:role1 and role2 and !role3
boolean[] result = subject().hasRoles(Arrays.asList("role1", "role2", "role3"));
Assert.assertEquals(true, result[0]);
Assert.assertEquals(true, result[1]);
Assert.assertEquals(false, result[2]);
}
//Shiro也提供了用于断言用户是否拥有某个角色的方法;
@Test
public void testCheckRole() {
login("classpath:shiro-role.ini", "zhang", "123");
//断言拥有角色:role1
subject().checkRole("role1");
//断言拥有角色:role1 and role3 失败抛出异常
subject().checkRoles("role1", "role3");
}

基于资源的访问控制(显示角色)

1、在ini配置文件配置用户拥有的角色及角色-权限关系(shiro-permission.ini) 

[users]
zhang=123,role1,role2
wang=123,role1
[roles]
role1=user:create,user:update
role2=user:create,user:delete

规则:“用户名=密码,角色1,角色2”“角色=权限1,权限2”,即首先根据用户名找到角色,然后根据角色再找到权限;即角色是权限集合;Shiro同样不进行权限的维护,需要我们通过Realm返回相应的权限信息。只需要维护“用户——角色”之间的关系即可。

2、测试用例

//Shiro提供了用于判断用户是否拥有某个权限或所有权限
@Test
public void testIsPermitted() {
login("classpath:shiro-permission.ini", "zhang", "123");
//判断拥有权限:user:create
Assert.assertTrue(subject().isPermitted("user:create"));
//判断拥有权限:user:update and user:delete
Assert.assertTrue(subject().isPermittedAll("user:update", "user:delete"));
//判断没有权限:user:view
Assert.assertFalse(subject().isPermitted("user:view"));
}
//Shiro提供了用于断言用户是否拥有某个权限或所有权限
@Test(expected = UnauthorizedException.class)
public void testCheckPermission () {
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");
}

字符串通配符权限

规则:“资源标识符:操作:对象实例ID”  即对哪个资源的哪个实例可以进行什么操作。其默认支持通配符权限字符串,“:”表示资源/操作/实例的分割;“,”表示操作的分割;“*”表示任意资源/操作/实例。

1、单个资源单个权限

subject().checkPermissions("system:user:update");  

用户拥有资源“system:user”的“update”权限。

2、单个资源多个权限

ini配置文件 

role4=system:user:update,system:user:delete 

然后通过如下代码判断

subject().checkPermissions("system:user:update","system:user:delete");  

用户拥有资源“system:user”的“update”和“delete”权限。如上可以简写成:

role4="system:user:update,delete"    

3、单个资源全部权限

ini配置文件 

role51="system:user:create,update,delete,view"  

然后通过如下代码判断

subject().checkPermissions("system:user:create,delete,update:view");  

用户拥有资源“system:user”的“create”、“update”、“delete”和“view”所有权限。如上可以简写成:

或者:role52=system:user:*
或者:role53=system:user

然后通过如下代码判断

subject().checkPermissions("system:user:*");
subject().checkPermissions("system:user");

4、所有资源全部权限

ini配置文件 

role61=*:view  

然后通过如下代码判断

subject().checkPermissions("user:view");  

用户拥有所有资源的“view”所有权限。假设判断的权限是“"system:user:view”,那么需要“role5=*:*:view”这样写才行。

5、实例级别的权限

5.1、单个实例单个权限

ini配置 文件

role71=user:view:1  

对资源user的1实例拥有view权限。

然后通过如下代码判断

subject().checkPermissions("user:view:1");  

5.2单个实例多个权限

ini配置 文件

role72=user:update,delete:1  

对资源user的1实例拥有update、delete权限。

然后通过如下代码判断

subject().checkPermissions("user:update,delete:1 ");
subject().checkPermissions("user:update:1,user:delete:1 ");

5.3单个实例所有权限

ini配置 文件

role71=user:*:1  

对资源user的1实例拥有view权限。

然后通过如下代码判断

subject().checkPermissions("user:view,update,delete,create:1 ");

5.4、所有实例单个权限

ini配置 文件

role74=user:auth:*  

对资源user的1实例拥有所有权限。

然后通过如下代码判断

subject().checkPermissions("user:auth:1", "user:auth:2"); 

5.5、所有实例所有权限

ini配置 文件

role74=user:*:*  

对资源user的1实例拥有所有权限。

然后通过如下代码判断

subject().checkPermissions("user:view:1", "user:auth:2");  

6、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”,即后缀匹配必须指定前缀(多个冒号就需要多个*来匹配)。

7、WildcardPermission

如下两种方式是等价的:

subject().checkPermission("menu:view:1");
subject().checkPermission(new WildcardPermission("menu:view:1"));

授权流程

流程如下:

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,重写doGetAuthorizationInfo

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。

3.5 Authorizer、PermissionResolver及RolePermissionResolver

Authorizer的职责是进行授权(访问控制),是Shiro API中授权核心的入口点,其提供了相应的角色/权限判断接口,具体请参考其Javadoc。SecurityManager继承了Authorizer接口,且提供了ModularRealmAuthorizer用于多Realm时的授权匹配。PermissionResolver用于解析权限字符串到Permission实例,而RolePermissionResolver用于根据角色解析相应的权限集合。

我们可以通过如下ini配置更改Authorizer实现:

authorizer=org.apache.shiro.authz.ModularRealmAuthorizer
securityManager.authorizer=$authorizer

对于ModularRealmAuthorizer,相应的AuthorizingSecurityManager会在初始化完成后自动将相应的realm设置进去,我们也可以通过调用其setRealms()方法进行设置。对于实现自己的authorizer可以参考ModularRealmAuthorizer实现即可,在此就不提供示例了。

设置ModularRealmAuthorizer的permissionResolver,其会自动设置到相应的Realm上(其实现了PermissionResolverAware接口),如:

permissionResolver=org.apache.shiro.authz.permission.WildcardPermissionResolver
authorizer.permissionResolver=$permissionResolver

设置ModularRealmAuthorizer的rolePermissionResolver,其会自动设置到相应的Realm上(其实现了RolePermissionResolverAware接口),如:

rolePermissionResolver=com.github.zhangkaitao.shiro.chapter3.permission.MyRolePermissionResolver
authorizer.rolePermissionResolver=$rolePermissionResolver
    @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//1. 从 PrincipalCollection 中来获取登录用户的信息
Object principal = principals.getPrimaryPrincipal();
//2. 利用登录的用户的信息来查询当前用户的角色或权限(可能需要查询数据库)
Set<String> roles = new HashSet<String>();
//3. 创建 SimpleAuthorizationInfo, 并设置其 reles 属性.
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//4.设置当前用户的权限
Set<String> permissions = new HashSet<String>();
info.setStringPermissions(permissions);
//5.返回 SimpleAuthorizationInfo 对象.
return info;
}

Shiro 常用注解:

@RequiresAuthentication

验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时。

@RequiresUser

验证用户是否被记忆,user有两种含义:

一种是成功登录的(subject.isAuthenticated() 结果为true);

另外一种是被记忆的(subject.isRemembered()结果为true)。

@RequiresGuest

验证是否是一个guest的请求,与@RequiresUser完全相反。

换言之,RequiresUser  == !RequiresGuest。

此时subject.getPrincipal() 结果为null.

@RequiresRoles

例如:@RequiresRoles("aRoleName");

如果subject中有aRoleName角色才可以访问被修饰方法。

@RequiresPermissions

例如: @RequiresPermissions({"file:read", "write:aFile.txt"} )

要求subject中必须同时含有file:read和write:aFile.txt的权限才能执行方法

Shiro 常用标签

guest 标签:用户没有身份验证时显示相应信息,即游客访问信息

user 标签:用户已经经过认证/记住我登录后显示相应的信息。

authenticated标签:即Subject.login登录成功,不是记住我登录的

notAuthenticated 标签:没有调用Subject.login进行登录,包括记住我自动登录的

pincipal 标签:显示用户身份信息,默认调用Subject.getPrincipal() 获取

hasRole 标签:如果当前 Subject 有角色将显示 body 体内容:

hasAnyRoles 标签:如果当前Subject有任意一个角色将显示body体内容

lacksRole:如果当前 Subject 没有角色将显示 body 体内容

hasPermission:如果当前 Subject 有权限将显示 body 体内容

lacksPermission:如果当前Subject没有权限将显示body体内容

第三章:shiro授权认证的更多相关文章

  1. Apache Shiro 使用手册(三)Shiro 授权

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等. 一.授权的三要素 授权有着三 ...

  2. Apache shiro集群实现 (三)shiro身份认证(Shiro Authentication)

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  3. Apache Shiro 使用手册(三)Shiro 授权(转发:http://kdboy.iteye.com/blog/1155450)

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等. 一.授权的三要素 授权有着三 ...

  4. (转) Apache Shiro 使用手册(三)Shiro 授权

    解惑之处: 使用冒号分隔的权限表达式是org.apache.shiro.authz.permission.WildcardPermission 默认支持的实现方式. 这里分别代表了 资源类型:操作:资 ...

  5. 三、Shiro授权开发

    Shiro 支持三种方式的授权: I. 编程式:通过写if/else 授权代码块完成: Subject subject =SecurityUtils.getSubject(); if(subject. ...

  6. Shiro授权认证原理和流程

    先来张图: 这是一张shiro的功能图: Authentication: 身份认证/登录,验证用户是否拥有相应的身份 Authorization: 授权/权限验证,验证某个已认证的用户是否拥有某个权限 ...

  7. (三)shiro的认证

    文章目录 认证思路 自定义用于登录检验的Realm的思路 代码实现 后记 认证思路 调用 SecurityUtils.getSubject() 方法,获取当前的 Subject 对象 : 调用 Sub ...

  8. 将 Shiro 作为应用的权限基础 三:基于注解实现的授权认证过程

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限等等. 一.用户权限模型 为实现一个较为灵活的用户权限数据模 ...

  9. Apache shiro集群实现 (四)shiro授权(Authentication)--访问控制

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

随机推荐

  1. 分布式系统关注点(15)——「水平&垂直切分」详解

    如果第二次看到我的文章,欢迎右侧扫码订阅我哟~ 

  2. Ethercat 学习总结一:协议总结

    原文地址:https://blog.csdn.net/qq923433160/article/details/83781812 EtherCAT 总线的国家标准相关资料,比较详细介绍了协议: http ...

  3. windows&lunix下node.js实现模板化生成word文件

    最近在做了一个小程序!里面有个功能就是根据用户提交的数据,自动生成一份word文档返回给用户.我也是第一次做这功能,大概思路就是先自己弄一份word模板,后台接受小程序发过来的数据,再根据这些数据将相 ...

  4. “==”、“equals()”、“hashcode()”之间的秘密

    前言 万丈高楼平地起,今天的聊点基础而又经常让人忽视的话题,比如“==”与“equals()”区别?为何当我们重写完"equals()"后也要有必要去重写"hashcod ...

  5. javascript入门篇(六、正则表达式)

    JavaScript 正则表达式 正则表达式是由一个字符序列形成的搜索模式.当你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容. 正则表达式可以是一个简单的字符,或一个更复杂的模式.正则表达 ...

  6. Nginx的“远方表哥”—Tengine

    本文收录在Linux运维企业架构实战系列 今天想起当初研究nginx反向代理负载均衡时,nginx自身的upstream后端配置用着非常不舒服: 当时使用的淘宝基于nginx二次开发的Tengine, ...

  7. 关于thinkphp5手动抛出Http异常时自定义404页面报错的问题

    在使用HttpException手动抛出异常时,希望跳转到自定义的错误页面,官方的文章中是这样描述的. 可以使用\think\exception\HttpException类来抛出异常 // 抛出 H ...

  8. 用CSS画一个带阴影的三角形的示例代码

    1. 思路 怎么用CSS3画一个带阴影的三角形呢 ? 有童鞋说, 这还不简单吗 网上有很多解决方案, 但其实大多都是实现不太完美的, 存在一些问题 假设我们做一个向下的三角形箭头 常见的方法大致有两种 ...

  9. 2018-01-12 Antlr4添加中文变量赋求值,括号,各种问题

    中文编程知乎专栏原文地址 例程(更多测试用例在此): 基数=100 基数×(基数+1)÷2 => 求值为5050 续上文Antlr4实现数学四则运算, 修改的语法规则部分: 程序: 声明+; 声 ...

  10. 用samba来创建windows下的文件共享

    前言 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写,即为服务器消息块 ,SMB主要是作为Microsoft的网络 ...