shiro框架
一、简介
1、简述shiro框架
一个用于应用程序安全验证和授权的 Java 安全框架。它提供了一系列的技术,例如认证、授权、会话管理和安全数据管理,可以帮助开发者更容易地实现应用程序安全性。
2、Shiro与SpringSecurity的对比
- 1、SpringSecurity基于Spring开发,项目若使用Spring作为基础,配合SpringSecurity做权限更加方便,而Shiro需要和Spring进行整合开发;
- 2、SpringSecurity功能比Shiro更加丰富些,例如安全维护方面
- 3、SpringSecurity社区资源相比Shiro更加丰富
- 4、Shiro的配置和使用比较简单,SpringSecurity上手复杂些
- 5、Shiro依赖性低,不需要任何框架和容器,可以独立运行,SpringSecurity依赖Spring容器
- 6、Shiro不仅仅可以使用在web中,他可以工作在任何环境中。在集群会话时Shiro最重要的一个好处或许就是它的会话独立于容器的。
3、基本功能
- Authentication:⾝份认证/登录,验证⽤⼾是不是拥有相应的⾝份;
- Authorization:授权,即权限验证,验证某个已认证的⽤⼾是否拥有某个权限;即判 断⽤⼾是否能进⾏什么操作。如:验证某个⽤⼾是否拥有某个⻆⾊。或者细粒度的验证某 个⽤⼾对某个资源是否具有某个权限;
- Session Manager:会话管理,即⽤⼾登录后就是⼀次会话,在没有退出之前,它的所 有信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,⽽不是明⽂存 储;
- Web Support:Web ⽀持,可以⾮常容易的集成到Web 环境;
- Caching:缓存,⽐如⽤⼾登录后,其⽤⼾信息、拥有的⻆⾊/权限不必每次去查,这样 可以提⾼效率;
- Remember Me:记住我,这个是⾮常常⻅的功能,即⼀次登录后,下次再来的话可以⽴ 即知道你是哪个⽤⼾
4、Shiro架构
二、基本使用
1、环境搭建
1.1 prom文件
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
1.2 INI文件
- Shiro获取权限相关信息,可以通过数据库获取,也可以通过INI配置文件获取
- 在resource文件目录下新建shiro.ini文件
#定义⽤⼾信息
#格式:⽤⼾名=密码,⻆⾊1,⻆⾊2,....
[users]
zhangsan=123,admin
lisi=456,manager,seller
wangwu=789,clerk
# ----------------
2、 登录认证
- 1、收集用户身份、凭证,即用户名、密码
- 2、调用
Subject.login
进行登录,如果失败将得到相应的AuthenticationException
异常,根据异常提示用户错误信息否则登录成功 - 3、创建自定义的Realm类,继承
org.apache.shiro.realm.AuthenticatingRealm
类,实现doGetAuthenticationInfo()
方法
- 实现例子
public class Main {
public static void main(String[] args) {
//1、初始化获取SecurityManager
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//2、获取Subject对象
Subject subject = SecurityUtils.getSubject();
//3、创建token对象,web应用用户名密码从页面传递
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
//4、完成登录
try {
subject.login(token);
System.out.println("登录成功");
}
catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名不存在");
}
catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
catch (AuthenticationException e) {
throw new RuntimeException(e);
}
}
}
3、角色、授权
3.1 概念
- 授权(访问控制),即在应用中控制谁访问哪些资源(如访问页面资源、编辑资源、页面操作等)。在授权中需要了解的几个关键对象:主题(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
- 主体(Subject):访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源
- 资源(Resource):在应用中用户可以访问的URL,比如访问JSP页面、查看\编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只有授权后才能访问
- 权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权利。即权限表示在应用中用户不能访问某个资源,如:访问用户列表查看/新增/修改/删除用户数据(即很多时候都是CRUD式权限控制)等。权限代表了用户没有操作某个资源的权利,即反映在某个资源上的操作允不允许
- Shiro支持粗粒权限(如用户模板的所有权限)细粒度权限(操作某个用户的权限,即实例级别)
- 角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师都是角色,不同的角色拥有一组不同的权限
3.2 授权方式
- 1、编程式:通过写
if/else
授权代码完成
if(subject.hasRole("admin")){
//有权限
}else{
//无权限
}
- 2、注解式:通过在执行的Java方法上防置相应的注解完成,没有权限将抛出相应的异常
@RequiresRole("admin")
public void hello(){
//有权限
}
- 3、JSP/GSP标签:在JSP/GSP页面通过相应的标签完成
<shiro:hasRole name="admin">
<!--有权限-->
</shiro:hasRole>
3.3 授权流程
- 首先调用
Subject.isPermitted*/hasRole*
接口,其会委托给SecurityMannager
,而SecurityMannager
接着回委托给Authorizer
Authorizer
是真正的授权者,如果调用如isPermitted
(“user:view”),其首先会通过PermissionResolver
把字符串转换成相应的Permission
实例- 在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限
Authorizer
会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer
进行循环判断,如果匹配如isPermitted*/hasRole*
会返回true,否则返回false表示授权失败
3.4 授权实例
try {
subject.login(token);
System.out.println("登录成功");
//5、判断角色
boolean hasRole = subject.hasRole("role1");
System.out.println("是否拥有此角色="+hasRole);
//6、判断权限
boolean permitted = subject.isPermitted("user:insert");
System.out.println("是否拥有此权限="+hasRole);
//也可以用checkPermission方法,但没有返回值,没权限抛异常
subject.checkPermission("user:delete");
}
catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名不存在");
}
catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
catch (AuthenticationException e) {
throw new RuntimeException(e);
}
4、Shiro加密(MD5)
public class ShiroMD5 {
public static void main(String[] args) {
//密码明文
String password="123";
//使用MD5加密
Md5Hash md5Hash = new Md5Hash(password);
System.out.println(md5Hash);
//在密码明文后再拼接一个字符串,然后再进行加密,加盐
Md5Hash md5Hash1 =new Md5Hash(password,"wangkay");
System.out.println("md5Hash1 = " + md5Hash1.toHex());
//为了保证安全,避免破解还可以多次迭代加密,加很多盐
Md5Hash md5Hash2 =new Md5Hash(password,"wangkay",3);
System.out.println("md5Hash2 = " + md5Hash2);
//使用父类进行带盐3次加密
SimpleHash simpleHash = new SimpleHash("MD5",password,"wangkay",3);
System.out.println("simpleHash = " + simpleHash.toHex());
}
}
5、Shiro自定义登录认证
[main]
md5CredentialsMatcher=org.apache.shiro.authc.credential.Md5credentialsMatcher
md5CredentialsMatcher.hashIterations=3
myrealm=org/example/maintest/MyRealm.java
myrealm.credentialsMatcher=$md5CredentialsMatcher
securityManager.realms=$myrealm
[users]
zhangsan=0a6b3234beefb30de9205eb3285664e0,role1,role2
lisi=456,manager,seller
wangwu=789,clerk
# ----------------
[roles]
role1:user:insert,user:select
public class MyRealm extends AuthenticatingRealm {
//自定义登录认证方法,Shiro的login的方法底层会调用该类的认证方法进行认证
//需要配置自定义的realm生效,在ini文件中可以配置,在springboot中配置
//该方法只是进行获取对比的信息,认证步骤还是按照Shiro底层逻辑认证完成
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1、获取身份信息
String principal = token.getPrincipal().toString();
//2、获取凭证信息
String password = new String((char[]) token.getCredentials());
System.out.println("认证用户信息:"+principal+"----"+password);
//3、访问数据库,获取存储的用户信息
if (principal.equals("zhangsan")){
//数据库中存储的加盐3次迭代的密码
String pwdInfo = "0a6b3234beefb30de9205eb3285664e0";
//创建封装校验逻辑对象,封装
AuthenticationInfo info = new SimpleAuthenticationInfo(
token.getPrincipal(),
pwdInfo,
ByteSource.Util.bytes("wangkay"),
token.getPrincipal().toString());
}
//4、创建封装校验逻辑对象,封装数据返回
return null;
}
}