Spring Security:安全上下文SecurityContext介绍与Debug分析

Spring Security:安全上下文SecurityContext介绍与Debug分析,第1张

Spring Security:安全上下文SecurityContext介绍与Debug分析 SecurityContext

定义与当前执行线程关联的最小安全信息的接口,安全上下文存储在SecurityContextHolder中,SecurityContext从SecurityContextHolder获取,并包含当前经过身份验证的用户的Authentication。

SecurityContext接口源码:

public interface SecurityContext extends Serializable {
	
	Authentication getAuthentication();

	
	void setAuthentication(Authentication authentication);
}

SecurityContext接口只有一个实现类,即SecurityContextImpl类。

SecurityContextImpl

SecurityContext接口的基本实现。

public class SecurityContextImpl implements SecurityContext {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    // 身份验证请求令牌
	private Authentication authentication;
    // 无参构造方法
	public SecurityContextImpl() {}
    // 接受Authentication实例的构造方法
	public SecurityContextImpl(Authentication authentication) {
		this.authentication = authentication;
	}

    // 重写equals方法
	@Override
	public boolean equals(Object obj) {
	    // obj也需要是SecurityContextImpl类型
		if (obj instanceof SecurityContextImpl) {
			SecurityContextImpl test = (SecurityContextImpl) obj;
            // 它们的authentication属性都为null
			if ((this.getAuthentication() == null) && (test.getAuthentication() == null)) {
				return true;
			}
            // 它们的authentication属性都不为null,并且调用equals方法返回true
			if ((this.getAuthentication() != null) && (test.getAuthentication() != null)
					&& this.getAuthentication().equals(test.getAuthentication())) {
				return true;
			}
		}

		return false;
	}
    // 返回authentication属性
	@Override
	public Authentication getAuthentication() {
		return authentication;
	}
    // 重写hashCode方法
	@Override
	public int hashCode() {
		if (this.authentication == null) {
			return -1;
		}
		else {
			return this.authentication.hashCode();
		}
	}
    // 设置authentication属性
	@Override
	public void setAuthentication(Authentication authentication) {
		this.authentication = authentication;
	}
    // 重写toString方法
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(super.toString());

		if (this.authentication == null) {
			sb.append(": Null authentication");
		}
		else {
			sb.append(": Authentication: ").append(this.authentication);
		}

		return sb.toString();
	}
}
Debug分析

项目结构图:

pom.xml:



    4.0.0

    com.kaven
    security
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.6.RELEASE
    

    
        8
        8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-security
        
        
            org.projectlombok
            lombok
        
    


application.yml:

spring:
  security:
    user:
      name: kaven
      password: itkaven
      roles: USER
logging:
  level:
    org:
      springframework:
        security: DEBUG

MessageController(定义接口):

package com.kaven.security.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MessageController {
    @GetMapping("/message")
    public String getMessage() {
        return "hello spring security";
    }
}

启动类:

package com.kaven.security;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

SecurityConfig(Spring Security的配置类,不是必须的,因为有默认的配置):

package com.kaven.security.config;

import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 任何请求都需要进行验证
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                 // 记住身份验证
                .rememberMe(Customizer.withDefaults())
                 // 基于表单登陆的身份验证方式
                .formLogin(Customizer.withDefaults());
    }
}

Debug方式启动应用,访问http://localhost:8080/message,Spring Security会通过SecurityContextHolder创建空SecurityContextImpl实例(实例的authentication属性为空)。

ThreadLocalSecurityContextHolderStrategy是基于ThreadLocal的SecurityContextHolderStrategy(针对线程存储安全上下文信息的策略,SecurityContextHolder使用它管理SecurityContext)实现。SecurityContextHolder使用ThreadLocalSecurityContextHolderStrategy实例创建空SecurityContextImpl实例(实例的authentication属性为空)。


然后AnonymousAuthenticationFilter会处理请求(当前是匿名访问资源),该过滤器会设置该空SecurityContextImpl实例的authentication属性为AnonymousAuthenticationToken实例。

而通过身份验证(authenticated属性为true)的AnonymousAuthenticationToken实例(身份验证请求令牌),也是没有权限访问/message接口的,所以请求被拒绝访问了。

之后请求会被重定向到表单登陆页面,需要填写用户名和密码进行身份验证。

点击登陆后,又会创建一个空SecurityContextImpl实例(实例的authentication属性为空)。

之后会设置该空SecurityContextImpl实例的authentication属性为UsernamePasswordAuthenticationToken实例,并且该UsernamePasswordAuthenticationToken实例的authenticated属性为true(通过身份验证)。

控制台的日志也能说明身份验证成功了。

请求也成功访问到了/message接口。

安全上下文SecurityContext介绍与Debug分析就到这里,如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5712217.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存