Shiro的认证流程及授权实现
一、 Shiro的认证流程... 2
1 具体执行流程... 2
1.1 Shiro初始化SecurityManager 2
1.2 创建Subject主体对象... 2
1.3 执行登录认证... 2
二、 Shiro授权实现... 3
1 ini文件实现本地授权... 3
1.1 编写ini文件... 3
1.2 编写代码测试... 4
2 Realm实现授权... 4
2.1 ini文件... 4
2.2 realm代码... 4
3 JDBC实现数据库授权... 5
3.1 创建数据库表和准备数据... 5
3.2 编写realm代码... 10
三、 SSM+Shiro实现认证和授权... 16
1 完善POJO类型... 16
2 完善Mapper 16
2.1 修改映射文件... 16
3 实现Realm中的授权方法... 19
4 JSP页面修改... 20
5 bug. 21
一、 Shiro的认证流程
1 具体执行流程
1.1 Shiro初始化SecurityManager
比如: 加载本地的ini文件. 或初始化DefaultWebSecurityManager对象.
SecurityManager是Shiro框架中的核心组件.
1.2 创建Subject主体对象
建议使用SecurityUtils工具创建对象. 因为工具创建的Subject对象,与线程相关. 是一个线程绑定的资源. 且,如果使用了shiro-web相关的jar包. SecurityUtils会将Subject和线程以及HttpSession做关联; 一个用户对应唯一的一个Subject.
1.3 执行登录认证
就是Subject.login方法执行. login方法可以主动调用,也可以被动触发.
被动触发: 如在SSM+Shiro实现认证流程的时候, 所有代码没有执行subject.login. 是Shiro框架自动执行触发的. 就是FormAuthenticationFitler中某方法触发调用的.
1.3.1 收集token数据
就是用户的身份信息和凭证信息.
1.3.2 执行login方法
登录认证
1.3.3 SecurityManager执行安全相关操作
Shiro的核心组件需要执行认证逻辑.
1.3.3.1 访问Authenticator是SecurityManager的父接口. 内部定义了用户认证的具体流程.
1.3.3.2 检索Realm信息根据配置信息(ini文件或spring配置文件), 查找realm. 如果没有realm,执行简单流程.如果有realm组件, 则执行realm组件中的认证方法.doGetAuthticationInfo方法. 运行具体的认证过程.
1.3.3.3 Realm交互Realm会根据具体的代码定义逻辑. 访问外部的数据源. 如: 数据库, 文件系统等.
1.3.3.4 完成认证方法doGetAuthenticationInfo会有返回值. AuthticationInfo对象.如果返回为null, 身份不存在. 如果返回为对象. 需要再次匹配凭证.
1.3.3.5 完成认证逻辑Shiro框架的SecurityManager. SecurityManager会根据认证方法返回的AuthenticationInfo对象中的凭证,匹配请求参数中的凭证信息. 决定用户是否认证成功.
二、 Shiro授权实现
可以实现认证和授权. 授权功能是建立在认证功能完成的基础之上的逻辑.
认证不通过, 没有必要授权.
可以使用ini本地操作,或realm实现远程数据操作.
1 ini文件实现本地授权
1.1 编写ini文件
# 用户数据定义, 用来定义shiro中的本地用户信息.包含身份凭证等.
# 语法 : 身份=凭证,角色1,角色2,角色3
[users]
zhangsan=123,userManager1,userManager2
lisi=321,customerManager1,customerManager2,customerManager3,customerManager4
# 角色信息定义. 包含角色的名称和具体的权限.
# 语法是: 角色名=权限1,权限2,权限3
# 权限的命名最好唯一. 可以避免多角色,类似权限的判断逻辑混乱问题.
[roles]
userManager=insert,update,delete,select
userManager1=insert
userManager2=delete
userManager3=update
userManager4=select
customerManager=insert,update,delete,select
customerManager1=insert
customerManager2=delete
customerManager3=update
customerManager4=select
1.2 编写代码测试
在本地ini文件配置的Shiro环境中. 认证成功,则授权信息已读取并保存到Subject中了.可以通过Subject中的方法,来判断用户的角色和权限.
// 检查用户的角色是否存在, 判断角色名称是否在主体中存在
boolean hasRole = subject.hasRole("userManager1");
System.out.println("userManager1 - " + hasRole);
// 测试权限, 测试权限命名是否存在在Subject中.
boolean isPermitted = subject.isPermitted("user:insert");
System.out.println("user:insert - " + isPermitted);
2 Realm实现授权
就是实现Realm中的doGetAuthorizationInfo方法.
具体实现的逻辑, 就是将角色数据和权限数据,增加到方法的返回值对象中.返回值对象是AuthorizationInfo类型的对象.
2.1 ini文件
[main]
myRealm=com.bjsxt.shiro.realm.MyRealm
securityManager.realms=$myRealm
2.2 realm代码
/**
* 授权方法
* 所谓是Realm中是授权,就是将角色数据和权限数据,增加到subject主体中.
* 需要准备角色和权限数据, 并添加到主体信息中.
*
* @param principals - 就是认证方法直接成功后,得到的主体数据对象. 主体数据对象,不是Subject, 是principal
*
* @return AuthorizationInfo - 返回的具体授权信息. 内部就是用户主体中的角色集合和权限集合.
*
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("授权方法.");
String roleName = "userManager";
String permission1 = "user:insert";
String permission2 = "user:update";
String permission3 = "user:*";
// 唯一身份信息, 就是SimpleAuthenticationInfo中的principal信息.
System.out.println("primary principal : " + principals.getPrimaryPrincipal());
// 创建权限和角色数据的载体对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 增加角色数据
info.addRole(roleName);
// 增加权限数据
info.addStringPermission(permission1);
info.addStringPermission(permission2);
info.addStringPermission(permission3);
// 返回角色和权限的数据载体
return info;
}
3 JDBC实现数据库授权
3.1 创建数据库表和准备数据
-- 用户表格
drop table if exists tb_user;
create table tb_user(
id bigint primary key auto_increment,
name varchar(32),
login_name varchar(32),
login_pswd char(32)
);
insert into tb_user(name, login_name, login_pswd)
values
('张三', 'zhangsan', '123'),
('李四', 'lisi', '123');
-- 角色表
drop table if exists tb_role;
create table tb_role(
id bigint primary key auto_increment,
role_name varchar(255),
remark text -- 描述, 文本文件类型, 相当于Oracle中的CLOB.
);
insert into tb_role(role_name, remark)
values
('超级管理员', '拥有所有权限'),
('HRM', '拥有人事总监权限'),
('Sales', '拥有销售权限');
-- 用户角色关系
drop table if exists tb_user_role;
create table tb_user_role(
user_id bigint references tb_user(id),
role_id bigint references tb_role(id),
primary key (user_id, role_id)
);
insert into tb_user_role(user_id, role_id)
values
(1, 1),
(2, 2),
(2, 3);
-- 权限表格
drop table if exists tb_rule;
create table tb_rule(
id bigint primary key auto_increment,
rule_name varchar(255),
remark text
);
insert into tb_rule(rule_name, remark)
values
('system:all', '系统管理的全部权限'),
('system:log', '系统管理中的日志管理权限'),
('user:all', '用户管理的全部权限'),
('user:insert', '用户管理的查询,新增权限'),
('user:update', '用户管理的查询,修改权限'),
('user:delete', '用户管理的查询,删除权限'),
('customer:all', '客户管理的全部权限');
-- 角色权限关系
drop table if exists tb_role_rule;
create table tb_role_rule(
role_id bigint references tb_role(id),
rule_id bigint references tb_rule(id),
primary key (role_id, rule_id)
);
insert into tb_role_rule(role_id, rule_id)
values
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(1, 7),
(2, 3),
(2, 4),
(2, 5),
(2, 6),
(3, 7);
-- 资源表
drop table if exists tb_resources;
create table tb_resources(
id bigint primary key auto_increment,
text varchar(255), -- 显示文本内容
url varchar(255), -- 访问地址路径
parent_id integer references tb_resources(id), -- 上级资源ID
is_parent int(1), -- 是否是父节点, 父节点 - 0, 叶子节点 - 1
type varchar(20), -- 类型, 代表资源的应用方式, 如: menu对应的是菜单资源,要在页面的菜单列表中显示的. btn是按钮资源, 在页面显示的. file是文件资源, 代表什么文件可以显示或下载
remark text -- 资源的描述
);
insert into tb_resources(text, url, parent_id, is_parent, type, remark)
values
文章来源:
Author:芸诺
link:http://yuuuo.com/?id=38