本文主要是介绍【Shiro】Shiro 的学习教程(一)之快速入门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- 1、Shiro 简介
- 2、Shiro 认证、授权
- 2.1、认证
- 2.2、授权
- 3、快速入门
- 4、自定义 Realm
- 5、加密
- 6、实现授权
1、Shiro 简介
Shiro 官网:https://shiro.apache.org/
Shiro
是一个功能强大且易于使用的 Java 安全框架,它执行身份验证、授权、加密和会话管理。使用 Shiro 易于理解的 API,您可以快速轻松地保护任何应用程序—从最小的移动应用程序到最大的 web 和企业应用程序
Shiro 的架构图如下:
各组件说明如下:
Subject
:即主体,外部应用与 Subject 进行交互,Subject 记录了当前的操作用户,将用户的概念理解为当前操作的主体。外部程序通过 Subject 进行认证授权,而 Subject 是通过SecurityManager 安全管理器进行认证授权SecurityManager
:即安全管理器,对全部的 Subject 进行安全管理,它是 Shiro 的核心,负责对所有的 Subject 进行安全管理。通过 SecurityManager 可以完成 Subject的认证、授权等,实质上SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理等(SecurityManager
是一个接口,继承了Authenticator
、Authorizer
、SessionManager
这三个接口)Authenticator
:即认证器,对用户身份进行认证,Authenticator 是一个接口,Shiro 提供ModularRealmAuthenticator
实现类,通过ModularRealmAuthenticator
基本上可以满足大多数需求;也可以自定义认证器Authorizer
:即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限Realm
:即领域,相当于 Datasource 数据源,SecurityManager 进行安全认证,需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 Realm 就需要从数据库获取用户身份信息。LDAP 数据源的JndiLdapRealm
,JDBC数 据源的JdbcRealm
,ini 文件数据源的iniRealm
,Properties 文件数据源的PropertiesRealm
,等等,我们也可以插入自己的 Realm 实现来代表自定义的数据源SessionManager
:即会话管理,Shiro 框架定义了一套会话管理,它不依赖 web 容器的 session,所以 Shiro 可以使用在非 web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录SessionDAO
:即会话 dao,是对 session 会话操作的一套接口,比如要将 session 存储到数据库,可以通过 jdbc 将会话存储到数据库CacheManager
:即缓存管理,将用户权限数据存储在缓存,这样可以提高性能Cryptography
:即密码管理,Shiro 提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能
2、Shiro 认证、授权
2.1、认证
身份认证:就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确
在认证过程中,涉及三个概念:
- Subject:主体。主体可以是用户、程序等,进行认证的都称为主体
- Principal:身份信息,是主体(Subject)进行身份认证的标识,标识必须具有唯一性。如:用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)
- Credential:凭证信息。只有主体自己知道的安全信息,如密码、证书等
2.2、授权
授权方式:
- 基于角色
- 基于资源
权限字符串
权限字符串的规则是:资源标识符:操作:资源实例标识符,意思是:对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用 * 通配符
如:
- 用户创建权限:user:create,或user:create:*
- 用户修改实例 001 的权限:user:update:001
- 用户实例 001 的所有权限:user:*:001
3、快速入门
引入 POM 依赖:
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.5.3</version>
</dependency>
shiro.ini
:位于 resources
资源目录下
# 约定写法
[users]
# 用户名=密码(,角色)
christy=123456,admin
zzc=666666,super
tide=654321,guest[roles]
super=*
admin=guest:*
guest=user:delete:zhangsan
测试类:
public class ShiroAuthenticatorTest2 {public static void main(String[] args) {//准备阶段//// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.给安全管理器设置 RealmsecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));// 3.给全局安全工具类 SecurityUtils 设置安全管理器SecurityUtils.setSecurityManager(securityManager);// 4.拿到当前的 SubjectSubject subject = SecurityUtils.getSubject();// 5.创建令牌AuthenticationToken token = new UsernamePasswordToken("christy","123456");try {//认证阶段//// 6.用户认证System.out.println("认证状态:" + subject.isAuthenticated());subject.login(token);System.out.println("认证状态:" + subject.isAuthenticated());} catch (UnknownAccountException e){e.printStackTrace();System.out.println("认证失败:用户不存在!");} catch (IncorrectCredentialsException e){e.printStackTrace();System.out.println("认证失败:密码不正确!");} catch (Exception e){e.printStackTrace();}//授权//// 7.角色授权System.out.println(subject.hasRole("admin"));// 8.资源授权System.out.println(subject.isPermitted("guest:update:01"));}
}
4、自定义 Realm
查看源代码知:有两个抽象方法:
- 认证:
AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken)
- 授权:
AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection)
且:AuthorizingRealm
继承于 AuthenticatingRealm
。意味着自定义的 Reaml 只要继承 AuthorizingRealm
,便可重写认证、授权方法
自定义 Reaml:
public class CustomerRealm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();System.out.println(principal);// 模拟根据身份信息从数据库查询if("christy".equals(principal)){// 参数说明:用户名 | 密码 | 当前realm的名字return new SimpleAuthenticationInfo(principal,"123456", this.getName());}return null;}}
测试:
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(new CustomerRealm());
5、加密
Shiro 中提供了一整套的加密算法,并且提供了随机盐。Shiro 使用指定的加密算法将用户密码和随机盐进行加密,并按照指定的散列次数将散列后的密码存储在数据库中。由于随机盐每个用户可以不同,这就极大的提高了密码的安全性
Md5Hash:有三个构造方法:
public Md5Hash(Object source)
:只加密public Md5Hash(Object source, Object salt)
:加密、加盐public Md5Hash(Object source, Object salt, int hashIterations)
:加密、加盐、加散列次数
加密后的 Realm:
public class CustomerRealm extends AuthorizingRealm {@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();System.out.println(principal);// 模拟根据身份信息从数据库查询if("christy".equals(principal)){// 参数说明:用户名 | 密码(加密后的密码) | 盐 | 当前realm的名字return new SimpleAuthenticationInfo(principal,"41a4e25bcf1272844e38b19047dd68a0", ByteSource.Util.bytes("1q2w3e"), this.getName());}return null;}}
加密后的密码如下:
Md5Hash md5Hash03 = new Md5Hash("123456","1q2w3e", 1024);
// 41a4e25bcf1272844e38b19047dd68a0
System.out.println(md5Hash03.toHex());
测试:
public class ShiroAuthenticatorTest {public static void main(String[] args) {// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.给安全管理器设置 Realm//securityManager.setRealm(new IniRealm("classpath:shiro.ini"));CustomerRealm customerRealm = new CustomerRealm();// 3.为 Realm 设置凭证匹配器HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();// 设置加密算法credentialsMatcher.setHashAlgorithmName("md5");// 设置hash次数credentialsMatcher.setHashIterations(1024);customerRealm.setCredentialsMatcher(credentialsMatcher);securityManager.setRealm(customerRealm);// 4.给全局安全工具类 SecurityUtils 设置安全管理器SecurityUtils.setSecurityManager(securityManager);// ... }
}
【注意】:此代码中给 Reaml 设置了 CredentialsMatcher(HashedCredentialsMatcher
),代码默认使用 SimpleCredentialsMatcher
CredentialsMatcher
结构图如下:
6、实现授权
CustomerRealm2
:
public class CustomerRealm2 extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {// 从系统返回的身份信息集合中获取主身份信息(用户名)String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal();System.out.println("用户名: "+primaryPrincipal);// TODO 根据用户名获取当前用户的角色信息,以及权限信息SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();//将数据库中查询角色信息赋值给权限对象simpleAuthorizationInfo.addRole("admin");simpleAuthorizationInfo.addRole("user");//将数据库中查询权限信息赋值个权限对象simpleAuthorizationInfo.addStringPermission("user:*:01");simpleAuthorizationInfo.addStringPermission("product:create");return simpleAuthorizationInfo;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 在token中获取用户名String principal = (String) authenticationToken.getPrincipal();System.out.println(principal);// 模拟根据身份信息从数据库查询if("christy".equals(principal)){// 参数说明:用户名 | 密码 | 当前realm的名字return new SimpleAuthenticationInfo(principal,"123456", this.getName());}return null;}}
ShiroAuthenticatorTest3
:
public class ShiroAuthenticatorTest3 {public static void main(String[] args) {// 1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.给安全管理器设置 RealmCustomerRealm2 customerRealm = new CustomerRealm2();securityManager.setRealm(customerRealm);// 3.给全局安全工具类 SecurityUtils 设置安全管理器SecurityUtils.setSecurityManager(securityManager);// 4.拿到当前的 SubjectSubject subject = SecurityUtils.getSubject();// 5.创建令牌AuthenticationToken token = new UsernamePasswordToken("christy","123456");try {// 6、用户认证System.out.println("认证状态:" + subject.isAuthenticated());subject.login(token);System.out.println("认证状态:" + subject.isAuthenticated());} catch (UnknownAccountException e){e.printStackTrace();System.out.println("认证失败:用户不存在!");} catch (IncorrectCredentialsException e){e.printStackTrace();System.out.println("认证失败:密码不正确!");} catch (Exception e){e.printStackTrace();}if (subject.isAuthenticated()) {// 基于角色权限控制System.out.println(subject.hasRole("admin"));//基于多角色权限控制(同时具有)System.out.println(subject.hasAllRoles(Arrays.asList("admin", "super")));//是否具有其中一个角色boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));for (boolean aBoolean : booleans) {System.out.println(aBoolean);}System.out.println("==============================================");System.out.println("权限:"+subject.isPermitted("user:update:01"));System.out.println("权限:"+subject.isPermitted("product:create:02"));//分别具有那些权限boolean[] permitted = subject.isPermitted("user:*:01", "order:*:10");for (boolean b : permitted) {System.out.println(b);}//同时具有哪些权限boolean permittedAll = subject.isPermittedAll("user:*:01", "product:create:01");System.out.println(permittedAll);}}
}
这篇关于【Shiro】Shiro 的学习教程(一)之快速入门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!