IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> shiro-No realms have been configured -> 正文阅读

[Java知识库]shiro-No realms have been configured

描述

Configuration error: No realms have been configured! One or more realms must be present to execute an authentication attempt.
大概意思是找不到你实现的Realm。
但是我的配置文件里已经配置了啊,虽然是看别人的。然后我对比了一下配置文件,我发现别人和我唯一的区别是[main]和[user]
我的:

[user]
realm=orange.shiro.realm.MyRealmLogin
securityManager.realms=$realm

别人的:

[main]
realm=orange.shiro.realm.MyRealmLogin
securityManager.realms=$realm

然后我把我的[user]换成了[main]就行了。

解决办法

下面源码会解释为什么把[]干掉也可以
在你的ini文件中把 [] 全部删除

realm=orange.shiro.realm.MyRealmLogin
securityManager.realms=$realm

或者把你的[xxxxxxx…]换成 [main] 就行了

[main]
realm=orange.shiro.realm.MyRealmLogin
securityManager.realms=$realm

或者换成[]

[]
realm=orange.shiro.realm.MyRealmLogin
securityManager.realms=$realm

源码重点解析

本来想画一个时序图更直观一点,但是太浪费时间了,就不画了。
我使用的版本是1.9.0,IniSecurityManagerFactory已经快被抛弃了,但是我为了入门,还是都了解一遍好。

1. 初始化

  • 先看一下shiro是如何初始化ini文件的
    首先找到IniSecurityManagerFactory工厂类,从构造器里面入手,经过调试,最终定位在了Ini类的load(Scanner scanner)方法上。
public void load(Scanner scanner) {
			 // 这个其实就是[main]里面的main,默认是空字符串,这个跟ini文件[]可有可无有关
        String sectionName = "";
        // 这个是ini文本内容
        StringBuilder sectionContent = new StringBuilder();
        while(scanner.hasNextLine()) {
            String rawLine = scanner.nextLine();
            // 清除左右空白
            String line = StringUtils.clean(rawLine);
            if (line != null && !line.startsWith("#") && !line.startsWith(";")) {
            		// 这里就是读取ini文件中 [] 里面的内容
            		// 这里的逻辑其实就是按照[name]里的name进行内容分组
            		// 根据newSectionName是否为null来进行分组,分组内容放到 Ini.Section中
                String newSectionName = getSectionName(line);
                if (newSectionName != null) {
                    this.addSection(sectionName, sectionContent);
                    sectionContent = new StringBuilder();
                    sectionName = newSectionName;
                    if (log.isDebugEnabled()) {
                        log.debug("Parsing [" + newSectionName + "]");
                    }
                } else {
                    sectionContent.append(rawLine).append("\n");
                }
            }
        }

        this.addSection(sectionName, sectionContent);
    }
  • section名字规则,就是[]
protected static String getSectionName(String line) {
    String s = StringUtils.clean(line);
    // 判断是否是[],如果没有[],就使用默认的""。如果存在[]就截取里面的内容,作为section的键
    return isSectionHeader(s) ? cleanName(s.substring(1, s.length() - 1)) : null;
}

isSectionHeader

protected static boolean isSectionHeader(String line) {
   String s = StringUtils.clean(line);
   return s != null && s.startsWith("[") && s.endsWith("]");
}

2. 赋值

我的代码

@RestController
@RequestMapping("/login")
public class ShiroLogin {

    @GetMapping
    public String login(@RequestParam String account,
                        @RequestParam String password) {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(account, password);
        try {
            subject.login(usernamePasswordToken);
        }catch (Exception e) {
            return "登录失败";
        }
        return "登录成功";
    }
}
  • 在哪里使用?
    从Subject的login入口作为研究对象,一直深入,发现login里面竟然有SecurityManager,这里先放弃探究SecurityManager怎么初始化的,我线程基础很烂。经过调试,我发现实现SecurityManager接口的类是DefaultSecurityManager,其实ctrl + 点击就能找到了…。

找啊找,找到了ModularRealmAuthenticator,这个类有一个realms属性,保存的就是我们实现Realm的类对象。

private Collection<Realm> realms;

但是它在哪里初始化realms的呢??又是在哪里初始化ModularRealmAuthenticator的呢??

最后在AuthenticatingSecurityManager中找到了这行代码

private Authenticator authenticator = new ModularRealmAuthenticator();

额,这里就是初始化 ModularRealmAuthenticator 的地方,继续找初始化realms的地方。
最后在IniSecurityManagerFactory Ini的工厂类中类中找到了这段代码

关于我们这个异常的原因,结合上面的初始化,和下面代码的getConfigSection、createSecurityManager方法可以找到原因

private SecurityManager createSecurityManager(Ini ini) {
   return this.createSecurityManager(ini, this.getConfigSection(ini));
}

// 这里就是我们配置文件ini中关于[]的内容,它默认只有[main] [] 或者 不需要[]
// 所以你的配置文件中存在[sss][user]...都不管用。
private Section getConfigSection(Ini ini) {
   Section mainSection = ini.getSection("main");
   if (CollectionUtils.isEmpty(mainSection)) {
       mainSection = ini.getSection("");
   }

   return mainSection;
}

private SecurityManager createSecurityManager(Ini ini, Section mainSection) {
	this.getReflectionBuilder().setObjects(this.createDefaults(ini, mainSection));
	// 根据section去找到对应的分组,默认就是main和""。这个在初始化那边是按照[]去分组初始化的。
  Map<String, ?> objects = this.buildInstances(mainSection);
  SecurityManager securityManager = this.getSecurityManagerBean();
  boolean autoApplyRealms = this.isAutoApplyRealms(securityManager);
  if (autoApplyRealms) {
  		// 这里就是初始化realms的代码了,如果realms为空,就会抛出我们标题的异常了。
       Collection<Realm> realms = this.getRealms(objects);
       if (!CollectionUtils.isEmpty(realms)) {
           this.applyRealmsToSecurityManager(realms, securityManager);
       }
   }
   return securityManager;
}
// 解析,暂时不研究,猜测关于初始类有关
private Collection<Realm> getRealms(Map<String, ?> instances) {
        List<Realm> realms = new ArrayList();
        Iterator var3 = instances.entrySet().iterator();

        while(true) {
            while(var3.hasNext()) {
                Entry<String, ?> entry = (Entry)var3.next();
                String name = (String)entry.getKey();
                Object value = entry.getValue();
                if (value instanceof RealmFactory) {
                    this.addToRealms(realms, (RealmFactory)value);
                } else if (value instanceof Realm) {
                    Realm realm = (Realm)value;
                    String existingName = realm.getName();
                    if (existingName == null || existingName.startsWith(realm.getClass().getName())) {
                        if (realm instanceof Nameable) {
                            ((Nameable)realm).setName(name);
                            log.debug("Applied name '{}' to Nameable realm instance {}", name, realm);
                        } else {
                            log.info("Realm does not implement the {} interface.  Configured name will not be applied.", Nameable.class.getName());
                        }
                    }

                    realms.add(realm);
                }
            }

            return realms;
        }
    }

总结

其实shiro的工厂在初始化ini的时候把内容进行分组保存到Ini.Section中,根据[]进行分组,划分细节忽略。然后SecutiryManager,在使用的时候只是拿空字符串和main这两个组的内容。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:15:04  更:2022-04-24 09:15:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:23:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码