shiro相关源码解析

news2024/9/24 7:18:50

1. 认证过程相关源码解析

前后文可接查看
shiro的登陆都是通过subject.login()方法实现,接下来我们就进入login方法查看实现过程:

1.1 进入DelegatingSubject类的login方法:

此类实现了Subject接口:

public void login(AuthenticationToken token) throws AuthenticationException {
    this.clearRunAsIdentitiesInternal();
    // 此处继续调用securityManager.login方法
    Subject subject = this.securityManager.login(this, token);
    String host = null;
    PrincipalCollection principals;
    if (subject instanceof DelegatingSubject) {
        DelegatingSubject delegating = (DelegatingSubject)subject;
        principals = delegating.principals;
        host = delegating.host;
    } else {
        principals = subject.getPrincipals();
    }

    if (principals != null && !principals.isEmpty()) {
        this.principals = principals;
        this.authenticated = true;
        if (token instanceof HostAuthenticationToken) {
            host = ((HostAuthenticationToken)token).getHost();
        }

        if (host != null) {
            this.host = host;
        }

        Session session = subject.getSession(false);
        if (session != null) {
            this.session = this.decorate(session);
        } else {
            this.session = null;
        }

    } else {
        String msg = "Principals returned from securityManager.login( token ) returned a null or empty value.  This value must be non null and populated with one or more elements.";
        throw new IllegalStateException(msg);
    }
}

1.2 进入SecurityManager的login方法

可以看到SecurityManager 实现了认证器、授权器和会话管理。

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
    Subject login(Subject var1, AuthenticationToken var2) throws AuthenticationException;

    void logout(Subject var1);

    Subject createSubject(SubjectContext var1);
}

此接口方法的实现方法时是DefaultSecurityManager类的login方法:

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;
    try {
        info = this.authenticate(token);
    } catch (AuthenticationException var7) {
        AuthenticationException ae = var7;

        try {
            this.onFailedLogin(token, ae, subject);
        } catch (Exception var6) {
            if (log.isInfoEnabled()) {
                log.info("onFailedLogin method threw an exception.  Logging and propagating original AuthenticationException.", var6);
            }
        }

        throw var7;
    }

    Subject loggedIn = this.createSubject(token, info, subject);
    this.onSuccessfulLogin(token, info, loggedIn);
    return loggedIn;
}

进入authenticate方法:

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
   return this.authenticator.authenticate(token);
}

可以看到访问的是认证器的authenticate方法,点击进入:

1.3 进入AbstractAuthenticator认证器的authenticate方法:

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    if (token == null) {
        throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
    } else {
        log.trace("Authentication attempt received for token [{}]", token);

        AuthenticationInfo info;
        try {
            info = this.doAuthenticate(token);
            if (info == null) {
                String msg = "No account information found for authentication token [" + token + "] by this Authenticator instance.  Please check that it is configured correctly.";
                throw new AuthenticationException(msg);
            }
        } catch (Throwable var8) {
            AuthenticationException ae = null;
            if (var8 instanceof AuthenticationException) {
                ae = (AuthenticationException)var8;
            }

            if (ae == null) {
                String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).";
                ae = new AuthenticationException(msg, var8);
                if (log.isWarnEnabled()) {
                    log.warn(msg, var8);
                }
            }

            try {
                this.notifyFailure(token, ae);
            } catch (Throwable var7) {
                if (log.isWarnEnabled()) {
                    String msg = "Unable to send notification for failed authentication attempt - listener error?.  Please check your AuthenticationListener implementation(s).  Logging sending exception and propagating original AuthenticationException instead...";
                    log.warn(msg, var7);
                }
            }

            throw ae;
        }

        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);
        this.notifySuccess(token, info);
        return info;
    }
}

1.4 进入ModularRealmAuthenticator的doAuthenticate方法

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
    this.assertRealmsConfigured();
    // 获取所有域
    Collection<Realm> realms = this.getRealms();
    return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
}

这里,获取realm域,如果是一个,那就是单数据库的,如果是多个,那就是多数据库的,需要从多个数据库中查询用户信息。这里我们看单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);
    } else {
        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);
        } else {
            return info;
        }
    }
}

1.5 进入AuthenticatingRealm的getAuthenticationInfo方法

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
	//登录操作,没有缓存,不走这个方法
    AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
    if (info == null) {
    	//进入这个方法
        info = this.doGetAuthenticationInfo(token);
        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
        if (token != null && info != null) {
        	// 添加缓存
            this.cacheAuthenticationInfoIfPossible(token, info);
        }
    } else {
        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
    }

    if (info != null) {
        this.assertCredentialsMatch(token, info);
    } else {
        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
    }

    return info;
}

访问到当前类下的抽象方法:

protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;

此抽象方法有多个:实现方法:项目中如果想要真正的实现登陆,也是需要自定义实现此方法
在这里插入图片描述

1.6 用户名校验–进入SimpleAccountRealm的doGetAuthenticationInfo方法

此方法进行用户名校验,只校验用户名,不进行密码校验

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
	// 强转token
    UsernamePasswordToken upToken = (UsernamePasswordToken)token;
    // 根据token中的用户名获取用户
    SimpleAccount account = this.getUser(upToken.getUsername());
    if (account != null) {
    	// 判断用户是否被锁
        if (account.isLocked()) {
            throw new LockedAccountException("Account [" + account + "] is locked.");
        }

		// 判断密码是否过期
        if (account.isCredentialsExpired()) {
            String msg = "The credentials for account [" + account + "] are expired";
            throw new ExpiredCredentialsException(msg);
        }
    }

    return account;
}

进入getUser方法:

protected SimpleAccount getUser(String username) {
    this.USERS_LOCK.readLock().lock();

    SimpleAccount var2;
    try {
    	// 通过断点可以看到这里的users的长度跟我们在配置文件中配置的shiro.ini中users的数量相同
    	// 即从我们设置的用户中找到了对应的account(用户名校验正确)
        var2 = (SimpleAccount)this.users.get(username);
    } finally {
        this.USERS_LOCK.readLock().unlock();
    }

    return var2;
}

校验成功后回到AuthenticatingRealm的getAuthenticationInfo方法继续往下走

1.7 密码校验–进入的AuthenticatingRealm的assertCredentialsMatch方法

此方法用于判断获取到的用户中的密码和token中的密码是否一致

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    CredentialsMatcher cm = this.getCredentialsMatcher();
    if (cm != null) {
        if (!cm.doCredentialsMatch(token, info)) {
            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.");
    }
}

1.8 realm类图

1.8.1 shiro提供的Realm

在这里插入图片描述

1.8.2 根据认证源码认证使用的是SimpleAccountRealm

在这里插入图片描述

1.9 总结:

如果以后我们想要自定义用户名密码校验过程怎么做?

  1. 定义一个类继承AuthorizingRealm类,并实现doGetAuthorizationInfo方法
  2. 在方法中自定义用户名校验过程
  3. 密码校验不需要我们完成,shiro会帮我们实现(通过校验token中的密码和doGetAuthorizationInfo中获取到的info信息中的密码是否一致)

为什么不让我们自己做密码校验

因为涉及到密码加密,自己做处理可能会影响到shiro的密码加密相关操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/187856.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于ssm的高校二手物品交易网 java idea mysql

本文论述了民办高校二手物品交易网的设计和实现&#xff0c;该网站从实际运用的角度出发&#xff0c;运用了计算机网站设计、数据库等相关知识&#xff0c;网络和Mysql数据库设计来实现的&#xff0c;网站主要包括用户注册、用户登录、浏览商品、搜索商品、查看商品并进行购买&…

bilibili全链路压测改造之全链自动化测试实践

01 、背景与意义 B站直播营收送礼业务有着高写、在跨晚和S赛等大型活动下流量陡增、数据实时性要求高等特性&#xff0c;传统压测对于写场景为了避免影响线上数据做了各种屏蔽和黑名单处理&#xff0c;有着无法逼近线上真实情况的问题&#xff0c;因此业务对全链路压测有着较大…

拿下阿里自动化测试岗23k*14薪offer的全程面试记录解析以及总结,一面二面三面,项目,功能,自动化,性能测试,面试题问答

一、自我介绍 面试官您好&#xff01;我叫xx&#xff0c;来自深圳&#xff0c;毕业之后一直从事于软件测试的工作&#xff0c;有做过保险、金融、电商等项目&#xff1b;有做过做功能测试、接口测试&#xff0c;自动化测试&#xff0c;在工作中积极主动、可以独立的完成测试工…

shiro(一):shiro基本概念及基本使用(认证、授权)

1. 权限的管理 1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理&#xff0c;权限管理属于系统安全的范畴&#xff0c;权限管理实现对用户访问系统的控制&#xff0c;按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用…

算法刷题打卡第76天:判断矩阵是否是一个 X 矩阵

判断矩阵是否是一个 X 矩阵 难度&#xff1a;简单 如果一个正方形矩阵满足下述 全部 条件&#xff0c;则称之为一个 X 矩阵 &#xff1a; 矩阵对角线上的所有元素都 不是 0 矩阵中所有其他元素都是 0 给你一个大小为 n x n 的二维整数数组 grid &#xff0c;表示一个正方形矩…

CV——day72:从零开始学YOLO——YOLO-v3(可以在我的资源里下载完整的v1到v3的笔记啦!)

YOLO-v36. YOLO-v36.1 YOLO-v3 改进综述6.2 多scale方法改进与特征融合6.3 经典变换方法对比分析6.4 残差连接方法解读6.5 整体网络模型架构分析6.6 先验框设计改进6.7 softmax层改进6. YOLO-v3 **tips&#xff1a;**作者本人因为美军广泛运用于军事领域&#xff0c;所以决定不…

基于php电影点播平台/电影网站

摘要网络技术给生活带来了十分的便利。所以把电影点播平台与现在网络相结合。在点播平台发展的整个过程中&#xff0c;电影信息管理担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类电影信息管理程序也在不断改进。本课题所设计的电影点播平台&#xff0c;使…

张艺谋《满江红》起诉自媒体人,杨语莲推荐周兆成意在下个谋女郎

伴随着兔年春节的到来&#xff0c;又迎来一波贺岁剧热潮&#xff0c;著名导演张艺谋的《满江红》&#xff0c;也如期和观众朋友见面。随着春节的逐渐结束&#xff0c;贺岁电影《满江红》票房&#xff0c;也再次创下了新高&#xff0c;关于这部电影的话题也多了起来。 最引人关注…

中国电子学会2021年12月份青少年软件编程Python等级考试试卷一级真题(含答案)

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;一级&#xff09; 一、单选题(共25题&#xff0c;共50分) 1. 昨天的温度是5摄氏度&#xff0c;今天降温7摄氏度&#xff0c;今天的温度是多少摄氏度&#xff1f;&#xff08; &#xff09; A. 12 …

利用ChatGPT自动编写下载高德地图poi数据的代码

最近ChatGPT很火&#xff0c;它自己对于自己的解释如下图。我们可以让它来帮我们写代码&#xff0c;属于是薅机器人羊毛了。 首先注册账号&#xff0c;可百度&#xff0c;如&#xff1a;【教程】ChatGPT 保姆级注册教程&#xff0c;但中国大陆手机号不支持OpenAI的注册服务&am…

进程概念(PCB、进程创建、进程状态等)

进程是一个运行的程序&#xff0c;是所有计算机的基础。这个过程与计算机代码不一样&#xff0c;尽管它们非常相似。程序通常被认为是 “被动的” 实体&#xff0c;而进程则是 “主动的” 实体。硬件状态、RAM、CPU和其它属性都是进程持有的属性。下面我们就来了解更多关于进程…

活体识别1:近红外(NIR)图像特性

说明 最近在接触活体识别&#xff0c;在网上找到一个介绍近红外光&#xff08;NIR&#xff09;特性的论文&#xff0c;我简单做个笔记。原文的全文在文末参考资料里。 来自&#xff1a;[1]隋孟君,茅耀斌,孙金生.基于近红外图像特征的活体人脸检测[J].自动化与仪器仪表,2021(0…

Win10下使用WSL2

打包 wsl --export Ubuntu-20.04 E:\Ubuntu\ubuntu.tar.gz 注销之前 wsl --unregister Ubuntu-20.04 导入 wsl --import Ubuntu-20.04 E:\Ubuntu\ E:\Ubuntu\ubuntu.tar.gz --version 2 设置默认登陆用户为安装时用户名 ubuntu2004.exe config --default-user dwb 更新清…

vue 预览 word

最近做的项目要求实现预览word, pdf&#xff0c;png等文件功能&#xff0c;pdf以及png都很简单&#xff0c;轻松百度搞定&#xff0c;但是word预览研究了好久&#xff0c;所以特此记录分享。前端实现预览word分为两种&#xff0c;一种是上传前预览&#xff08;也就是前端使用in…

SpringBoot笔记:统一请求参数修改(HttpServletRequest流复制),加解密参数也可参考处理

文章目录需求实现思路实战演练实现过滤器Filter继承 HttpServletRequestWrapper实现 RequestBodyAdvice 统一处理请求参数测试代码测试效果需求 需要进行统一的解密请求 header 头里面的关键字 encryKey &#xff0c;将解密出来的值赋给 provinceId 并传递给后端的每一个请求接…

23年 yolov5车辆识别+行人识别+车牌识别+车速检测代码(python)

行人识别yolov5和v7对比yolo车距yolo车距1代码&#xff1a;yolov5车辆检测代码 已有1503人下载 代码无需更改&#xff0c;直接可以预测&#xff01;&#xff01;&#xff01; 流程&#xff1a; 版本与配置声明 # YOLOv5 requirements # Usage: pip install -r requirements.tx…

C语言--结构体初阶

目录前言结构体类型的声明什么是结构结构的声明结构体变量的定义结构成员的类型结构体变量的初始化结构体的成员访问结构体传参函数调用的参数压栈前言 在前面的C语言学习中&#xff0c;我们学习了形如char&#xff0c;short&#xff0c;int&#xff0c;float等的不同类型的变…

活动星投票臻我风采评选视频投票的功能在线投票程序

“臻我风采评选”网络评选投票_线上小程序的投票方式_视频投票的功能_在线投票程序用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务…

即时通讯开发之详解TCP/IP中的IP选路、动态选路

静态 IP 选路 1一个简单的路由表 选路是 IP 层最重要的一个功能之一。前面的部分已经简单的讲过路由器是通过何种规则来根据 IP 数据包的 IP 地址来选择路由。 这里就不重复了。 对于一个给定的路由器,可以打印出五种不同的 flag&#xff1a;  U 表明该路由可用。  G…

Qlik Sense Enterprise Windows版(非集群)——详细安装步骤

Qlik Sense分为客户端(Desktop)和服务器(Server)&#xff0c;本文主要介绍Qlik Sense Server的图形化界面安装操作。 安装方式也分为两种&#xff0c;一种是图形化界面安装&#xff0c;另一种是静默安装。一般只有在特殊报错情况下我们才使用静默安装&#xff0c;静默安装的方…