SecurityContextHolder之策略模式源码分析

简介: SecurityContextHolder之策略模式源码分析

springsecurity 之 登录用户数据的获取

springsecurity中,用户登录信息本质是保存到HttpSession中,springsecurity进行封装 获取登录数据有两种思路:

  1. 从SecurityContextHolder中获取
  2. 从当前请求对象中获取

从SecurityContextHolder中获取

@RestController
public class HelloController {
    @GetMapping("/hello")
    public void hello() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println("authentication.getClass() = " + authentication.getClass());
    }
}

SecurityContextHolder存放的是SecurityContext ,SecurityContextHolder中定义三种不同的数据存储策略,采用了策略模式

  1. MODE_THREADLOCAL :将SecurityContext放在ThreadLocal中,开启子线程,子线程获取不到用户数据。
  2. MODE_INHERITABLETHREADLOCAL:多线程环境,子线程也能获取到用户数据。
  3. MODE_GLOBAL:数据保存到一个静态变量中,web开发中很少使用。

SecurityContextHolderStrategy接口用来规范存储策略中的方法

public interface SecurityContextHolderStrategy {
    void clearContext();

    SecurityContext getContext();

    void setContext(SecurityContext var1);

    SecurityContext createEmptyContext();
}

有三个实现类 对应三个不同的存储策略

ThreadLocalSecurityContextHolderStrategy

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.security.core.context;

import org.springframework.util.Assert;

final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
    private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal();

    ThreadLocalSecurityContextHolderStrategy() {
    }

    public void clearContext() {
        contextHolder.remove();
    }

    public SecurityContext getContext() {
        SecurityContext ctx = (SecurityContext)contextHolder.get();
        if (ctx == null) {
            ctx = this.createEmptyContext();
            contextHolder.set(ctx);
        }

        return ctx;
    }

    public void setContext(SecurityContext context) {
        Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
        contextHolder.set(context);
    }

    public SecurityContext createEmptyContext() {
        return new SecurityContextImpl();
    }
}

存储载体是ThreadLocal 针对SecurityContext的操作都是在ThreadLocal中进行操作。SecurityContext只是个接口,只有一个实现类是SecurityContextImpl

InheritableThreadLocalSecurityContextHolderStrategy

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.security.core.context;

import org.springframework.util.Assert;

final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
    private static final ThreadLocal<SecurityContext> contextHolder = new InheritableThreadLocal();

    InheritableThreadLocalSecurityContextHolderStrategy() {
    }

    public void clearContext() {
        contextHolder.remove();
    }

    public SecurityContext getContext() {
        SecurityContext ctx = (SecurityContext)contextHolder.get();
        if (ctx == null) {
            ctx = this.createEmptyContext();
            contextHolder.set(ctx);
        }

        return ctx;
    }

    public void setContext(SecurityContext context) {
        Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
        contextHolder.set(context);
    }

    public SecurityContext createEmptyContext() {
        return new SecurityContextImpl();
    }
}

存储载体为InheritableThreadLocal ,InheritableThreadLocal继承ThreadLocal,多了一个特性,就是在子线程创建的时间,会自动将父线程的数据复制到子线程中。实现了子线程中能够获取登录数据的功能。

GlobalSecurityContextHolderStrategy

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.security.core.context;

import org.springframework.util.Assert;

final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
    private static SecurityContext contextHolder;

    GlobalSecurityContextHolderStrategy() {
    }

    public void clearContext() {
        contextHolder = null;
    }

    public SecurityContext getContext() {
        if (contextHolder == null) {
            contextHolder = new SecurityContextImpl();
        }

        return contextHolder;
    }

    public void setContext(SecurityContext context) {
        Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
        contextHolder = context;
    }

    public SecurityContext createEmptyContext() {
        return new SecurityContextImpl();
    }
}

存储载体是一个静态变量,也可以在多线程环境下使用,但用的较少。

SecurityContextHolder源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.security.core.context;

import java.lang.reflect.Constructor;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

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";
    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() {
    }

    public static void clearContext() {
        strategy.clearContext();
    }

    public static SecurityContext getContext() {
        return strategy.getContext();
    }

    public static int getInitializeCount() {
        return initializeCount;
    }

    private static void initialize() {
        if (!StringUtils.hasText(strategyName)) {
            strategyName = "MODE_THREADLOCAL";
        }

        if (strategyName.equals("MODE_THREADLOCAL")) {
            strategy = new ThreadLocalSecurityContextHolderStrategy();
        } else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {
            strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
        } else if (strategyName.equals("MODE_GLOBAL")) {
            strategy = new GlobalSecurityContextHolderStrategy();
        } else {
            try {
                Class<?> clazz = Class.forName(strategyName);
                Constructor<?> customStrategy = clazz.getConstructor();
                strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();
            } catch (Exception var2) {
                ReflectionUtils.handleReflectionException(var2);
            }
        }

        ++initializeCount;
    }

    public static void setContext(SecurityContext context) {
        strategy.setContext(context);
    }

    public static void setStrategyName(String strategyName) {
        SecurityContextHolder.strategyName = strategyName;
        initialize();
    }

    public static SecurityContextHolderStrategy getContextHolderStrategy() {
        return strategy;
    }

    public static SecurityContext createEmptyContext() {
        return strategy.createEmptyContext();
    }

    public String toString() {
        return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount=" + initializeCount + "]";
    }

    static {
        initialize();
    }
}

SecurityContextHolder 定义三个静态常量描述三种不同存储策略,在静态代码块中初始化,根据不同的strategyName初始化不同的存储策略,可以调用配置系统变量或者调用setStrategyName改变策略。

在默认情况下,从子线程中获取登录数据是获取不到的,因为默认是MODE_THREADLOCAL策略。

存储策略在System.getProperty("spring.security.strategy");加载,可以配置系统变量来修改策略。

在VM option参数添加

-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

SpringContextHolder默认将用户信息存储在ThreadLocal中,在SpringBoot中不同的请求使用不同的线程处理,是怎么获取到用户的信息的呢? 下篇文章揭晓,有懂的可以在评论区留言。

相关文章
|
1月前
|
设计模式 算法 Java
模板方法--设计模式
模板方法--设计模式
17 0
|
4月前
|
设计模式 算法
设计模式--策略模式(由简单工厂到策略模式到两者结合图文详解+总结提升)
设计模式--策略模式(由简单工厂到策略模式到两者结合图文详解+总结提升)
|
8月前
|
设计模式 Java
java实现23种设计模式-观察者模式
java实现23种设计模式-观察者模式
31 0
|
9月前
|
设计模式 算法 搜索推荐
【设计模式】用Java实现策略模式
多种算法或行为选择:当有多个相关的算法或行为可供选择,并且需要在运行时动态选择其中之一时,策略模式非常适用。它允许根据需求选择适当的策略,而不需要更改客户端代码。
68 0
|
9月前
|
设计模式 算法 Java
【设计模式】用Java实现模板模式
模板模式(Template Pattern)是一种行为设计模式,它定义了一个操作中的算法骨架,将一些步骤延迟到子类中实现。模板模式使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
77 0
|
9月前
|
设计模式 Java API
【设计模式】用Java实现职责链模式
责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着处理链进行传递,直到有一个处理者能够处理该请求。每个处理者都可以决定是否将请求传递给下一个处理者,从而形成一条处理链。
66 0
|
9月前
|
设计模式 Java
【设计模式】用Java实现观察者模式
观察者模式是一种行为设计模式,用于实现对象之间的发布-订阅机制。在该模式中,存在一个主题对象(被观察者),它维护了一个观察者列表,并在自身状态发生改变时通知所有观察者。观察者对象订阅主题对象的状态变化,并在收到通知后执行相应的操作。
50 0
|
设计模式 Java 应用服务中间件
设计模式之责任链模式(Java实现)
设计模式之责任链模式(Java实现)
设计模式之责任链模式(Java实现)
|
设计模式 算法 Java
Java常用设计模式-策略模式
Java常用设计模式-策略模式
133 0
|
设计模式 算法 Java
设计模式之策略模式(Java实现)
设计模式之策略模式(Java实现)
设计模式之策略模式(Java实现)