Shiro简介
Shiro是Apache下的一个用于身份校验,授权,会话管理的一个安全框架。它能够应用在任何需要安全校验的场合。
它主要由三个核心组件构成:
- Subject:主体,即“当前用户”。主体不仅可以是系统用户,也有可能是第三方进程,其他应用程序等等。
- SecurityManager:它是Shiro框架的核心。它通过门面模式进行构造,将Subject和Realm聚合到其内部,以此提供各种安全管理的服务。
- Realm:域,用于配置相关用户及其权限信息。在进行登陆认证以及授权访问时,能够向SecurityManager提供授权信息。同时,用户可以自定义Realm的认证机制,来达到权限验证的复杂化。
Shiro架构
![ab7a0e8637582a2f2b30f299b8912668.png](https://i-blog.csdnimg.cn/blog_migrate/74900b5e8db27d481d8377b7d4eb282e.jpeg)
任何的主体(无论是c++程序还是Java程序),在授权时都会经由SecuirityManager处理。如果是授权问题,则由Authorizer处理,如果是认证,则由Authenticator处理。授权指的是将应用的权力赋予其他主体,其中主体是已经经过认证的主体。而认证是将为经过认证的主体进行认证,将其判定为系统角色或非系统角色。
同时,Security Manager也集成了Realms(域,可以认为是用户身份信息的集合),会话管理(Session Manager)和缓存管理。
Shiro架构的具体实现
首先是Subject主体。
它是一个定义好的接口,定义了一个主体在授权认证体系中可能出现的一些动作:
![8c888225b6d700a7ee3217f9382f37b2.png](https://i-blog.csdnimg.cn/blog_migrate/a82d13e73b60218d7407bd84b9aded39.jpeg)
其中定义了一些常见的方法:
- isPermitted是否被认证(参数可以是Permission(或者继承该类),String(如"阅读某某文档权限","查看员工信息权限"等),以及String数组。
- checkPermission。相关参数同上
- hasRole,checkRole等角色相关函数,
- login和logout登陆退出动作
- 一些支持函数等。
其次是Realm:
![7ea89c8c02b6874a29acc7547b7c837f.png](https://i-blog.csdnimg.cn/blog_migrate/799e6673876c5fab1837a670acf13e62.png)
也是个接口定义,定义了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图大致如下:
![38cb76017487ec2886a23db75bbf035a.png](https://i-blog.csdnimg.cn/blog_migrate/9bfabac78b60c70e040857d44af7c350.jpeg)
相关建表语句如下:
用户表:
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] 等文章,并实地验证了一遍,没有问题,转载请说明出处,十分感谢。