简介
Shiro和Spring Security都是Java领域中常用的安全框架。它执行身份验证、授权、加密和会话管理。它们都提供了身份认证和授权功能,可以帮助开发者快速构建安全的应用程序。Shiro相对来说更加轻量级,易于使用和扩展,而Spring Security则更加完善和强大,可以与Spring框架无缝集成。选择哪个框架取决于具体的需求和项目规模。
Shiro核心架构
Subject
Subject
即主体,外部应用与subject进行交互,subject记录了当前的操作用户,将用户的概念理解为当前操作的主体。外部程序通过subject进行认证授权,而subject是通过SecurityManager安全管理器进行认证授权
SecurityManager
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等SecurityManager
是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口
Authenticator
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器
Realm
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息
不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码
SessionManager
sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录
SessionDAO
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库
CacheManager
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能
Cryptography
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
主配置文件
Shiro的主配置文件是shiro.ini。它是Apache Shiro安全框架的核心配置文件,用于定义安全策略、认证和授权规则等。在shiro.ini文件中,你可以配置Shiro的Realm、Session管理器、密码加密算法、缓存等各种组件和属性。通过编辑shiro.ini文件,你可以自定义Shiro的行为和功能,以满足你的应用程序安全需求。
Apache Shiro 与Spring Security区别
- Shiro 用于中小型项目比较常见,简单易上手,可以支持多种环境
- Shiro 可以不跟任何的框架或者容器绑定,可独立运行
- Spring Security:一般多用于spring环境,中大型项目,更强大
- Spring Security 则必须要有Spring环境
Shiro中的认证
什么是认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确
认证的几种状态
- UnknownAccountException:用户名错误
- IncorrectCredentialsException:密码错误
- DisabledAccountException:账号被禁用
- LockedAccountException:账号被锁定
- ExcessiveAttemptsException:登录失败次数过多
- ExpiredCredentialsException:凭证过期
基于ini认证
1.新建项目 --导入依赖
--新建 配置文件ini
2. shiro帮我们创建用户
创建令牌 ,类比页面传入的账号密码
密码错误-- IncorrectCredentialsException
账号错误-- UnknownAccountException
源码分析--
导入依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.2</version>
</dependency><dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
<scope>provided</scope>
</dependency>
编写ini,shiro默认支持的是ini配置的方式(只是演示) shiro-au.ini,真实项目使用xml
#用户的身份、凭据
[users]
zhangsan=555
xiaoluo=666
使用 Shiro 相关的 API 完成身份认证
@Test
public void testLogin(){
//创建Shiro的安全管理器,是shiro的核心
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//加载shiro.ini配置,得到配置中的用户信息(账号+密码)
IniRealm iniRealm = new IniRealm("classpath:shiro-au.ini");
securityManager.setRealm(iniRealm);
//把安全管理器注入到当前的环境中
SecurityUtils.setSecurityManager(securityManager);
//无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断
Subject subject = SecurityUtils.getSubject();
System.out.println("认证状态:"+subject.isAuthenticated());
//创建令牌(携带登录用户的账号和密码)
UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","666");
//执行登录操作(将用户的和 ini 配置中的账号密码做匹配)
subject.login(token);
System.out.println("认证状态:"+subject.isAuthenticated());
//登出
//subject.logout();
//System.out.println("认证状态:"+subject.isAuthenticated());
}
自定义realm — 授权
思路:
1.获取当前登录用户的id/name
//获取当前登录用户对象 ,登录传过来的第一个参数
方式一 :principals.getPrimaryPrincipal();
方式二:SecurityUtils.getSubject().getPrincipal();2.以用户id作为条件查询mysql数据,查询该用户拥有角色/权限
//假装查数据
//List<String> roles= roleService.queryByEmployee(Employee.getId());
//List<String> //List<String> roles= permissionService.queryByEmployee(Employee.getId());
= permissionService.queryByEmployee(Employee.getId());
3.将角色与权限封装到授权info对象中
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles) //添加角色
info.addStringPermission(permission);//添加权限
实现:
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前用户对象
Employee employee = (Employee)principalCollection.getPrimaryPrincipal();
//以用户id查询数据库,判断该角色/用户是否拥有权限 模拟
List<String> roles= Arrays.asList("seller");
List<String> permissions= Arrays.asList("customer:list","customer:save");
//封装到info对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
public class ShiroDemo {
@Test
public void testLogin(){
//创建Shiro的安全管理器,是shiro的核心
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//加载shiro.ini配置,得到配置中的用户信息(账号+密码)
IniRealm iniRealm = new IniRealm("classpath:shiro-author.ini");
//自定义Realm,查出用户信息
Realm realm = new Realm();
securityManager.setRealm(realm);
//把安全管理器注入到当前的环境中
SecurityUtils.setSecurityManager(securityManager);
//无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断
Subject subject = SecurityUtils.getSubject();
System.out.println("认证状态:"+subject.isAuthenticated());
//创建令牌(携带登录用户的账号和密码)
UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","12");
//执行登录操作(将用户的和 ini 配置中的账号密码做匹配)
subject.login(token);
System.out.println("认证状态:"+subject.isAuthenticated());
//登出
//subject.logout();
//System.out.println("认证状态:"+subject.isAuthenticated());
//判断用户是否有某个角色
System.out.println("hr:"+subject.hasRole("hr"));
System.out.println("seller:"+subject.hasRole("seller"));
//是否同时拥有多个角色
System.out.println("是否同时拥有role1和role2:"+subject.hasAllRoles(Arrays.asList("hr", "seller")));
boolean[] booleans = subject.hasRoles(Arrays.asList("hr", "seller"));
//System.out.println(booleans);
//check开头的是没有返回值的,当没有权限时就会抛出异常
subject.checkRole("seller");
//判断用户是否有某个权限
System.out.println("user:delete:"+subject.isPermitted("user:delete"));
subject.checkPermission("customer:list");
}
}
//如果用户是超级管理员
//设置超级管理员角色
info.addRole("admin")
//设置超级管理员权限
info.addStringPermission("*:*");