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这两个组的内容。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值