Authentication源码解析
1、获取当前的 Subject. 调用 SecurityUtils.getSubject();
从当前线程的threadLocals属性中获取Subject对象
SecurityUtils
public static Subject getSubject() {
Subject subject = ThreadContext.getSubject();
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}
ThreadContext
public static Subject getSubject() {
return (Subject) get(SUBJECT_KEY);
} public static Object get(Object key) {
if (log.isTraceEnabled()) {
String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
} Object value = getValue(key);
if ((value != null) && log.isTraceEnabled()) {
String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
return value;
} private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
private static Object getValue(Object key) {
Map<Object, Object> perThreadResources = resources.get();
return perThreadResources != null ? perThreadResources.get(key) : null;
}
ThreadLocal<T>
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
2、Subject 的 login(AuthenticationToken)
登录验证成功后将Subject.authenticated置位true,登录后再登陆时,当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
DelegatingSubject
public void login(AuthenticationToken token) throws AuthenticationException {
clearRunAsIdentitiesInternal();
Subject subject = securityManager.login(this, token);
...... this.principals = principals;
this.authenticated = true;
AuthenticatingSecurityManager中调用authenticator.authenticate(),其中authenticator初始化是org.apache.shiro.authc.pam.ModularRealmAuthenticator.ModularRealmAuthenticator
this.authenticator = new ModularRealmAuthenticator();
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
return this.authenticator.authenticate(token);
}
ModularRealmAuthenticator中循环Realms判断
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection<Realm> realms = getRealms();
if (realms.size() == ) {
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
return doMultiRealmAuthentication(realms, authenticationToken);
}
}
那ModularRealmAuthenticator中循环Realms是何时赋值的呢?Spring创建DefaultSecurityManager对象及属性赋值之后,调用afterRealmsSet()
AuthenticatingSecurityManager
protected void afterRealmsSet() {
super.afterRealmsSet();
if (this.authenticator instanceof ModularRealmAuthenticator) {
((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
}
}
下面以单Realm为例,进行doSingleRealmAuthentication解析。
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" +
token + "]. Please ensure that the appropriate Realm implementation is " +
"configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
}
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " +
"submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
}
return info;
}
realm.getAuthenticationInfo()中主要实现了:
1、调用Realm.doGetAuthenticationInfo(),从数据源中获取用户、密码及相应的密码处理
2、进行数据源获取的密码和登录页面获取密码的比对
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info = getCachedAuthenticationInfo(token);
if (info == null) {
//otherwise not cached, perform the lookup:
info = doGetAuthenticationInfo(token);//从数据源中获取用户、密码及相应的密码处理
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
} if (info != null) {
assertCredentialsMatch(token, info);//进行数据源获取的密码和登录页面获取密码的比对
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
开发中一般继承Realm,重写doGetAuthenticationInfo。
最后分析下assertCredentialsMatch方法。
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
if (!cm.doCredentialsMatch(token, info)) {
//not successful - throw an exception to indicate this:
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}
根据credentialsMatcher 属性来进行的密码的比对。在定义Realm时根据加密方式定义相应的CredentialsMatcher,默认为SimpleCredentialsMatcher
<bean id="myRealm" class="org.tarena.shiro.realm.MyRealm">
<property name="credentialsMatcher" >
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"></property>
<property name="hashIterations" value=""></property>
</bean>
</property>
</bean>
最新文章
- 关于浏览器兼容问题及hack写法
- Tomcat_启动多个tomcat时,会报StandardServer.await: Invalid command &#39;&#39; received错误
- 数据库连接JDBC和数据库连接池C3P0自定义的java封装类
- format——MATLAB
- oracle——表修改语句集合
- Object-C 对象 (创建/销毁 对象)-- 笔记
- jQuery中间each实施例的方法
- 微信账号 echo_server 的实现
- Linux - CentOS6.5服务器搭建与初始化配置详解(下)
- Omi命令行界面omi-cli发布
- 从foreach语句枚举元素看数组
- node.js之第一天
- python的项目结构
- node-express-2-jade
- HTML5为输入框添加语音输入功能
- RabbitMQ系列教程之四:路由(Routing)(转载)
- UIScrollView中图片放大后不居中,或居中后移动有偏差解决办法
- 乞丐版servlet容器第3篇
- 【转载】COM 组件设计与应用(一)——起源及复合文件
- Java Socket Timeout 总结
热门文章
- MiniUI表单验证总结
- Flutter AspectRatio、Card 卡片组件
- openresty开发系列26--openresty中使用redis模块
- centos7.6_x86_64使用Squid搭建代理服务器让windows上网
- ISO/IEC 9899:2011 条款6.5.7——按位移位操作符
- Java程序员金三银四精心准备的面试题及答案(基础篇)
- python读写csv文件的方法(还没试,先记录一下)
- locust设置断言的方法
- 宣化上人:大佛顶首楞严经四种清净明诲浅释(4-5)(转自学佛网:http://www.xuefo.net/nr/article23/230699.html)
- 【转】行内元素和inline-block产生的水平空隙bug