描述
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) {
String sectionName = "";
StringBuilder sectionContent = new StringBuilder();
while(scanner.hasNextLine()) {
String rawLine = scanner.nextLine();
String line = StringUtils.clean(rawLine);
if (line != null && !line.startsWith("#") && !line.startsWith(";")) {
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);
}
protected static String getSectionName(String line) {
String s = StringUtils.clean(line);
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));
}
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));
Map<String, ?> objects = this.buildInstances(mainSection);
SecurityManager securityManager = this.getSecurityManagerBean();
boolean autoApplyRealms = this.isAutoApplyRealms(securityManager);
if (autoApplyRealms) {
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这两个组的内容。
|