Apache shiro 权限框架
Posted PinkHub
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Apache shiro 权限框架相关的知识,希望对你有一定的参考价值。
Apache shiro学习文档
作者:pinkhub
时间:2021/12/24
技术栈:springboot+mybatisPlus+shiro
未来,我们抱团取暖,结伴而行…
文档目录
第一章 Apache Shiro 概述
Apache Shiro 是一个强大而灵活的开源安全框架,可以干净地处理身份验证、授权、企业会话管理和加密。
身份验证:有时也被称为“登录”,这是一种证明用户真实身份的行为。
授权:访问控制的过程,即确定“谁”可以访问“什么”。
会话管理:管理特定于用户的会话,即使是在非 Web 或 EJB 应用程序中。
密码学:使用密码算法确保数据安全,同时仍然易于使用。
官方文档:https://shiro.apache.org/reference.html
第二章 Apache Shiro 架构
关键字:Subject
、SecurityManager``和``Realms
第三章 Apache Shiro 配置
3.1 配置文件(ini)配置
基于INI配置使DefinitionRealm、definitionRealm生效
# =======================
# Shiro INI configuration
# =======================
[main]
# Objects and their properties are defined here,
# Such as the securityManager, Realms and anything
# else needed to build the SecurityManager
definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
securityManager.realms=$definitionRealm
[users]
# The 'users' section is for simple deployments
# when you only need a small number of statically-defined
# set of User accounts.
jay=1234
[roles]
# The 'roles' section is for simple deployments
# when you only need a small number of statically-defined
# roles.
[urls]
# The 'urls' section is used for url-based security
# in web applications. We'll discuss this section in the
# Web documentation
3.2 配置类配置
@Configuration
public class ShiroConfig
/**
* 创建realm、securityManagert组件交给spring容器管理 等价于shiro.ini配置文件
* @return
*/
@Bean("shiroRealm")
public ShiroRealm shiroRealm()
return new ShiroRealm();
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm)
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm);
return securityManager;
第四章 Apache Shiro 核心
前提
导入shiro依赖:可以选择shiro-spring-boot-starter
、shiro-spring
、shiro-core
的一个。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
tips:
如果选择shiro-core依赖时,版本不要太高,否则IniSecurityManagerFactory
方法过时
4.1 编码和解码算法
shiro提供base64和16进制字符串编码和解码的API支持。
工具类如下:
public class EncodesUtil
/**
* 利用Hex字节转字符串
* @param input 输入字节数组
* @return 字符串
*/
public static String encodeHex(byte[] input)
return Hex.encodeToString(input);
/**
* 利用Hex字符串转字节
* @param input 输入字符串
* @return 字节数组
*/
public static byte[] decodeHex(String input)
return Hex.decode(input);
/**
* 利用Base64格式将字节转字符串
* @param input
* @return
*/
public static String encodeBase64(byte[] input)
return Base64.encodeToString(input);
/**
* 利用Base64格式将字符串转字节
* @param input
* @return
*/
public static byte[] decodeBase64(String input)
return Base64.decode(input);
4.2 散列算法
散列算法用于生成数据的摘要信息,不可逆算法,常用于存储密码,常见的散列算法有:MD5、SHA等,散列的对象:“密码+salt”,salt其实是干扰数据。
散列算法的5种实现类:
tips:其中6种加密实现类继承于SimpleHash
类
关键字:salt→SecureRandomNumberGenerator
password→SimpleHash
摘要算法工具类:用于对密码加密
/*
*@param1:算法名称
*@param2:明文
*@param3:盐值
*@param4;加密次数
public SimpleHash(String algorithmName,Object source,@Nullable Object salt,int hashIterations)
*/
public class DigestsUtil
//算法类型
public static final String SHA1="SHA-1";
//加密次数
public static final Integer ITERATION=512;
/**sha1摘要算法
* @param input 明文字符串
* @param salt 干扰数据
* @return
*/
public static String sha1(String plaintext,String salt)
return new SimpleHash(SHA1,plaintext,salt,ITERATION).toString();
/**
* 随机生成salt
* @return salt(16进制)
*/
public static String createSalt()
SecureRandomNumberGenerator secureRandomNumberGenerator = new SecureRandomNumberGenerator();
return secureRandomNumberGenerator.nextBytes().toHex();
/**
*生成密码和salt的密文
* @param pwd
* @return
*/
public static Map<String,String> entryptPassword(String pwd)
HashMap<String, String> map = new HashMap<>();
String salt=createSalt();
String password=sha1(pwd,salt);
map.put("salt",salt);
map.put("password",password);
return map;
测试类:
System.out.println(DigestsUtil.entryptPassword("123").toString());
运行结果:
password=54eeefc1368c375feeac0f71e1e6c929d4d5d6f1, salt=ba18ca250fd8442eaccfa3a03c3a5530
4.3 认证
认证流程
【第一步】:Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息 UsernamePasswordToken
【第二步】:使用Subject门面获取到封装着用户的数据的标识token subject.login(usernamePasswordToken);
【第三步】:Subject把标识token交给SecurityManager,SecurityManager再把标识token委托给认证器Authenticator进行身份验证。
认证器的作用一般是用来指定如何验证,它规定本次认证用到哪些Realm
【第四步】:认证器Authenticator将传入的标识token,与数据源Realm对比,验证token是否合法
关键字:
doGetAuthenticationInfo
、SimpleAuthenticationInfo
案例一:登陆测试1
数据来自shiro.ini文件
第一步:导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
第二步:创建shiro.ini
#声明用户账号
[users]
jay=1234
第三步:测试代码
public void shiroLogin()
//1.导入ini配置创建工厂
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过工厂构建安全构建器
SecurityManager securityManager = factory.getInstance();
//3.通过工具类让安全构建器生效
SecurityUtils.setSecurityManager(securityManager);
//4.通过工具类获取subject主体
Subject subject = SecurityUtils.getSubject();
//5.构建账号和密码
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jay", "1234");
//6.使用subject登录
subject.login(usernamePasswordToken);
//7.输出状态
System.out.println("登陆状态:"+subject.isAuthenticated());
截图:
案例二:登陆测试2
数据来自数据库
【第一步】:自定义realm
public class DefinitionRealm extends AuthorizingRealm
/*
* 认证方法
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
String loginName = (String)authenticationToken.getPrincipal();
//模拟数据库查询
SecurityServiceImpl securityService = new SecurityServiceImpl();
String password = securityService.findPasswordByUserName(loginName);
if(password=="")
throw new AuthenticationException("账号不存在");
return new SimpleAuthenticationInfo(loginName,password,getName());
/*
*鉴权方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
return null;
【第二步】:创建shiro.ini definitionRealm生效
[main]
definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
securityManager.realms=$definitionRealm
【第三步】:测试
@Test
public void shiroLogin()
//1.导入ini配置创建工厂
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.工厂构建安全构建器
SecurityManager securityManager = factory.getInstance();
//3.通过工具类让安全构建器生效
SecurityUtils.setSecurityManager(securityManager);
//4.通过工具获取subject主体
Subject subject = SecurityUtils.getSubject();
//5.构建账号和密码
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jay", "1234");
//6.使用subject登录
subject.login(usernamePasswordToken);
//7.输出状态
System.out.println("登陆状态:"+subject.isAuthenticated());
案例三:登录测试3
Realm使用散列算法模拟登录
【第一步】:创建shiro.ini,使自定义realm生效
[main]
definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
securityManager.realms=$definitionRealm
【第二步】:创建service接口
public interface SecurityService
Map<String,String> findPasswordByUserName(String userName);
【第三步】:创建service实现类,模拟根据用户名从数据库查询其加密密码、角色列表、权限列表
@Service
public class SecurityServiceImpl implements SecurityService
@Override
public Map<String,String> findPasswordByUserName(String userName)
return DigestsUtil.entryptPassword("123456");
【第四步】:创建realm
public class DefinitionRealm extends AuthorizingRealm
public DefinitionRealm()
//1.指定密码匹配方式
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
//2.指定密码迭代次数
hashedCredentialsMatcher.setHashIterations(DigestsUtil.ITERATION);
//3.生效
setCredentialsMatcher(hashedCredentialsMatcher);
/*
* 认证方法
* */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
String loginName = (String)authenticationToken.getPrincipal();
SecurityServiceImpl securityService = new SecurityServiceImpl();
Map<String, String> map = securityService.findPasswordByUserName(loginName);
if(map.isEmpty())
throw new AuthenticationException("账号不存在");
String password=map.get("password");
String salt = map.get("salt");
return new SimpleAuthenticationInfo(loginName,password, ByteSource.Util.bytes(salt),getName());
/*
鉴权方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
return null;
【第五步】:编写测试类
@Test
public void shiroLogin()
//1.导入ini配置创建工厂
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.工厂构建安全构建器
SecurityManager securityManager = factory.getInstance();
//3.通过工具类让安全构建器生效
SecurityUtils.setSecurityManager(securityManager);
//4.通过工具获取subject主体
Subject subject = SecurityUtils.getSubject();
//5.构建账号和密码
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("pinkhub", "123456");
//6.使用subject登录
subject.login(usernamePasswordToken);
//7.输出状态
System.out.println("登陆状态:"+subject.isAuthenticated());
源码追踪:
将token、info密码匹配结果boolean值,返回subject.isAuthenticated()
4.4 授权
前提:用户必须通过认证;角色和权限存放在数据库中
基本流程
【第一步】:首先调用Subject.isPermitted*/hasRole*
接口,然后委托给SecurityManager
(安全管理器)
【第二步】:SecurityManager
在委托给内部组件Authorizer
(授权器)
【第三步】:Authorizer
再将请求委托给Realm
去做
【第四步】:Realm
将用户请求的参数封装成权限对象,再从我们重写的doGetAuthorizationInfo
方法中获取从数据库中查询到的权限集合
【第五步】:Realm
将用户传入的权限对象,与从数据库查出的权限对象进行一一对比。如果用户传入的权限对象在数据库中查出来的权限对象中,则返回true,否则返回false
关键字:doGetAuthorizationInfo
、SimpleAuthorizationInfo
ShiroConfig配置
配置内容:
(1)创建自定义ShiroDbRealm
实现,用于权限认证、授权、加密方式的管理,同时从数据库中取得相关的角色、资源、用户的信息,然后交于DefaultWebSecurityManager权限管理器管理
(2)创建DefaultWebSecurityManager
权限管理器用于管理DefaultWebSessionManager会话管理器、ShiroDbRealm
(3)创建ShiroFilterFactoryBean
的shiro过滤器指定权限管理器、同时启动连接链及登录URL、未登录的URL的跳转
(4)创建SimpleCookie
,访问项目时,会在客户端中cookie中存放ShiroSession
(5)创建DefaultWebSessionManager
会话管理器定义cookie机制、定时刷新、全局会话超时时间然后交于DefaultWebSecurityManager权限管理器管理
@Configuration
public class ShiroConfig
/**
* 创建ShiroDBRealm交给spring容器
* @return
*/
@Bean("shiroDBRealm")
public ShiroDBRealm shiroDBRealm()
return new ShiroDBRealm();
/**
* 安全管理器
* @param userRealm
* @return
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("shiroDBRealm") ShiroDBRealm userRealm)
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
/**
* 拦截器--拦截请求
* 常用的过滤器:
* anno:无需认证即可访问
* authc:必须认证才能访问
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager)
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//拦截
HashMap<String, String> filterMap = new HashMap<>();
//filterMap.put("/user/login", "anon");
filterMap.put("/user/add", "perms[add]");
filterMap.put("/user/edit", "perms[update]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
//没通过验证,触发登录拦截请求,跳转登陆页面
shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
//触发未认证请求,跳转未授权页面
shiroFilterFactoryBean.以上是关于Apache shiro 权限框架的主要内容,如果未能解决你的问题,请参考以下文章
spring security与apache shiro 权限控制安全框架,那个更实用