- 权限管理
1.1 什么是权限管理?
权限管理实现对用户访问系统的控制,按照安全规则或者安全策略,可以控制用户只能访问自己被授权的资源
权限管理包括用户身份认证和授权两部分,简称认证授权
1.2 什么是身份认证?
身份认证就是判断一个用户是否为合法用户的处理过程,最常用的方式就是通过核对用户输入和用户名和口令是否与系统中存储的一致,来判断用户身份是否正确
1.3 什么是授权?
授权,即访问控制,控制谁能访问哪些资源,主体进行身份认证后需要分配权限方可访问系统资源,对于某些资源没有权限是无法访问的
- 什么是 shiro?
shiro 是一个功能强大且易于使用的 Java 安全框架,能够完成身份认证、授权、加密和会话管理等功能
2.1 shiro 的核心架构
- Subject
主体,外部应用与 subject 进行交互,subject 记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject 在 shiro 中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过 subject 进行认证授,而 subject 是通过 SecurityManager 安全管理器进行认证授权
- SecurityManager
安全管理器,对全部的 subject 进行安全管理,是 shiro 的核心。通过 SecurityManager 可以完成 subject 的认证、授权等,实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理。SecurityManager 是一个接口,继承了 Authenticator,Authorizer,SessionManager 这三个接口
- Authenticator
认证器,对用户身份进行认证,Authenticator 是一个接口,shiro 提供 ModularRealmAuthenticator 实现类,通过 ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器
- Authorizer
授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限
- Realm
领域,相当于 datasource 数据源,securityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 realm 就需要从数据库获取用户身份信息
- SessionManager
会话管理,shiro 框架定义了一套会话管理,它不依赖 web 容器的 session,所以 shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录
- SessionDAO
会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 jdbc 将会话存储到数据库
- CacheManager
CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能
- Cryptography
密码管理,shiro 提供了一套加解密的组件,方便开发,比如提供常用的散列、加解密等功能
- shiro 中的认证
3.1 认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确
3.2 shiro 认证中的关键对象
- Subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体
- Principal:身份信息
是主体(subject)进行身份认证的标识,标识必须具有唯一性, 如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)
- credential:凭证信息
是只有主体自己知道的安全信息,如密码、证书等
3.3 认证流程
3.4 认证开发
3.4.1 引入依赖
1 | xml复制代码<dependency> |
3.4.2 创建配置文件
该配置文件用来书写系统中相关的权限数据,主要用于学习使用
1 | ini复制代码[users] |
3.4.3 开发认证代码
1 | java复制代码public class TestAuthenticator { |
3.5 自定义 Realm
自定义 Realm 的实现,即是将认证/授权数据来源转为数据库的实现
1 | java复制代码public class TestCustomerRealmAuthenticator { |
自定义 Realm 代码实现
1 | java复制代码public class CustomerRealm extends AuthorizingRealm { |
3.6 明文加密
实际使用时,我们不可能把用户密码以明文形式显示,需要做加密处理
通常的加密方式是使用 md5 + salt + hash 散列的形式,校验过程:保存盐和散列后的值,在 shiro 完成密码校验
下面是使用 shiro 提供的 api 完成加密代码示例
1 | java复制代码public class TestShiroMD5 { |
自定义 CustomerMd5Realm
1 | java复制代码public class CustomerMd5Realm extends AuthorizingRealm { |
校验流程
1 | java复制代码public class TestCustomerMd5RealmAuthenticator { |
- shiro 中的授权
4.1 授权
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的
4.2 关键对象
授权可简单理解为 who 对 what(which) 进行 How 操作:
- Who,即主体(Subject),主体需要访问系统中的资源
- What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为 t01 的商品为资源实例,编号为 001 的商品信息也属于资源实例
- How,权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为 001 用户的修改权限等,通过权限可知道主体对哪些资源都有哪些操作许可
4.3 授权方式
基于角色的访问控制,以角色为中心进行访问控制
1 | java复制代码if(subject.hasRole("admin")){ |
基于资源的访问控制,以资源为中心进行访问控制
1 | java复制代码if(subject.isPermission("user:update:01")){ //资源实例 |
4.4 权限字符串
权限字符串的规则是:资源标识符:操作:资源实例标识符
,意思是对哪个资源的哪个实例具有什么操作,:
是资源/操作/实例的分割符,权限字符串也可以使用 *
通配符
例子:
- 用户创建权限:
user:create
,或user:create:*
- 用户修改实例 001 的权限:
user:update:001
- 用户实例 001 的所有权限:
user:*:001
4.5 授权编程实现
在之前 md5 加密的基础上,实现授权操作
自定义 CustomerMd5Realm
1 | java复制代码public class CustomerMd5Realm extends AuthorizingRealm { |
授权逻辑
1 | java复制代码public class TestCustomerMd5RealmAuthenticator { |
- shiro 整合 SpringBoot
shiro 整合 SpringBoot 的思路如图所示:
引入 shiro 整合 SpringBoot 依赖
1 | xml复制代码<dependency> |
5.1 认证
配置 shiro
1 | java复制代码@Configuration |
1 | java复制代码public class CustomerRealm extends AuthorizingRealm { |
shiro 提供了多个默认的过滤器,我们可以用这些过滤器来控制指定 url 的权限
配置缩写 | 对应的过滤器 | 功能 |
---|---|---|
anon | AnonymousFilter | 指定url可以匿名访问 |
authc | FormAuthenticationFilter | 指定url需要form表单登录,默认会从请求中获取username 、password ,rememberMe 等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。 |
authcBasic | BasicHttpAuthenticationFilter | 指定url需要basic登录 |
logout | LogoutFilter | 登出过滤器,配置指定url就可以实现退出功能,非常方便 |
noSessionCreation | NoSessionCreationFilter | 禁止创建会话 |
perms | PermissionsAuthorizationFilter | 需要指定权限才能访问 |
port | PortFilter | 需要指定端口才能访问 |
rest | HttpMethodPermissionFilter | 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释 |
roles | RolesAuthorizationFilter | 需要指定角色才能访问 |
ssl | SslFilter | 需要https请求才能访问 |
user | UserFilter | 需要已登录或“记住我”的用户才能访问 |
模拟认证、注册和退出过程
1 | java复制代码@Controller |
1 | java复制代码@Service |
5.2 授权
第一种方式,通过页面标签授权
1 | jsp复制代码<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> |
第二种方式,代码方式授权
1 | java复制代码@RequestMapping("save") |
第二种方式,注解方式授权
1 | java复制代码@RequiresRoles(value={"admin","user"})//用来判断角色 同时具有 admin user |
这里只是做个演示,实际开发中,我们需要对授权数据持久化。需要三张表:用户表、角色表和权限表,用户表和角色表之间,角色表和权限表之间都是多对多的关系,需要建立一张关系表
修改自定义 Realm
1 | java复制代码public class CustomerRealm extends AuthorizingRealm { |
5.3 shiro 缓存
使用缓存可以减轻 DB 的访问压力,从而提高系统的查询效率
5.3.1 整合 Ehcache
引入 ehcache 依赖
1 | xml复制代码<!--引入shiro和ehcache--> |
开启缓存
1 | java复制代码@Bean |
5.3.2 整合 Redis
引入 redis 依赖
1 | xml复制代码<!--redis整合springboot--> |
配置 redis 连接,启动 redis 服务
1 | properties复制代码spring.redis.port=6379 |
ehcache 提供了 EhCacheManager,而 EhCacheManager 实现了 CacheManager 接口,因此我们可以自定义一个 RedisCacheManager
1 | java复制代码public class RedisCacheManager implements CacheManager { |
再自定义 Cache 接口实现
1 | java复制代码public class RedisCache<K,V> implements Cache<K,V> { |
我们还需要对 salt 作序列化实现,由于 shiro 中提供的 SimpleByteSource 实现没有实现序列化,所以我们需要自定义 salt 序列化实现
1 | java复制代码 // 自定义 salt 实现,实现序列化接口 |
在 realm 中使用自定义 salt
1 | java复制代码public class MyByteSource implements ByteSource, Serializable { |
本文转载自: 掘金