最近在介绍Shiro所以打算将shiro中的相关源代码给分析下。因为内容比较多,所以会分几篇来介绍,希望对大家有所帮助。
先介绍下数据环境,后面的分析如果没有特别说明就都是基于此环境来测试的。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
shiro.ini文件
[users]
root = 123456
# 账号:root 密码:123456
测试代码
public static void main(String[] args) {
// 注意:SecurityManager在java.lang包下也有同名的SecurityManager
// 1.获取SecurityManager工厂对象 同时将配置文件信息封装到了ini对象中
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
// 2.获取SecurityManager对象
SecurityManager securityManager = factory.getInstance();
// 3.将SecurityManager添加到当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 4.获取subject
Subject subject = SecurityUtils.getSubject();
AuthenticationToken token = new UsernamePasswordToken("admin", "12345");
// 做登录认证
try {
subject.login(token );
} catch (UnknownAccountException e) {
System.out.println("账号不存在...");
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误...");
}
System.out.println(subject.isAuthenticated());
}
我们先来分析下SecurityManager工厂对象的源代码,来看看具体做了什么事情
// 1.获取SecurityManager工厂对象 同时将配置文件信息封装到了ini对象中
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
进入IniSecurityManagerFactory有参构造方法中查看。
public IniSecurityManagerFactory(String iniResourcePath) {
// 1.先执行Ini中的fromResourcePath方法加载配置文件
// 2.调用自身另一个有参构造方法
this(Ini.fromResourcePath(iniResourcePath));
}
进入fromResourcePath方法查看
// 方法的返回值是 Ini对象 注意!!!
public static Ini fromResourcePath(String resourcePath) throws ConfigurationException {
if (!StringUtils.hasLength(resourcePath)) {
throw new IllegalArgumentException("Resource Path argument cannot be null or empty.");
}
// 实例化 Ini 对象
Ini ini = new Ini();
// Ini对象通过loadFromPath加载配置文件
ini.loadFromPath(resourcePath);
return ini;
}
进入loadFromPath方法
public void loadFromPath(String resourcePath) throws ConfigurationException {
InputStream is;
try {
// 将配置文件加载到内存中 以字节输入流的方式
is = ResourceUtils.getInputStreamForPath(resourcePath);
} catch (IOException e) {
throw new ConfigurationException(e);
}
// 继续
load(is);
}
继续进入load方法
public void load(InputStream is) throws ConfigurationException {
if (is == null) {
throw new NullPointerException("InputStream argument cannot be null.");
}
InputStreamReader isr;
try {
// 将InputStream包装成了InputStreamReader
isr = new InputStreamReader(is, DEFAULT_CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
throw new ConfigurationException(e);
}
// 继续进入
load(isr);
}
继续进入load
public void load(Reader reader) {
Scanner scanner = new Scanner(reader);
try {
// 继续进入
load(scanner);
} finally {
try {
scanner.close();
} catch (Exception e) {
log.debug("Unable to cleanly close the InputStream scanner. Non-critical - ignoring.", e);
}
}
}
public void load(Scanner scanner) {
// 存储 sectionName 默认是""
String sectionName = DEFAULT_SECTION_NAME;
// sectionContent 拼接section的内容
StringBuilder sectionContent = new StringBuilder();
while (scanner.hasNextLine()) {
// 循环读取的每行记录
String rawLine = scanner.nextLine();
// 去掉两侧的空格
String line = StringUtils.clean(rawLine);
// null 或者 #开头 ;开头就跳过
if (line == null || line.startsWith(COMMENT_POUND) || line.startsWith(COMMENT_SEMICOLON)) {
//skip empty lines and comments:
continue;
}
// [users]截取掉 [和] 具体实现看后面的方法
String newSectionName = getSectionName(line);
if (newSectionName != null) {
//found a new section - convert the currently buffered one into a Section object
// 将获取的新的section(ini中的门面关键字)
addSection(sectionName, sectionContent);
//reset the buffer for the new section:
sectionContent = new StringBuilder();
sectionName = newSectionName;
if (log.isDebugEnabled()) {
log.debug("Parsing " + SECTION_PREFIX + sectionName + SECTION_SUFFIX);
}
} else {
//normal line - add it to the existing content buffer:
sectionContent.append(rawLine).append("\n");
}
}
//finish any remaining buffered content:
addSection(sectionName, sectionContent);
}
getSectionName方法
protected static String getSectionName(String line) {
String s = StringUtils.clean(line);
if (isSectionHeader(s)) {
// 去掉第一个和最后一个字符
return cleanName(s.substring(1, s.length() - 1));
}
return null;
}
protected static boolean isSectionHeader(String line) {
String s = StringUtils.clean(line);
// 记录不为空 [开头 ]结尾就返回true否则false
return s != null && s.startsWith(SECTION_PREFIX) && s.endsWith(SECTION_SUFFIX);
}
进入addSection方法
private void addSection(String name, StringBuilder content) {
if (content.length() > 0) {
String contentString = content.toString();
String cleaned = StringUtils.clean(contentString);
if (cleaned != null) {
Section section = new Section(name, contentString);
if (!section.isEmpty()) {
// 该代码是核心
sections.put(name, section);
}
}
}
}
好了配置文件的加载到此,回到this(Ini.fromResourcePath(iniResourcePath))这儿来,Ini.fromResourcePath(iniResourcePath)代码返回了Ini对象,同时解析了shiro.ini中的配置信息,并保存在了Ini的sections对象中。 接下来进入this(Ini.fromResourcePath(iniResourcePath))构造方法中来看。
public IniSecurityManagerFactory(Ini config) {
setIni(config);
}
进入setIni方法
将Ini赋值给了IniFactorySupport的成员变量,至此代码看完,