1、获取当前的 Subject. 调用 SecurityUtils.getSubject();

从当前线程的threadLocals属性中获取Subject对象

  1. SecurityUtils
  2. public static Subject getSubject() {
  3. Subject subject = ThreadContext.getSubject();
  4. if (subject == null) {
  5. subject = (new Subject.Builder()).buildSubject();
  6. ThreadContext.bind(subject);
  7. }
  8. return subject;
  9. }
  1. ThreadContext
  2. public static Subject getSubject() {
  3. return (Subject) get(SUBJECT_KEY);
  4. }
  5.  
  6. public static Object get(Object key) {
  7. if (log.isTraceEnabled()) {
  8. String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
  9. log.trace(msg);
  10. }
  11.  
  12. Object value = getValue(key);
  13. if ((value != null) && log.isTraceEnabled()) {
  14. String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
  15. key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
  16. log.trace(msg);
  17. }
  18. return value;
  19. }
  20.  
  21. private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
  22. private static Object getValue(Object key) {
  23. Map<Object, Object> perThreadResources = resources.get();
  24. return perThreadResources != null ? perThreadResources.get(key) : null;
  25. }
  1. ThreadLocal<T>
  2. public T get() {
  3. Thread t = Thread.currentThread();
  4. ThreadLocalMap map = getMap(t);
  5. if (map != null) {
  6. ThreadLocalMap.Entry e = map.getEntry(this);
  7. if (e != null)
  8. return (T)e.value;
  9. }
  10. return setInitialValue();
  11. }

2、Subject 的 login(AuthenticationToken)

登录验证成功后将Subject.authenticated置位true,登录后再登陆时,当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()

  1. DelegatingSubject
  2. public void login(AuthenticationToken token) throws AuthenticationException {
  3. clearRunAsIdentitiesInternal();
  4. Subject subject = securityManager.login(this, token);
  5. ......
  6.  
  7. this.principals = principals;
  8. this.authenticated = true;

AuthenticatingSecurityManager中调用authenticator.authenticate(),其中authenticator初始化是org.apache.shiro.authc.pam.ModularRealmAuthenticator.ModularRealmAuthenticator

  1. this.authenticator = new ModularRealmAuthenticator();
  2. public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
  3. return this.authenticator.authenticate(token);
  4. }

ModularRealmAuthenticator中循环Realms判断

  1. protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
  2. assertRealmsConfigured();
  3. Collection<Realm> realms = getRealms();
  4. if (realms.size() == ) {
  5. return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
  6. } else {
  7. return doMultiRealmAuthentication(realms, authenticationToken);
  8. }
  9. }

那ModularRealmAuthenticator中循环Realms是何时赋值的呢?Spring创建DefaultSecurityManager对象及属性赋值之后,调用afterRealmsSet()

  1. AuthenticatingSecurityManager
  2. protected void afterRealmsSet() {
  3. super.afterRealmsSet();
  4. if (this.authenticator instanceof ModularRealmAuthenticator) {
  5. ((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
  6. }
  7. }

下面以单Realm为例,进行doSingleRealmAuthentication解析。

  1. protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
  2. if (!realm.supports(token)) {
  3. String msg = "Realm [" + realm + "] does not support authentication token [" +
  4. token + "]. Please ensure that the appropriate Realm implementation is " +
  5. "configured correctly or that the realm accepts AuthenticationTokens of this type.";
  6. throw new UnsupportedTokenException(msg);
  7. }
  8. AuthenticationInfo info = realm.getAuthenticationInfo(token);
  9. if (info == null) {
  10. String msg = "Realm [" + realm + "] was unable to find account data for the " +
  11. "submitted AuthenticationToken [" + token + "].";
  12. throw new UnknownAccountException(msg);
  13. }
  14. return info;
  15. }

realm.getAuthenticationInfo()中主要实现了:

1、调用Realm.doGetAuthenticationInfo(),从数据源中获取用户、密码及相应的密码处理

2、进行数据源获取的密码和登录页面获取密码的比对

  1. public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  2.  
  3. AuthenticationInfo info = getCachedAuthenticationInfo(token);
  4. if (info == null) {
  5. //otherwise not cached, perform the lookup:
  6. info = doGetAuthenticationInfo(token);//从数据源中获取用户、密码及相应的密码处理
  7. log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
  8. if (token != null && info != null) {
  9. cacheAuthenticationInfoIfPossible(token, info);
  10. }
  11. } else {
  12. log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
  13. }
  14.  
  15. if (info != null) {
  16. assertCredentialsMatch(token, info);//进行数据源获取的密码和登录页面获取密码的比对
  17. } else {
  18. log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
  19. }
  20. return info;
  21. }

开发中一般继承Realm,重写doGetAuthenticationInfo。

最后分析下assertCredentialsMatch方法。

  1. protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
  2. CredentialsMatcher cm = getCredentialsMatcher();
  3. if (cm != null) {
  4. if (!cm.doCredentialsMatch(token, info)) {
  5. //not successful - throw an exception to indicate this:
  6. String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
  7. throw new IncorrectCredentialsException(msg);
  8. }
  9. } else {
  10. throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
  11. "credentials during authentication. If you do not wish for credentials to be examined, you " +
  12. "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
  13. }
  14. }

根据credentialsMatcher 属性来进行的密码的比对。在定义Realm时根据加密方式定义相应的CredentialsMatcher,默认为SimpleCredentialsMatcher

  1. <bean id="myRealm" class="org.tarena.shiro.realm.MyRealm">
  2. <property name="credentialsMatcher" >
  3. <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
  4. <property name="hashAlgorithmName" value="MD5"></property>
  5. <property name="hashIterations" value=""></property>
  6. </bean>
  7. </property>
  8. </bean>

Authentication源码解析的更多相关文章

  1. Spring Security 访问控制 源码解析

    上篇 Spring Security 登录校验 源码解析  分析了使用Spring Security时用户登录时验证并返回token过程,本篇分析下用户带token访问时,如何验证用户登录状态及权限问 ...

  2. Django生命周期 URL ----> CBV 源码解析-------------- 及rest_framework APIView 源码流程解析

    一.一个请求来到Django 的生命周期   FBV 不讨论 CBV: 请求被代理转发到uwsgi: 开始Django的流程: 首先经过中间件process_request (session等) 然后 ...

  3. Shiro源码解析-Session篇

    上一篇Shiro源码解析-登录篇中提到了在登录验证成功后有对session的处理,但未详细分析,本文对此部分源码详细分析下. 1. 分析切入点:DefaultSecurityManger的login方 ...

  4. Okhttp3源码解析(5)-拦截器RetryAndFollowUpInterceptor

    ### 前言 回顾: [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析](htt ...

  5. Spring Security 解析(七) —— Spring Security Oauth2 源码解析

    Spring Security 解析(七) -- Spring Security Oauth2 源码解析   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因 ...

  6. .Net Core 认证系统之Cookie认证源码解析

    接着上文.Net Core 认证系统源码解析,Cookie认证算是常用的认证模式,但是目前主流都是前后端分离,有点鸡肋但是,不考虑移动端的站点或者纯管理后台网站可以使用这种认证方式.注意:基于浏览器且 ...

  7. AspNetCore3.1_Secutiry源码解析_1_目录

    文章目录 AspNetCore3.1_Secutiry源码解析_1_目录 AspNetCore3.1_Secutiry源码解析_2_Authentication_核心项目 AspNetCore3.1_ ...

  8. AspNetCore3.1_Secutiry源码解析_2_Authentication_核心对象

    系列文章目录 AspNetCore3.1_Secutiry源码解析_1_目录 AspNetCore3.1_Secutiry源码解析_2_Authentication_核心项目 AspNetCore3. ...

  9. AspNetCore3.1_Secutiry源码解析_3_Authentication_Cookies

    系列文章目录 AspNetCore3.1_Secutiry源码解析_1_目录 AspNetCore3.1_Secutiry源码解析_2_Authentication_核心流程 AspNetCore3. ...

随机推荐

  1. Java实现视频网站的视频上传、视频转码、及视频播放功能(ffmpeg)

    视频网站中提供的在线视频播放功能,播放的都是FLV格式的文件,它是Flash动画文件,可通过Flash制作的播放器来播放该文件.项目中用制作的player.swf播放器. 多媒体视频处理工具FFmpe ...

  2. JVM 类加载器命名空间深度解析与实例分析

    一.创建Sample 1.创建实例 public class MyPerson { private MyPerson myPerson; public void setMyPerson(Object ...

  3. 《浅谈F5健康检查常用的几种方式》—那些你应该知道的知识(二)

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/sinat_17736151/articl ...

  4. IM 简介

    LayIM - 打造属于你自己的网页聊天系统http://layim.layui.com/ 瓜子IM智能客服系统的数据架构设计(整理自现场演讲) - 知乎https://zhuanlan.zhihu. ...

  5. Mac OS docker挂载文件夹

    sudo docker run -p 3306:3306 --name mysql -v /var/run/docker.sock:/var/run/docker.sock -v ~/mysql/co ...

  6. c代码审查软件

    1. Coccinelle http://coccinelle.lip6.fr/

  7. Linux系统调优——系统整体运行状态排查(七)

    (1).vmstat vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的整体状态值,包括服务器的CPU使用率,MEM内存使用,VMSwap虚拟内存交换情况,IO读写 ...

  8. 123457123456#0#-----com.tym.niuniuChengYu05--前拼后广--最牛成语tym

    com.tym.niuniuChengYu05--前拼后广--最牛成语tym

  9. shell脚本中执行mysql sql脚本文件并传递参数

    1 shell 文件内容替换 sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行替换.删除.新增.选取等特定工作,下面先了解一下sed的用法. 调用sed命 ...

  10. Swift4.0复习泛型

    1.泛型的基本使用: /// 定义了一个泛型结构体MyStruct, /// 其泛型形参为T struct MyStruct<T> {   /// 用泛型T定义存储式成员属性t var t ...