Shiro(四)介绍Shiro 多Realm验证和认证策略,接下来讲解Shiro 授权。
目录
一、权限配置
1.基本概念
- 授权,也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作 等)。
在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
-
主体(Subject):访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
-
资源(Resource):在应用中用户可以访问的 URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
-
权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)等。权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许。
-
Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)
-
角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
2.Shiro 授权方式
Shiro 支持三种方式的授权:
(1)编程式:通过写if/else 授权代码块完成(不常用) :
if(subject.hasRole("admin")){
//有权限
}else{
//无权限
}
(2)注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常 (常用)
@RequiresRoles("admin")
public void hello(){
//有权限
}
(3)JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成
<shiro:hasRole name="admin">
<!--有权限-->
</shiro:hasRole>
通过标签可以隐藏没有权限访问的资源
注意:注解式 是常用的方式
3.默认拦截器
Shiro 内置了很多默认的拦截器,比如身份验证、授权等相关的。默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举
1)默认拦截器:
2)详细区分
①身份验证相关的(5个)
②授权相关的(5个)
③其他(1个)
4.Permissions
5.授权 roles 的配置使用
新建两个jsp页面:admin.jsp和user.jsp,希望实现访问admin.jsp,需要具备admin的权限,访问user.jsp,需要具备user的权限
步骤:
(1)新建两个jsp页面:admin.jsp和user.jsp
admin.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>Admin Page</h3>
</body>
</html>
user.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>User Page</h3>
</body>
</html>
(2)在list.jsp页面添加访问admin.jsp 和 user.jsp的超链接:
controller:
/**
* 进入admin页面
* @return
*/
@RequestMapping("/toAdmin")
public ModelAndView admin(){
return new ModelAndView("admin");
}
/**
* 进入user页面
* @return
*/
@RequestMapping("/toUser")
public ModelAndView user(){
return new ModelAndView("user");
}
list.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>list</title>
</head>
<body>
<h3>list Page</h3>
<div>欢迎登录</div>
<a href="toAdmin">Admin page</a><br>
<a href="toUser">User page</a><br>
<a href="logout">logout</a>
</body>
</html>
(3)权限配置:在applicationContext.xml文件中 添加 上面两个访问链接的角色过滤
(4)启动项目服务,进行测试:
输入用户名admin,密码123456,点击submit,登录成功,进入list页面,由于上面配置 admin.jsp、user.jsp页面的访问需要 admin 、user角色权限,所以是没有权限访问,会进入unauthorized.jsp页面。user用户登录访问是一样的效果。
二、Shiro 实现授权
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表示授权失败。
授权的Realm如何实现?
答:授权需要继承 AuthorizingRealm类,并实现其doGetAuthorizationInfo方法。
注意点:AuthorizingRealm类继承自AuthenticatingRealm,但没有实现AuthenticatingRealm中的doGetAuthenticationInfo方法,所以认证和授权只需要继承AuthorizingRealm就可以了,同时需要实现它的两个抽象方法。
2.实现
ModularRealmAuthorizer 进行多 Realm 匹配流程:
- 1、首先检查相应的 Realm 是否实现了实现了Authorizer;
- 2、如果实现了 Authorizer,那么接着调用其相应的isPermitted*/hasRole* 接口进行匹配;
- 3、如果有一个Realm匹配那么将返回 true,否则返回 false。
多Realm授权的通过标准:
查看源码:
可以看到 只要有一个授权通过,它就可以进行 return true.
结论:多个Realm授权,只要有一个通过就可以。
2.1 修改 ShiroRealm.java
(1)修改 ShiroRealm的继承类,并实现doGetAuthorizationInfo方法
(2)doGetAuthorizationInfo方法实现
/**
* 授权
* 授权会被shiro回调的方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("doGetAuthorizationInfo---");
//1. 从 PrincipalCollection 中来获取登录用户的信息
Object principal = principals.getPrimaryPrincipal();
//2. 利用登录用户的信息来获取当前用户的角色或权限(可能需要查询数据库)
Set<String> roles= new HashSet<String>();
roles.add("user");//每个用户都有user这个角色
if("admin".equals(principal)){
roles.add("admin");//admin用户还有admin角色
}
//3. 创建 SimpleAuthorizationInfo,并设置其 roles 属性
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
//4. 返回 SimpleAuthorizationInfo 对象
return simpleAuthorizationInfo;
}
2.2 测试
(1)admin用户登录,admin.jsp和 user.jsp 两个页面都可以访问:
(2)user用户登录,user.jsp 页面可以访问,而admin.jsp页面没有权限访问 :
测试成功
可点击此处获取源码