简介
Apache Shiro 是 Java 的一个安全框架. 目前, 使用 Apache Shiro 的人越来越多, 因为它相当简单, 对比 Spring Security, 可能没有 Spring Security 做的功能强大, 但是在实际工作时可能并不需要那么复杂的东西, 所以使用小而简单的 Shiro 就足够了. 对于它俩到底哪个好, 这个不必纠结, 能更简单的解决项目问题就好了.
Shiro 可以非常容易的开发出足够好的应用, 其不仅可以用在 JavaSE 环境, 也可以用在 JavaEE 环境. Shiro 可以帮助我们完成: 认证, 授权, 加密, 会话管理, 与 web 集成, 缓存等, 在此我仅仅介绍我们公司使用的认证, 授权, 加密功能, 对于其他功能, 小伙伴可以发挥一下自学能力呦
在此让我们来了解一些 shiro 的一些基本的术语
Authentication:
身份认证 / 登录, 验证用户是不是拥有相应的身份;
Authorization:
授权, 即权限验证, 验证某个已认证的用户是否拥有某个权限; 即判断用户是否能做事情, 常见的如: 验证某个用户是否拥有某个角色. 或者细粒度的验证某个用户对某个资源是否具有某个权限;
Cryptography:
加密, 保护数据的安全性, 如密码加密存储到数据库, 而不是明文存储;
Subject:
主体, 代表了当前 "用户", 这个用户不一定是一个具体的人, 与当前应用交互的任何东西都是 Subject, 如网络爬虫, 机器人等; 即一个抽象概念; 所有 Subject 都绑定到 SecurityManager, 与 Subject 的所有交互都会委托给 SecurityManager; 可以把 Subject 认为是一个门面; SecurityManager 才是实际的执行者;
SecurityManager:
相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher; 是 Shiro 的心脏; 所有具体的交互都通过 SecurityManager 进行控制; 它管理着所有 Subject, 且负责进行认证和授权, 及会话, 缓存的管理
Realm:
域, Shiro 从从 Realm 获取安全数据(如用户, 角色, 权限), 就是说 SecurityManager 要验证用户身份, 那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法; 也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作; 可以把 Realm 看成 DataSource, 即安全数据源.
token
token 相当于一个令牌, 是将用户的一些信息 (如账号, 密码, 等) 经过散列然后生成一个无意义的字符串保存在数据库或者 Redis, 散列不可逆, 用户登录时只能通过重新散列然后得到串和数据库中的串做比较, 看是否相同判定账号密码是否正确
shiro 运行流程
首先, 我们从外部来看 Shiro 吧, 即从应用程序角度的来观察如何使用 Shiro 完成工作
应用代码通过 Subject 来进行认证和授权, 而 Subject 又委托给 SecurityManager;
我们需要给 Shiro 的 SecurityManager 注入 Realm, 从而让 SecurityManager 能得到合法的用户及其权限 进行判断.
对于 realm 的详解
相信看完上面的工作流程图大家一定会产生一个疑问 -->为什么要 realm? 请看下文分析....
shiro 作为一个安全授权框架, 对于用户安全登录上, 怎样的用户是安全的? 怎怎样的用户是可以进入的? 样的用户该拥有什么样的权限? 相信这些只有开发者才知道, realm 正是 shiro 留给开发者用于自定义用户登录验证和授权认证的一个类, 我们可以通过实现 Realm 接口创建一个自定义的 realm 类, 用于自定义用户登录验证方式 (AuthenticationInfo 来完成) 和授权验证(AuthorizationInfo 来完成)
登录验证
相信大家通过上面的讲解对 shiro 已经有了一些基本的了解, 那么接下来我将通过我们公司封装的 shiro 框架为例, 向大家揭秘登录和授权功能
编写 TokenFilter 来进行登录验证
enter description here
在配置文件里面配置需要进行验证的一些 mappering
enter description here
让我们来看一下 filter 里面主要的一个类
enter description here
首先他调用了一个 gettoken 方法, 返回了一个 Token, 让我们看一下 gettoken 方法
enter description here
通过查看代码, 我们知道这个 token 来自于请求域, 来自于用户填写的信息
再回到 filter 中我们会发现这么一句代码
boolean loginSuccess = this.login(new Token(token));
将获得的 token 传入到一个 login 方法中, 接下来让我们顺着这里往下走, 来到了另一个 login, 里面有这么一句 -->subject.login(token);
enter description here
进入 subject.login(token), 顺着一直走我们会发现这么一个类
enter description here
在这个方法里面我们可以发现这么一句 -->info = authenticate(token);, 我们可以发现这就是在间接地调用 relam 类里面的我们自动以的验证方法
让我们进入 relam 的 doGetAuthenticationInfo
enter description here
我们会发现他是通过用户数据为 key 在 Redis 中尝试获取有没有对应的 value, 来证明是否这个用户曾经注册过, 或者账号密码和一些信息是否正确
授权
关于授权的一些概念
主体
主体, 即访问应用的用户, 在 Shiro 中使用 Subject 代表该用户. 用户只有授权后才允许访问相应的资源.
资源
在应用中用户可以访问的任何东西, 比如访问 JSP 页面, 查看 / 编辑某些数据, 访问某个业务方法, 打印文本等等都是资源. 用户只要授权后才能访问.
权限
安全策略中的原子授权单位, 通过权限我们可以表示在应用中用户有没有操作某个资源的权力. 即权限表示在应用中用户能不能访问某个资源, 如:
访问用户列表页面
查看 / 新增 / 修改 / 删除用户数据 (即很多时候都是 CRUD(增查改删) 式权限控制)
打印文档等等...
如上可以看出, 权限代表了用户有没有操作某个资源的权利, 即反映在某个资源上的操作允不允许, 不反映谁去执行这个操作. 所以后续还需要把权限赋予给用户, 即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情, 而是由实现人员提供.
Shiro 支持粗粒度权限 (如用户模块的所有权限) 和细粒度权限(操作某个用户的权限, 即实例级别的), 后续部分介绍.
角色
角色代表了操作集合, 可以理解为权限的集合, 一般情况下我们会赋予用户角色而不是权限, 即这样用户可以拥有一组权限, 赋予权限时比较方便. 典型的如: 项目经理, 技术总监, CTO, 开发工程师等都是角色, 不同的角色拥有一组不同的权限.
隐式角色:
即直接通过角色来验证用户有没有操作权限, 如在应用中 CTO, 技术总监, 开发工程师可以使用打印机, 假设某天不允许开发工程师使用打印机, 此时需要从应用中删除相应代码; 再如在应用中 CTO, 技术总监可以查看用户, 查看权限; 突然有一天不允许技术总监查看用户, 查看权限了, 需要在相关代码中把技术总监角色从判断逻辑中删除掉; 即粒度是以角色为单位进行访问控制的, 粒度较粗; 如果进行修改可能造成多处代码修改.
显示角色:
在程序中通过权限控制谁能访问某个资源, 角色聚合一组权限集合; 这样假设哪个角色不能访问某个资源, 只需要从角色代表的权限集合中移除即可; 无须修改多处代码; 即粒度是以资源 / 实例为单位的; 粒度较细.,
授权方式
Shiro 支持三种方式的授权:
1. 编程式: 通过写 if/else 授权代码块完成:
Java 代码
- Subject subject = SecurityUtils.getSubject();
- if(subject.hasRole("admin")) {
- // 有权限
- } else {
- // 无权限
- }
注解式: 通过在执行的 Java 方法上放置相应的注解完成:
Java 代码 收藏代码
- @RequiresRoles("admin")
- public void hello() {
- // 有权限
- }
没有权限将抛出相应的异常;
- <shiro:hasRole name="admin">
- <!- 有权限 ->
- </shiro:hasRole>
来源: https://juejin.im/post/5c000817f265da61736a0420