Spring Security认证之登录用户数据获取

news2024/12/24 2:10:46

本文内容来自王松老师的《深入浅出Spring Security》,自己在学习的时候为了加深理解顺手抄录的,有时候还会写一些自己的想法。

登录用户数据获取

        登录成功之后,在后续的业务逻辑中开发者可能还需要获取到登录成功的用户对象,如果不是用任何安全管理框架那么可以将用户信息保存在HttpSession中,如果以后需要的话可以从HttpSession中获取数据。在Spring Security汇总,用户登录信息本质上还是保存在HttpSession中的,但是为了方便使用Spring Security对HttpSession中的用户信息进行了封装。

        获取登录用户的信息离不开一个重要的对象:Authentication。在Spring Security中,Authentication对象主要由两方面功能:

  • 作为AuthenticationManager的输入参数,提供用户身份认证的凭证。当它作为一个入参是,它的isAuthenticated方法返回false,表示用户还未认证。
  • 代表已经认证过的用户,此时的Authentication可以从SecurityContext中获取。

        一个Authentication对象主要包含三个方面的信息:

  • principal:定义认证的用户。如果用户使用用户名、密码的方式登录,principal通常就是一个UserDetails对象。
  • credentials:登录凭证,一般是指密码。当用户登录成功之后,登录凭证就会自动擦数,以防泄漏。
  • authorities:用户被授予的权限信息

        Java中本身提供了一个Principal接口来描述认证主体,Principal可以代表一个公司、个人或者登陆者ID。Spring Security中定义了Authentication接口用来规范登录用户信息,Authentication继承自Principal:


public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials();

    Object getDetails();

    Object getPrincipal();

    boolean isAuthenticated();

    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

        这里接口中定义的方法都很好理解:

  • getAuthorities方法:用来获取用户权限
  • getCredentials方法:用来获取用户凭证,一般来说是密码
  • getDetails方法:用来获取用户详细信息,可能是当前请求之类
  • getPrincipal方法:用来获取当前用户信息,可能是一个用户名,也可能是一个用户对象
  • isAuthenticated方法:当前用户是否认证成功

        可以看到在Spring Security中,只要获得Authentication对象就能获取到登录用户的详细信息。

        不同的认证方式对应不同的Authentication实例。在Spring Security中的Authentication实例类如下图:

        这些实现类看起来可能比较陌生,在后续的学习中这些实现类基本上都会涉及。在这些Authentication的实例中,最常用的的两个类:UsernamePasswordAuthenticationToken和RememberMeAuthentication。这里我们先混个眼熟,后面的学习中我们会学习到。大致了解了Authentication对象之后,接下来我们看下如何在登录成功后获取用户登录信息,即Authentication对象。

从SecurityContextHolder中获取

        在前面的学习项目中增加一个UserController,内容如下:

/**
 * @author tlh
 * @date 2022/11/17 21:11
 */
@RestController
public class UserController {

    @GetMapping("/user")
    public Object userInfo() throws JsonProcessingException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String name = authentication.getName();
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        ObjectMapper mapper = new ObjectMapper();
        Map userMap = new HashMap(2);
        userMap.put("userName", name);
        userMap.put("authorities", authorities);
        return mapper.writeValueAsString(userMap);
    }
}

        配置成功后启动项目,登录成功后访问/user接口,会返回用户名字和用户所拥有的的权限信息:

        这里看到SecurityContextHolder.getContext( )是一个静态方法,也就意味着我们随时可以获取到登录的用户信息,在service层中也可以轻松获取到。获取用户登录信息的代码很简单,那么SecurityContextHolder到底是什么?它里面的数据又是从何而来的?

 

SecurityContextHolder

        SecurityContextHolder中存储的是SecurityContext,SecurityContext中存储的是Authentication。三者的关系如下图:

 

        在SecurityContextHolder中存储SecurityContext定义了三种存储模式。使用的典型的策略模式。

  • MODE_THREADLOCAL:该种模式存放策略是将SecurityContext存放在ThreadLocal中。大家都知道ThreadLocal的特点就是在哪个线程中存储就只能在对应的线程中读取到,这个模式非常合适Web应用。应为一个请求无论经过多少个Filter到达Servlet都是有一个线程来处理。该模式也是Spring Security默认的存储SecurityContext的模式。
  • MODE_INHERITABLETHREADLOCAL:该种存储模式适用于多线程环境,如果希望子线程也能获取到登录用户的数据,就可以使用该模式。
  • MODE_GLOBAL:该模式实际使用了一个静态变量来存储SecurityContext。 

        我们看到SecurityContextHolder的源码大致上就看明白:

public class SecurityContextHolder {
    public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
    public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
    public static final String MODE_GLOBAL = "MODE_GLOBAL";
    private static final String MODE_PRE_INITIALIZED = "MODE_PRE_INITIALIZED";
    public static final String SYSTEM_PROPERTY = "spring.security.strategy";
    private static String strategyName = System.getProperty("spring.security.strategy");
    private static SecurityContextHolderStrategy strategy;
    private static int initializeCount = 0;

    public SecurityContextHolder() {
    }

    private static void initialize() {
        initializeStrategy();
        ++initializeCount;
    }

    //根据 strategyName 来加载对应的SecurityContextHolderStrategy(定义了各种保存SecurityContext的策略接口)
    private static void initializeStrategy() {
        if ("MODE_PRE_INITIALIZED".equals(strategyName)) {
            Assert.state(strategy != null, "When using MODE_PRE_INITIALIZED, setContextHolderStrategy must be called with the fully constructed strategy");
        } else {
              //默认采用ThreadLocal保存SecurityContext
            if (!StringUtils.hasText(strategyName)) {
                strategyName = "MODE_THREADLOCAL";
            }
            if (strategyName.equals("MODE_THREADLOCAL")) {
                strategy = new ThreadLocalSecurityContextHolderStrategy();
            } else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {
                //InheritableThreadLocal不同于ThreadLoca在于在创建子线程时会将父线程中的数据复制到子线程中
                strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
            } else if (strategyName.equals("MODE_GLOBAL")) {
                //采用一个静态变量保存SecurityContext
                strategy = new GlobalSecurityContextHolderStrategy();
            } else {
                try {
                    Class<?> clazz = Class.forName(strategyName);
                    Constructor<?> customStrategy = clazz.getConstructor();
                    strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();
                } catch (Exception var2) {
                    ReflectionUtils.handleReflectionException(var2);
                }
            }
        }
    }
    //省略。。。
}

        

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

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

相关文章

轻松学会JavaScript事件

文章目录事件与事件流事件监听&#xff08;绑定事件方法&#xff09;JavaScript事件鼠标事件表单事件键盘事件UI事件快速投票事件可以说是JavaScript最引人注目的特性&#xff0c;因为它提供了一个平台&#xff0c;让用户不仅能浏览页面中的内容&#xff0c;而且能跟页面进行交…

贪心算法+动态规划 | 眼光不同决定深度不同

前言 上大学那会有门课程叫做【算法与实践】, 算法配上 C 那感觉不要提多爽了。现在回想起来算法不局限于语言&#xff0c;只不过每个语言的语法不一罢了&#xff0c; 但是算法的内在逻辑都是相通的&#xff0c;今天我们通过三个案列来了解分析下算法之一 【贪心算法】。 简介…

显示控件——滑动选择

该控件也是一种调节控件&#xff0c;通过在既定范围内的滑动来选择具体选项值以图达到对变量的调控效果&#xff0c;其UI操作效果类似于拨动密码锁的滚轮。 位置信息&#xff1a;控件在工程页面区域的位置 “X”“Y”为控件区域左上角坐标。 “W”“H”为控件区域宽度和高度&a…

cpu设计和实现(取指)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 cpu设计的本质是数字电路的设计。要是没有verilog、vhdl这些语言&#xff0c;那么剩下来使用的方法基本只有卡诺图这一种了。在数字电路中&#xf…

看懂这篇文章,你就懂了Mybatis的二级缓存

缓存的概述和分类 概述 缓存就是一块内存空间.保存临时数据 为什么使用缓存 将数据源&#xff08;数据库或者文件&#xff09;中的数据读取出来存放到缓存中&#xff0c;再次获取的时候 ,直接从缓存中获取&#xff0c;可以减少和数据库交互的次数,这样可以提升程序的性能&a…

【MySQL】MySQL复制原理与主备一致性同步工作原理解析(原理篇)(MySQL专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

Qt之天气预报实现(一)预备篇

文章目录序章一、思路整理1.1 我的Qt版本信息1.2 我使用的API二、高德开放平台API的申请和使用2.1 API的申请步骤2.1.1 注册高德开放平台账号&#xff08;若已有账号请无视&#xff09;2.1.2 创建API_KEY2.2 API的使用2.2.1 天气查询文档和城市编码下载位置&#xff08;必读&am…

node版本与node-sass版本不兼容时问题解决

在项目运行中会经常遇到node版本号与node-sass版本号不兼容的问题&#xff0c;这时可以有两种解决方案。 附图&#xff1a;node与node-sass的对应关系 1、改node版本号去对应node-sass 2、改node-sass版本号去对应node 一般情况下选择修改node-sass的版本号&#xff0c;这里…

[附源码]SSM计算机毕业设计茶园认养管理平台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

PGL图学习之图神经网络GNN模型GCN、GAT[系列六]

PGL图学习之图神经网络GNN模型GCN、GAT[系列六] 项目链接&#xff1a;一键fork直接跑程序 https://aistudio.baidu.com/aistudio/projectdetail/5054122?contributionType1 0.前言-学术界业界论文发表情况 ICLR2023评审情况&#xff1a; ICLR2023的评审结果已经正式发布&a…

OPLSAA力场参数之快速建模—MS+Moltemplate

文章目录一、MS中画出分子结构二、根据OPLSAA力场文件设置原子力场1. OPLSAA力场2. 根据OPLSAA力场中的原子质量进行检查3. 在MS中设置为对应的原子编号三、转换为Lammps可以读取的Data文件四、采用Moltemplate自带工具生成Lt文件1. 生成LT文件2. LT文件结构五、引入OPLSAA力场…

Docker 【Nginx集群部署】

目录 1. nginx前置操作 2. 自定义容器 3. nginx常用命令 4. Error 4.1 502(无响应网关/代理) 4.2 404(找不到对应页面) 4.3 400(异常请求) 4.4 响应超时问题 5. 完整版本Nginx配置文件 1. nginx前置操作 1. 下载拉取nginx镜像 docker pull nginx 2. 使用nginx镜像创…

LVS负载均衡群集(DR模式)

LVS-DR工作原理 数据包流向分析 第一步&#xff1a;客户端发送请求到 Director Server (负载均衡器&#xff09;&#xff0c;请求的数据报文到达内核空间。 数据报文 源 IP ------客户端的 IP 目标 IP ------ VIP 源 MAC ------客户端的 MAC 目的 MAC ------ Director Server…

全产业链核心升级 集聚创新大展宏图——慕尼黑华南电子展回顾

展会简介 2022年11月15日至17日&#xff0c;高交会 - 智能制造、先进电子及激光技术博览会旗下成员展 &#xff08;LEAP Expo&#xff09;-慕尼黑华南电子展、慕尼黑华南电子生产设备展、华南先进激光及加工应用技术展览会及同期举办的华南电路板国际贸易采购博览会与中国&…

【全网热点】打造全网最全爱心代码仓库【火速领取爱心】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 本文章收录于专栏 【代码实践】 目录&#x1f319;正文&#x1f30f;部分效果在线演示&#x1f30f;部分效果截屏&#x1f338;文末祝…

操作系统4小时速成:复习内存管理,内部碎片和外部碎片,页式存储管理,段式存储管理,段页式存储管理,虚拟内存,页面置换算法,LRU内存替换算法

操作系统4小时速成&#xff1a;复习内存管理&#xff0c;内部碎片和外部碎片&#xff0c;页式存储管理&#xff0c;段式存储管理&#xff0c;段页式存储管理&#xff0c;虚拟内存&#xff0c;页面置换算法,LRU内存替换算法 2022找工作是学历、能力和运气的超强结合体&#xff…

react面试题详解

React-Router怎么设置重定向&#xff1f; 使用<Redirect>组件实现路由的重定向&#xff1a; <Switch><Redirect from/users/:id to/users/profile/:id/><Route path/users/profile/:id component{Profile}/> </Switch>当请求 /users/:id 被重定…

显示控件——直线进度条

该控件是指定一个图标&#xff08;矩形长条&#xff09;&#xff0c;通过其滑动在水平方向或者垂直方向实现来调节指定存储空间的变量的效果。滑动范围对应变量地址数据&#xff0c;显示位置通过变量设定。可以配合“滑动调节”触摸控件进行设置。 位置信息&#xff1a;控件在工…

Android App接管手势处理TouchEvnet中单点触摸和多点触控的讲解及实战(附源码 超简单实用)

运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一、单点触摸 dispatchTouchEvent onInterceptTouchEvent onTouchEvent三个方法的输入参数都是手势事件MotionEvent&#xff0c;其中包含触摸动作的所有信息&#xff0c;各种手势操作都从MotionEvent中获取触摸信息并判断处…

Python3,5行代码,让你拥有无限量壁纸美图,终于告别手动下载了。

这里写目录标题1、引言2、代码实战2.1 手动下载2.2 代码批量下载3、总结1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 你电脑桌面壁纸挺好看啊。 小鱼&#xff1a;那是&#xff0c; 毕竟我的审美观在这摆着呢。 小屌丝&#xff1a;你这话&#xff0c;让我不服… 小鱼&#xff…