Shiro的认证流程及授权实现

 ... 2

一、      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对象.

SecurityManagerShiro框架中的核心组件.

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