Shiro安全框架
一、Shiro概述
1、Apache Shiro
shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码加密和会话管理等。
常见的安全框架:
apache shiro
spring security
sa-token 前后端分离、分布式集群
2、主要作用
(1)身份认证
(2)授权
(3)加密
(4)会话管理
(5)缓存
3、核心组件
(1)Subject(用户)
即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
(2)SecurityManager(安全管理器)
1 | 它是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。 |
(3)Realm(数据源)
Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
二、springboot整合shiro
1、导入依赖
shiro-spring
1 | <!--shiro--> |
2、配置shiro
过滤器 , 安全管理器 ,数据源
1 | package com.example.springbootshirodemo.config; |
3、编写Realm
1 | AuthorizingRealm extends AuthenticatingRealm extends CachingRealm implements Realm |
1 | public class MyRealm extends AuthorizingRealm { |
三、身份认证和退出
1、认证流程
2、身份认证操作
(1)数据库设计和配置mybatis
五张表
user,role,permission,user_role,role_permission
1 | #mybatis |
(2)编写domain
1 | package com.example.springbootshirodemo.domain; |
(3)mapper
1 | package com.example.springbootshirodemo; |
(4)映射文件
1 | <?xml version="1.0" encoding="UTF-8" ?> |
(5)编写realm
1 | package com.example.springbootshirodemo.shiro; |
(6)修改shiroconfig
1 | package com.example.springbootshirodemo.config; |
(7)编写登录
1 |
|
3、退出认证
1 | @RequestMapping("/ty/logout") |
4、记住我
认证通过,将认证信息保存下来。
下次可以直接访问,无需再认证。
(1)页面
1 | 登录页面: |
(2)controller
1 | @RequestMapping("/user/login") |
(3)设置记住我
1 | /** |
1 | /** |
(4)错误Unable to execute ‘doFinal’ with cipher instance
解决:
//设置统一密钥,防止重启密钥发生变化导致记住我验证不通过
rememberMeManager.setCipherKey(Base64.decode(“4AvVhmFLUs0KTA3Kprsdag==”));
四、授权
一个用户拥有什么权限,就能进行什么操作。反之,不能操作。
权限设计:RBAC
Role-Base Access-Controll 基于角色的权限设计
Resource-Base Access-Controll 基于资源的权限设计
需要5张表:
user 、 role、 user_role
permission、role_permission
查找当前用户的角色和权限。
1 | package com.example.springbootshirodemo.domain; |
1 | package com.example.springbootshirodemo.domain; |
1 | package com.example.springbootshirodemo.domain; |
1 | package com.example.springbootshirodemo.mapper; |
1 | <?xml version="1.0" encoding="UTF-8" ?> |
1 | package com.example.springbootshirodemo.shiro; |
1、过滤器授权(基于url拦截的权限管理)
1 | package com.example.springbootshirodemo.controller; |
1 | package com.example.springbootshirodemo.config; |
2、注解授权
1 | RequiresAuthentication: |
开启注解:
1 | /** |
注解使用:
1 | package com.example.springbootshirodemo.controller; |
五、thymeleaf集成shiro标签
1、作用
通过shiro权限来控制页面上的内容是否显示。
2、导入依赖
1 | <!--shiro集成thymeleaf--> |
1 | <!--thymeleaf--> |
3、配置
1 | /** |
4、页面
1 | <!doctype html> |
1 | <!DOCTYPE html> |
5、controller
1 | package com.example.springbootshirodemo.controller; |
6、测试
六、加密
1、config
1 | /** |
2、realm
1 | package com.example.springbootshirodemo.shiro; |
3、数据库密码加密
1 | package com.example.springbootshirodemo.shiro; |
4、登录测试
七、缓存管理
1、作用
realm可以使用缓存,提高查询性能。
默认授权有缓存,认证没有缓存。
缓存类型:
shiro内置本地缓存MemoryConstrainedCacheManager
整合第三方缓存:EhCache等
2、操作
1 | /** |
可以添加给某个realm,也可以添加给securitymanager
如果添加给securitymanager,所有realm都采用缓存。
1 | /** |
八、会话管理
1、shiro支持三种会话管理方式
shiro提供了三个SessionManager的实现
- DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于非web环境。
- ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话。
- DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager自己维护着会话,容器无关。
推荐DefaultWebSessionManager
会话跟踪:shiro中有session对象,客户端浏览器有cookie(JSESSIONID),共同维护一个会话,存储相同的会话编号。
2、session会话管理
(1)session API
1 | //获取session,默认true |
(2)会话监听器(SessionListener接口)
1 | ①onStart(Session) |
(3)sessionDao
session对象默认保存在jvm内存中。
也可以保存在redis等数据库中,使用sessionDao完成。
3、配置
1 | /** |
九、分布式集群
1、分布式集群会话共享
2、操作
(1)搭建redis服务器
(2)springboot整合redis
依赖
1 | <!-- 操作Redis 只需引入spring-boot-starter-data-redis --> |
配置
1 | #redis数据源 |
测试:
1 | package com.example.springbootshirodemo; |
(3)编写RedisSessionDao
1 | package com.example.springbootshirodemo.shiro; |
(4)shiro配置
1 | /** |
(5)测试
十、前后端分离
1、shiro会话跟踪
服务器有session对象
客户端有cookie
但是在前后端分离开发中,一般不使用cookie。
2、解决方案:
token字符串存储在sessionStorage或者localStorage。
请求时,将token放在请求头中headers。
响应时,将token放在响应头中headers。
3、请求处理
(1)重写
自定义一个会话管理器,重写DefaultWebSessionManager的getSessionId(ServletRequest request, ServletResponse response) 。
该方法作用:从请求对象中获取会话编号。
默认从cookie中获取,改为从请求头中获取指定的键值对。
1 | package com.example.springbootshirodemo.shiro; |
(2)修改配置
1 | /** |
4、响应处理
默认响应,将会话编号保存在cookie中。
改为:将会话编号保存在响应头的token中。
MySessionManager重写onStart方法:
1 | @Override |
5、测试
十一、JWT
1、JWT
Json Web Token
2、会话跟踪
(1)session和cookie
(2)token
(3)jwt
3、JWT token字符串格式
JWTString
1 | Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret) |
header(标头).payload(载荷).Signature(签名)
(1)header(标头)
{
“alg”:”HS256”,
“typ”:”JWT”
}
alg:加密算法名称
type:令牌类型
到目前为止,jwt的签名算法有三种:
HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)
ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)
(2)payload(载荷)
JWT指定七个默认字段供选择:
还可以自定义字段:
username:zhangsan
{
“iss”:”hs”,
“sub”:”java”,
“iat”:”2021-12-01”,
“exp”:”2021-12-08”,
“jti”:”ajkdhfkaldfkahkjfhjka”,
“login”:true,
“username”:”zhangsan”
}
(3)Signature(签名)
通过指定的算法和密钥生成的。
secret:服务器端设计的密钥。
使用指定的算法和密钥,将header和payload生成一个加密字符串。
这个字符串就是我们的Signature签名。
HMACSHA256(base64UrlEncode(header)+”.”+base64UrlEncode(payload),secret)
4、JWT插件
官网推荐了6个Java使用JWT的开源库,其中比较推荐使用的是java-jwt
和jjwt-root
。
5、生成token(签名)
(1)引入依赖
1 | <!--jwt--> |
(2)生成token
1 | /** |
(3)结果
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9. |
我们可以看到,信息发生变化,token字符串生成的也发生了变化。
每次生成的token可能都不一样。
6、解析token(验签)
(1)token的合法性
(2)是否过期
(3)获取数据
1 | /** |