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>

最新文章

  1. 关于浏览器兼容问题及hack写法
  2. Tomcat_启动多个tomcat时,会报StandardServer.await: Invalid command &#39;&#39; received错误
  3. 数据库连接JDBC和数据库连接池C3P0自定义的java封装类
  4. format——MATLAB
  5. oracle——表修改语句集合
  6. Object-C 对象 (创建/销毁 对象)-- 笔记
  7. jQuery中间each实施例的方法
  8. 微信账号 echo_server 的实现
  9. Linux - CentOS6.5服务器搭建与初始化配置详解(下)
  10. Omi命令行界面omi-cli发布
  11. 从foreach语句枚举元素看数组
  12. node.js之第一天
  13. python的项目结构
  14. node-express-2-jade
  15. HTML5为输入框添加语音输入功能
  16. RabbitMQ系列教程之四:路由(Routing)(转载)
  17. UIScrollView中图片放大后不居中,或居中后移动有偏差解决办法
  18. 乞丐版servlet容器第3篇
  19. 【转载】COM 组件设计与应用(一)——起源及复合文件
  20. Java Socket Timeout 总结

热门文章

  1. MiniUI表单验证总结
  2. Flutter AspectRatio、Card 卡片组件
  3. openresty开发系列26--openresty中使用redis模块
  4. centos7.6_x86_64使用Squid搭建代理服务器让windows上网
  5. ISO/IEC 9899:2011 条款6.5.7——按位移位操作符
  6. Java程序员金三银四精心准备的面试题及答案(基础篇)
  7. python读写csv文件的方法(还没试,先记录一下)
  8. locust设置断言的方法
  9. 宣化上人:大佛顶首楞严经四种清净明诲浅释(4-5)(转自学佛网:http://www.xuefo.net/nr/article23/230699.html)
  10. 【转】行内元素和inline-block产生的水平空隙bug