Shiro基本功能

🍜shiro安全框架 - 图1

Shiro架构

  1. 从外部来看shiro的架构

🍜shiro安全框架 - 图2

Application Code:应用代码

Subject:主体(用户,第三方等与程序对接的会话主体)

Security Manager 安全核心(Shrio 的核心功能)

realm:域,相当于数据源,通过realm存取认证、授权相关数据。(securityManager的授权和认证的逻辑最终都是交给在这里执行的,可以理解为调用数据库查询一系列的信息,然后用这些信息和login的参数进行匹配,一般情况下在项目中采用自定义的realm,因为不同的业务需求不一样)

  1. 内部框架

🍜shiro安全框架 - 图3

环境搭建

导入配置

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.apache.shiro</groupId>
  4. <artifactId>shiro-core</artifactId>
  5. <version>1.9.0</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>commons-logging</groupId>
  9. <artifactId>commons-logging</artifactId>
  10. <version>1.2</version>
  11. </dependency>
  12. </dependencies>

Shiro获取权限相关信息可以通过数据库获取,也可以通过ini配置文件获取

登录认证

首先说明登录认证的基本流程是

  1. 收集用户名和密码也就是用户的凭证
  2. 调用Subject.login()进行登录
  3. 创建自定义的Realm类,继承AuthorizingRealm类,实现doGetAuthenticationInfo()

示例:

  1. package com.sftest.logintest;
  2. import org.apache.shiro.SecurityUtils;
  3. import org.apache.shiro.authc.AuthenticationException;
  4. import org.apache.shiro.authc.IncorrectCredentialsException;
  5. import org.apache.shiro.authc.UnknownAccountException;
  6. import org.apache.shiro.authc.UsernamePasswordToken;
  7. import org.apache.shiro.config.IniSecurityManagerFactory;
  8. import org.apache.shiro.mgt.SecurityManager;
  9. import org.apache.shiro.subject.Subject;
  10. public class logintest {
  11. public static void main(String[] args) {
  12. //1. 初始化获取SecurityManager
  13. IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
  14. SecurityManager securityManager = factory.getInstance();
  15. SecurityUtils.setSecurityManager(securityManager);
  16. //2. 获取Subject对象
  17. Subject subject = SecurityUtils.getSubject();
  18. //3. 创建token对象,web应用用户名密码从页面传递
  19. UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "z3");
  20. //4. 完成登录
  21. try {
  22. subject.login(token);
  23. System.out.println("登录成功");
  24. } catch (UnknownAccountException e) {
  25. e.printStackTrace();
  26. System.out.println("用户不存在");
  27. } catch (IncorrectCredentialsException e) {
  28. e.printStackTrace();
  29. System.out.println("密码错误");
  30. } catch (AuthenticationException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. }

问题解决:

F:Ctrl+Alt+V快捷键突然使用不了?

Q:查看是否是快捷键冲突占用,QQ音乐的快捷键冲突,关闭即可

F:报错java: JDK isn’t specified for module

Q:IDEA自带的JDK有问题,换成自己安装的JDK就可以了

示例结果

🍜shiro安全框架 - 图4

接下来对这段简单的登录验证逻辑进行Debug,查看具体代码实现。

login处下个断点,调试走到这里,可以看到实际上调用的login方法是在DelegatingSubject类中的login方法

到这里看到username和password的值,然后看到实际上是securitymanager调用login方法

🍜shiro安全框架 - 图5

继续跟进,DefaultSecurityManager下的login方法,已经走到了authenticate()

🍜shiro安全框架 - 图6

走到AuthenticatingSecurityManager类下的authenticate方法,一个认证器(authenticator)调用了authenticate方法

🍜shiro安全框架 - 图7

还没看到方法实现的地方,继续跟进,最终在AbstractAuthenticator类中找到了token,发现又交给了另一个方法去处理了

🍜shiro安全框架 - 图8

真能藏,然后到这里它改名字了,叫authenticationToken,然后这里有个逻辑,同步也是发现这里用到了realm对象,调试到这一步的时候就已经发现size=1,所以这里调用的应该是doSingleRealmAuthentication方法

🍜shiro安全框架 - 图9

继续跟进,到了doSingleRealmAuthentication类中,又发现token传递到了一个方法里面

🍜shiro安全框架 - 图10

继续跟,到这里终于看到了有点逻辑了,看下它又做了什么

好像是获取这个token相关的缓存了,但是没有获取到,所以info为null,然后就继续看,发现处理token的方法是doGetAuthenticationInfo

🍜shiro安全框架 - 图11

而 Shiro的认证依赖AuthenticatingRealm里的getAuthenticationInfo方法,该方法会调用我们自定义的认证方法<font style="color:rgb(77, 77, 77);">doGetAuthenticationInfo</font>,获取本次认证的结果。

经过此处给info赋值了。

🍜shiro安全框架 - 图12

最终走完过程(没有走rememberme这个逻辑)

🍜shiro安全框架 - 图13

角色授权

授权:访问控制(访问页面,编辑数据,页面操作),在授权中用到的几个对象:Subject(主体),Resource(资源),Permission(权限),Role(角色)

  1. 角色授权的三种方式
  • 代码式:通过if{}else{}来进行控制
  • 注解:使用注解,有权限则进一步调用,没权限抛出异常@RequiresRoles
  • 标签:Shiro自带标签<shiro:hasRole name="admin"></shiro:hasRole>

设置角色需要在shiro.ini中进行配置

判断用户是否拥有角色权限:

  1. //shiro.ini
  2. [users]
  3. zhangsan=z3, role1, role2
  4. //.java
  5. boolean role1 = subject.hasRole("role1");

用户操作权限,在配置文件中进行设置

  1. //shiro.ini
  2. [roles]
  3. role1=user:insert, user:select
  4. //.java
  5. boolean permitted = subject.isPermitted("user:insert");

还有一个方法来检查权限

  1. subject.checkPermission("user:select");

没有返回值,有权限直接向下执行,没有权限抛出异常。

🍜shiro安全框架 - 图14

Shiro加密

  1. Md5加密测试

为保障安全,还可以进行md5多次迭代加密。

  1. package com.sftest.logintest;
  2. import org.apache.shiro.crypto.hash.Md5Hash;
  3. public class CryptoTest {
  4. public static void main(String[] args) {
  5. //MD5加密测试
  6. String password = "m0rewuwuwuwu";
  7. Md5Hash md5Hash = new Md5Hash(password);
  8. System.out.println(md5Hash.toHex());
  9. //加盐
  10. Md5Hash woc = new Md5Hash(password, "woc");
  11. System.out.println(woc.toHex());
  12. //多次迭代加盐
  13. Md5Hash hash = new Md5Hash(password, "woc", 3);
  14. System.out.println(hash);
  15. //使用父类进行加密
  16. SimpleHash simpleHash = new SimpleHash("MD5","m0rewuwuwuwu","woc",3);
  17. System.out.println(simpleHash);
  18. }
  19. }

然后看了下MD5加密的源码,其实是

🍜shiro安全框架 - 图15

观察使用父类进行加密的代码,似乎明白了点什么。