好久没写博客了,这段时间对最近项目做个总结,先从登入下手,话不多说直奔主题,Shiro的登录使用以及原理。
目录
一、Shiro主要作用
shiro主要的作用就是实现用户登录(认证,授权,加密等),用户登录后的用户信息存储(缓存),用户登出等。
二、登录的使用
在使用登录的时候,最常见的一串代码就是通过工具类SecurityUtils获取Subject,然后对Token进行login();
// 得到subject然后对创建用户名/密码身份验证
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("hu", "123");
subject.login(token);
这时候只对这串代码进行编译运行,你会发现会报一个异常信息
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.
2.1 SecurityManager的生成与使用
根据报错信息以及对SecuriTyUtils的源码发现使用SecurityUtils.getSubject()的时候必须要为其设置一个securityManager,具体如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.shiro;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.Subject.Builder;
import org.apache.shiro.util.ThreadContext;
public abstract class SecurityUtils {
private static SecurityManager securityManager;
public SecurityUtils() {
}
public static Subject getSubject() {
// 通过ThreadContext获取对应的Subject,若未在ThredContext中加入该subject必定为空
// ThreadContext可以通过源码了解到为使用过TreadLocal模式 具体看标题三
//因为是TreadLocal所以表示每个线程初次进来的时候,获取到的subeject必为空
Subject subject = ThreadContext.getSubject();
if (subject == null) {
//具体看下列代码块 主要执行为通过SecurityManager创建出Subject
subject = (new Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}
public static void setSecurityManager(SecurityManager securityManager) {
SecurityUtils.securityManager = securityManager;
}
public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
SecurityManager securityManager = ThreadContext.getSecurityManager();
if (securityManager == null) {
securityManager = SecurityUtils.securityManager;
}
if (securityManager == null) {
String msg = "No SecurityManager accessible to the calling code, either bound to the " + ThreadContext.class.getName() + " or as a vm static singleton. This is an invalid application " + "configuration.";
throw new UnavailableSecurityManagerException(msg);
} else {
return securityManager;
}
}
}
// 在Subjct初次获取到为空的时候会调用的Subject的静态内部类,创建一个Builder,在通过buildSubject的方法进行实现Subject的生成
public static class Builder {
private final SubjectContext subjectContext;
private final SecurityManager securityManager;
// 需要先设置对应的SecurityManage
public Builder() {
this(SecurityUtils.getSecurityManager());
}
// 通过securityManager创建出subject
public Subject buildSubject() {
return this.securityManager.createSubject(this.subjectContext);
}
..........loading...............
}
得出结论 Subject的实例都会(也是必须)绑定一个SecurityManager,对Subject的操作会转为Subject与SecurityManager之间的交互。
看来Subject的生成都是SecurityManager在做苦力活啊。
那么SecurityManager是怎么生成的?
先查阅下官方文档SecurityManager是怎么生成的
根据官方文档:http://greycode.github.io/shiro/doc/tutorial.html
那么我们就先通过使用ini文件进行尝试下:
在resource下创建一个shiro.ini文件放入用户信息
[users]
hu=123
然后通过官方给出的:
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
//2、得到SecurityManager实例 并绑定给SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
这么一个案例又让我们想起了那个Spring等源码中最喜欢用的工厂模式,看看这里的Factory接口啥样:
package org.apache.shiro.util;
public interface Factory<T> {
T getInstance();
}
就是一个用来生成T对应实例的工厂,一个很一般的代码,噢,我的意思是,我的上帝啊,这真是一个写的很棒的代码,一个很完美的工厂。
那么看看IniSecurityManagerFactory干了啥?
看了代码构造函数就做了这些小事,就是将Ini对象赋值下
// 调用了对应的构造函数,将iniResourcePath路径读取为代码可识别的Ini对象
public IniSecurityManagerFactory(String iniResourcePath) {
this(Ini.fromResourcePath(iniResourcePath));
}
// Ini对象具体就是根据文件路径读取对应的文件内的信息流
// 然后调用对应Ini的构造函数
public IniSecurityManagerFactory(Ini config) {
this.setIni(config);
}
那么就是主要是在getInstance的方法中咯?通过对IniSecurityManagerFactory类中查看并未发现getInstance
那么就可以确定在了子类中的实现(运用了模板设计模式的实现)
public class IniSecurityManagerFactory extends IniFactorySupport<SecurityManager>
public abstract class IniFactorySupport<T>