天天看点

apache shiro版本查看_Shiro安全框架(上)

Shiro简介

Shiro是Apache下的一个用于身份校验,授权,会话管理的一个安全框架。它能够应用在任何需要安全校验的场合。

它主要由三个核心组件构成:

  1. Subject:主体,即“当前用户”。主体不仅可以是系统用户,也有可能是第三方进程,其他应用程序等等。
  2. SecurityManager:它是Shiro框架的核心。它通过门面模式进行构造,将Subject和Realm聚合到其内部,以此提供各种安全管理的服务。
  3. Realm:域,用于配置相关用户及其权限信息。在进行登陆认证以及授权访问时,能够向SecurityManager提供授权信息。同时,用户可以自定义Realm的认证机制,来达到权限验证的复杂化。

Shiro架构

apache shiro版本查看_Shiro安全框架(上)

shiro架构图

任何的主体(无论是c++程序还是Java程序),在授权时都会经由SecuirityManager处理。如果是授权问题,则由Authorizer处理,如果是认证,则由Authenticator处理。授权指的是将应用的权力赋予其他主体,其中主体是已经经过认证的主体。而认证是将为经过认证的主体进行认证,将其判定为系统角色或非系统角色。

同时,Security Manager也集成了Realms(域,可以认为是用户身份信息的集合),会话管理(Session Manager)和缓存管理。

Shiro架构的具体实现

首先是Subject主体。

它是一个定义好的接口,定义了一个主体在授权认证体系中可能出现的一些动作:

apache shiro版本查看_Shiro安全框架(上)

其中定义了一些常见的方法:

  • isPermitted是否被认证(参数可以是Permission(或者继承该类),String(如"阅读某某文档权限","查看员工信息权限"等),以及String数组。
  • checkPermission。相关参数同上
  • hasRole,checkRole等角色相关函数,
  • login和logout登陆退出动作
  • 一些支持函数等。

其次是Realm:

apache shiro版本查看_Shiro安全框架(上)

也是个接口定义,定义了supports,用于支持AuthenticationToken的生成;同时也定义了getAuthenticationInfo,通过token获得AuthenticationInfo(认证信息),提交给上级(SecurityManager)中的Authz(Authorizer简称)和Authc(Authenticator)进行认证和鉴权。

最后是SecurityManager:

它是一个很简单的接口,定义了一些基础的方法,源代码如下:

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
 Subject login(Subject var1, AuthenticationToken var2) throws AuthenticationException;

 void logout(Subject var1); 

 Subject createSubject(SubjectContext var1);
}
           

可以发现,他继承了Authenticator,Authorizer,SessionManager,并将Subject的login,logout动作集合在其中,所以,这些代码定义了Shiro的整个架构。

围绕三个组件的其他组件

对于Subject主体而言,一些诸如Permission(主体所拥有的权限),Role(主体的角色)等类,都是一些十分合理的设置。

对于Realm而言,则是AuthenticationInfo(一个用户经过认证后生成的认证信息)以及AuthenticationToken(一个用户所持有的凭证,通常是登陆时,校验用户密码,通过则颁发)

对于SecurityManager,Authenticator中只定义了authenticate方法,并有一系列的默认实现。而Authorizer中定义了很多权限相关的方法。而SessionManager是Shiro的框架支持,它提供了一些安全会话的功能。

特别说明

Shiro框架是一个比较简单的安全框架,它将我们日常编码中的授权认证抽象成一套体系,主要围绕三个核心组件来进行架构。读者可以先领会Shiro框架的架构思想,再配合下面的例子进行深刻的理解。至于Shiro中的数据结构,诸如Permission之类的,读者只需大致了解其含义及功能,无需太过执着于它们的具体定义。

Shiro框架实践案例

有了理论知识后,就开始实践搭建一套案例来进行实际运用。

技术说明:

本案例的搭建基于Java 8环境,使用的是SpringBoot框架,采用Maven进行项目构建。使用的相关外部框架的版本如下:

  • Shiro版本为1.4.0版本
  • MyBatis-plus-starter版本为1.0.5
  • MyBatis-plus版本为2.1.9

项目结构

​ShiroDemo​

​ --src​

​ --main​

​ --java //项目文件目录​

​ --com.jungle //主目录​

​ --config //项目相关配置​

​ --controller //控制层​

​ --domain //实体类​

​ --mapper //dao ​

​ --service //服务层​

​ --resource //资源文件目录​

​ --mapper //mapper映射文件所在处​

​ --test //项目测试文件​

​ --pom.xml //maven项目文件​

开始 ---进行简单的数据库搭建

其实,当你引入Shiro的包时,这个安全的机制就已经建立成了。但是呢,Shiro只是一个框架,只是提供一个基础,我们需要根据需求来定制化我们的安全机制。

那么,我们来模拟一个比较常见的场景:

首先,一个系统总是有用户,除了用户以外,还有角色(比如系统管理员,普通用户),权限(阅读某某文档的权限)。为了将这些角色关联起来,需要建立关联表,其中用户于角色是一对一的关系,角色和权限是一对多的关系。

E-R图大致如下:

apache shiro版本查看_Shiro安全框架(上)

相关建表语句如下:

用户表:

CREATE TABLE `user_inf` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  `password` varchar(128) NOT NULL,
  `salt` varchar(12) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=10004 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
           

角色表:

CREATE TABLE `role_inf` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
           

用户-角色关联表:

CREATE TABLE `user_role_inf` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  KEY `user_foreign_constraint` (`user_id`),
  KEY `role2_foreign_constraint` (`role_id`),
  CONSTRAINT `role2_foreign_constraint` FOREIGN KEY (`role_id`) REFERENCES `role_inf` (`id`),
  CONSTRAINT `user_foreign_constraint` FOREIGN KEY (`user_id`) REFERENCES `user_inf` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
           

权限表:

CREATE TABLE `permission_inf` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `permission_name` varchar(20) NOT NULL,
  `permission_url` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
           

角色-权限关联表:

CREATE TABLE `role_permission_inf` (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  KEY `role_foreign_constraint` (`role_id`),
  KEY `permission_foreign_constraint` (`permission_id`),
  CONSTRAINT `permission_foreign_constraint` FOREIGN KEY (`permission_id`) REFERENCES `permission_inf` (`id`),
  CONSTRAINT `role_foreign_constraint` FOREIGN KEY (`role_id`) REFERENCES `role_inf` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
           

继续 ---进行简单的配置

把Shiro引入进来后,就可以进行一些配置了。

首先,我们需要配置Shiro的核心,配置SecurityManager,相关代码如下:

@Bean
           

这里,securityManager使用Realm是自定义的,也是需要我们自己去配置的,它的配置如下:

@Bean
           

MyShiroRealm:

public 
           

这里我们继承AuthorizingRealm类,该类是一个抽象类,对于常见的授权和认证都有了较好的实现,只要去实现它的doGetAuthorizationInfo方法即可。注意,该方法被重载成了两个方法,其中一个接收的是PrincipalCollection,另一个接收的是AuthenticationToken。

第一个接收PrincipalCollection的是怎么实现的呢?首先,任何用户登陆都会经过Shiro,但是我们用户登陆进去,都会被包装成PrincipalCollection。然后再进入到Realm中,我们要将自己的权限规则告诉Shiro框架,所以就需要统一包装成PrincipalCollection,然后使用getPrimaryPrincipal来获取当前的用户。然后再经过一些查询操作,将角色权限关系通过编码搭载Shiro框架之上。接着,只要按照规则包装好AuthorizationInfo即可。

第二个接收AuthenticationToken,这个token主要包括的是Principal(强制转换后就是对应的Subject了)和凭证。然后这个方法就是用于进行登陆验证的。

那么问题就来了,如果登陆失败,那么要怎么处理这些错误信息呢?Shiro提供了一套的Filter机制,用来过滤相关信息的。实现如下:

public 
           

这里我们只重写了setFailureAttribute方法。其实,继承的这个FormAuthenticationFilter有很深的“城府”,我们下回分析。

由于本人首次在知乎写作,所以如有不足还请见谅。由于貌似知乎单篇文章有字数限制还是其他什么原因,故分为两节书写。

这篇文章绝大部分内容为自创内容,不过有部分内容如Realm,MyAuthenticationFilter等相关内容是借鉴Spring Boot [集成-Shiro] 等文章,并实地验证了一遍,没有问题,转载请说明出处,十分感谢。