This article has participated in the call for good writing activities, click to view: back end, big front end double track submission, 20,000 yuan prize pool waiting for you to challenge

Springsecurity access to logged-in user data

In SpringSecurity, user login information is essentially stored in HttpSession. Springsecurity encapsulates and obtains login data in two ways:

  1. From SecurityContextHolder
  2. From the current request object

From SecurityContextHolder

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

The SecurityContextHolder holds the SecurityContext, which defines three different data storage policies using the policy pattern

  1. MODE_THREADLOCAL: Put the SecurityContext in a ThreadLocal and start a child thread that cannot obtain user data.
  2. MODE_INHERITABLETHREADLOCAL: in a multi-threaded environment, child threads can also access user data.
  3. MODE_GLOBAL: Data is stored in a static variable, rarely used in Web development.

The method for SecurityContextHolderStrategy interface used to standardize storage strategy

public interface SecurityContextHolderStrategy {
    void clearContext(a);

    SecurityContext getContext(a);

    void setContext(SecurityContext var1);

    SecurityContext createEmptyContext(a);
}
Copy the code

There are three implementation classes for three different storage policies

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(a) {
        contextHolder.remove();
    }

    public SecurityContext getContext(a) {
        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(a) {
        return newSecurityContextImpl(); }}Copy the code

The storage vector is ThreadLocal and all operations on the SecurityContext are done in a ThreadLocal. SecurityContext is just an interface, and only one implementation class is 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(a) {
        contextHolder.remove();
    }

    public SecurityContext getContext(a) {
        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(a) {
        return newSecurityContextImpl(); }}Copy the code

InheritableThreadLocal inherits ThreadLocal and automatically copies data from the parent thread to the child thread at the time the child thread is created. The function of obtaining login data in sub-thread is realized.

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(a) {
        contextHolder = null;
    }

    public SecurityContext getContext(a) {
        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(a) {
        return newSecurityContextImpl(); }}Copy the code

The storage vector is a static variable that can also be used in multithreaded environments, but less often.

SecurityContextHolder source

//
// 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(a) {}public static void clearContext(a) {
        strategy.clearContext();
    }

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

    public static int getInitializeCount(a) {
        return initializeCount;
    }

    private static void initialize(a) {
        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(a) {
        return strategy;
    }

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

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

    static{ initialize(); }}Copy the code

SecurityContextHolder defines three static constants describing three different storage policies to be initialized in a static code block. Different storage policies can be initialized according to the strategyName. You can call a configuration system variable or call setStrategyName to change the policy.

By default, retrieving login data from child threads is not available because the default is the MODE_THREADLOCAL policy.

Store the policy in system.getProperty (“spring.security.strategy”); Load, you can configure system variables to modify policies.

Added in the VM Option parameter

-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

SpringContextHolder stores user information in a ThreadLocal by default. How does SpringContextHolder retrieve user information when different requests are processed by different threads in SpringBoot? Let us know in the comments section of our next post.