• SpringBoot整合SpringSecurity [超详细] (二)获取用户信息



    前言

    前置条件:

    • 要求掌握简单的项目搭建
    • (默认用户名和随机密码)或者(自定义用户名和密码)

    项目结构如下:

    在这里插入图片描述

    一、代码版

    在这里插入图片描述

    package cn.cy.controller;
    
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class HelloController {
        // 此处的 User是SpringSecurity提供的一个封装用户的类
        @RequestMapping("/getUser")
        public User getUser(){
            // SecurityContext 安全上下文. 用来存储认证授权的相关信息(账号信息和相关权限)
            SecurityContext context = SecurityContextHolder.getContext();
            // Authentication 封装了登录用户信息的对象
            Authentication authentication = context.getAuthentication();
            // 基于认证对象获取用户身份信息
            User user = (User)authentication.getPrincipal();
            System.out.println(user);
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    请添加图片描述

    二、注解版

    在这里插入图片描述

    package cn.cy.controller;
    
    import org.springframework.security.core.annotation.AuthenticationPrincipal;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class HelloController {
        // 此处的 User是SpringSecurity提供的一个封装用户的类
        @RequestMapping("/getUser")
        public UserDetails getUser(@AuthenticationPrincipal UserDetails user){
            System.out.println(user);
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    请添加图片描述

    三、拓展

    1. SecurityContext接口

    安全上下文,这个接口可以设置(setter)或者获取(getter)当前的认证对象。

    SecurityContext源码:

    public interface SecurityContext extends Serializable {
    	/**
    	 * 获取当前经过身份验证的主体,或身份验证请求令牌。
    	 * @return 如果没有可用的身份验证信息,则 Authentication 或 null
    	 */
        Authentication getAuthentication();
    	/**
    	 * 更改当前经过身份验证的主体,或删除身份验证信息。
    	 * @param authentication – 新的身份验证令牌,如果不应存储进一步的身份验证信息,则为 null
    	 */
        void setAuthentication(Authentication authentication);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2. Authentication接口

    认证对象

    • principa
      • 定义认证的用户,通常指的就是UserDetail对象
    • credentials
      • 登录凭证,一般指的是密码,当用户登录成功之后,这个密码是不被保存的,以防止泄露 – Password=[PROTECTED]
    • authorities
      • 用户的权限信息

    Authentication源码

    public interface Authentication extends Principal, Serializable {
    
    	/**
    	 * 获取用户权限
    	 */
    	Collection<? extends GrantedAuthority> getAuthorities();
    	
    	/**
    	 * 用来获取用户凭证,一般来说就是密码。
    	 */
    	Object getCredentials();
    
    	/**
    	 * 用来获取用户的详细信息,可能是当前的请求之类。
    	 */
    	Object getDetails();
    
    	/**
    	 * 用来获取当前用户信息信息,可能是一个用户名,也可能是一个用户对象。
    	 */
    	Object getPrincipal();
    
    	/**
    	 * 当前用户是否认证成功。
    	 */
    	boolean isAuthenticated();
    	
    	/**
    	 * 
    	 */
    	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    通过上述的信息,可以看出在SpringSecurity中,只要我们获取了Authentication对象,就可以获取到登录用户的用户详情了。

    3. SecurityContextHolder

    工具类,用于存放 SecurityContext ,默认是使用ThreadLocal实现的,这样就保证了本线程内所有的方法都可以获得SecurityContext对象

    3.1 SecurityContextHolder重要方法

    • public static SecurityContext getContext()
      • 获取SercurityContext安全上下文对象
    • public static void setContext(SecurityContext context)
      • 设置/修改SercurityContext安全上下文对象
    • public static void clearContext()
      • 清除SercurityContext安全上下文对象
    • public static SecurityContext createEmptyContext()
      • 创建一个空的SercurityContext安全上下文对象

    3.2 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";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. MODE_THREADLOCAL

      • 将SecurityContext存放在ThreadLocal中,ThreadLocal的特点是在哪个线程中存储就要在哪个线程中读取,这其实非常适合web应用,因为在默认情况下,一个请求无论经过多少Filter到达Servlet,都是由一个线程来处理的,这也是SecurityContextHolder默认的存储策略,这种存储策略意味着如果在具体的业务代码中,开启了子线程,在子线程去获取登录用户数据,就会获取不到。
    2. MODE_INHERITABLETHREADLOCAL

      • 这种存储模式适用于多线程环境,如果希望在子线程中也能够获取到登录用户数据,那么可以使用这种存储模式。
    3. MODE_GLOBAL

      • 这种存储模式实际上是将数据保存在一个静态变量中,在JAVAWeb开发中,这种模式很少使用到

    4. SecurityContextHolder,SecurityContext,Authentication的关系

    在这里插入图片描述

  • 相关阅读:
    基于减法平均算法的无人机航迹规划-附代码
    Swift之深入解析子数组提取的性能优化
    Java 多线程(五):锁(三)
    ELK 集群部署
    云计算平台建设总体技术方案参考
    安全配置错误规避指南
    密码学消息鉴别
    【番外篇】Rust环境搭建+基础开发入门+Rust与.NET6、C++的基础运算性能比较
    macOS swift下使用贝塞尔曲线制作五子棋盘(2)
    利用ChatGPT练习口语
  • 原文地址:https://blog.csdn.net/weixin_46030002/article/details/126367295