什么是Apache Shiro?
Apache Shiro 是一个强大而灵活的开源安全框架,可以干净地处理身份验证、授权、企业会话管理和加密。
shiro可以做的事:
- 对用户进行身份验证以验证其身份
- 对用户执行访问控制 :
- 确定是否为用户分配了特定的安全角色
- 确定是否允许用户做某事
- 在任何环境中使用会话 API,即使没有 Web 或 EJB 容器
- 在身份验证、访问控制或会话生命周期期间对事件做出反应。
- 聚合 1 个或多个用户安全数据的数据源,并将其全部呈现为单个复合用户“视图”。
- 启用单点登录 (SSO) 功能
- 无需登录即可为用户关联启用“记住我”服务
Apache Shiro的特性:
Shiro 的目标是 Shiro 开发团队所说的“应用程序安全的四大基石”——身份验证、授权、会话管理和密码学:
-
身份验证(Authentication):有时也称为“登录”,这是证明用户就是他们所说的身份的行为。
-
授权(Authorization):访问控制的过程,即确定“谁”可以访问“什么”。
-
会话管理(Session Management):管理特定于用户的会话,即使是在非 Web 或 EJB 应用程序中。
-
密码加密(Cryptography):使用密码算法确保数据安全,同时仍然易于使用。
还有一些附加功能:
- Web支持: Shiro 的 Web 支持 API 有助于轻松保护 Web 应用程序。
- 缓存(Caching):可确保安全操作保持快速高效。
- 并发(Concurrency):Apache Shiro 通过其并发特性支持多线程应用程序。
- 测试(Run As):测试支持可帮助您编写单元和集成测试,并确保您的代码按预期得到保护。
- 记住我(Remember Me):记住跨会话的用户身份。
Apache Shiro架构:
Shiro 的架构有 3 个主要概念:Subject
、SecurityManager
和Realms
。下图是这些组件如何交互的高级概述,我们将在下面介绍每个概念:
-
Subject:
封装了当前登录用户的信息,并且Subject执行任何操作都必须调用SecurityManager。
-
SecurityManager:
是 Shiro 架构的核心,相当于是一个工具箱,调用里面的各种工具(组件),这些组件共同形成一个对象图。 -
Realms:
是一个特定的dao,底层访问数据库,并将返回的结果交给SecurityManager,是安全数据与应用程序连接的桥梁。
核心架构图:
springboot集成了Apache Shiro:
1.首先添加Shiro Spring web starter依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.7.1</version>
</dependency>
2.启动类或者配置类上添加一个realm对象:
@Bean
public Realm realm() {
...
}
3.设置访问权限拦截器ShiroFilterChainDefinition:
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
// logged in users with the 'admin' role
chainDefinition.addPathDefinition("/user/*/*", "anon");//允许匿名访问,并且匿名访问的要放到前面
chainDefinition.addPathDefinition("/**", "authc");//需要认证方可访问
// logged in users with the 'document:read' permission
return chainDefinition;
}
4.在Realm中编写认证授权流程:
/**
* 定义shiro realm对象,基于此对象获取用户认证和授权信息,
* 假如将来你的项目只做认证,不做授权,则继承继承AuthenticatingRealm对象即可
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private SysUserService service;
@Autowired
private MenuDao menuDao;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo
(PrincipalCollection principalCollection) {
//获取登录用户
SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
//基于登录用户id,查询用户权限
Set<String> strings = menuDao.selectUserPermissions(user.getId());
//封装用户权限信息
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.setStringPermissions(strings);
return info;
}
/**获取并封装认证信息
* @param authenticationToken 为封装了客户端认证信息的一个令牌对象
*/
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo
(AuthenticationToken authenticationToken)
throws AuthenticationException {
//获取客户端提交的用户名
String username = ((UsernamePasswordToken) authenticationToken).getUsername();
//基于用户名查询用户信息并校验
SysUser user = service.findUserByUsername(username);
if(user==null)throw new UnknownAccountException();
if(user.getValid()==0)throw new LockedAccountException();
//封装用户信息
ByteSource salt = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,user.getPassword(),salt,getName());
return info;
}
@Override
public CredentialsMatcher getCredentialsMatcher() {
//"MD5"为加密的方式
HashedCredentialsMatcher matcher=new HashedCredentialsMatcher("MD5");
//"1"代表加密的次数
matcher.setHashIterations(1);
return matcher;
}
}
5.Controller层通过 SecurityUtils来获取一个Subject对象
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken user=new UsernamePasswordToken(username,password);
token.setRememberMe(true);//设置记住我
subject subject.login(user);
通过Subject的login方法提交当前用户信息到ShiroSecrityManger,login方法需要一个token对象,最终是通过Subject对象的login方法将用户的登录信息提交到ShiroSecrityManger中。
6.授权还需要在Controller层添加@RequiresPermissions("授权标识")
/**
* @RequiresPermissions 描述方法时,此方法为一个授权切入点方法,我们在
* 访问此方法时就需要授权,有权限则可以访问,没有权限则抛出异常。那如何
* 判定用户有没有访问此方法的权限呢?当我们在方法时,shiro框架底层会获取
* 此方法上的 @RequiresPermissions注解,进而取到注解中的权限标识,然后
* 会调用subject对象的checkPermissions(权限标识)方法检测用户是否有权限。
* 这个方法的权限检测调用流程分析?
* subject->SecurityManager-Authorize-->Realm
*/
如果失效,可能是没有添加:
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setUsePrefix(false);
return advisorAutoProxyCreator;
}
这段代码对控制层方法上的注解有效(例如@GetMapping)
7.缓存的配置:
/**配置授权缓存管理器,默认应用的缓存是shiro框架内置的缓存对象,
* 缓存实现就是一个map.*/
@Bean
protected CacheManager shiroCacheManager() {
return new MemoryConstrainedCacheManager();
}
这里缓存的是授权标识,标识如果访问的是同一个权限,就不会反复查询数据库权限,提高了程序的效率。
注:如果数据库更改了权限,并且设置了Shiro缓存,呢么就得重启服务或者清除缓存
可能不一定准确,请大神指出不足,谢谢