实测 Spring Security 7 的 FilterChainProxy:默认链有 16 个过滤器;两条链时每个请求只选第一条匹配的链(/api/** 链 11 个、兜底链 15 个);401 触发的 /error 派发会被兜底链拦下变成 302 到 /login;兜底链放在前面会在启动时失败;ignoring 的路径完全不经过过滤器,没有安全响应头,permitAll 的路径仍然经过 11 个过滤器。
实测 Spring Security 7 的认证流程:ProviderManager 里默认只有一个 DaoAuthenticationProvider;密码错与用户不存在都是 BadCredentialsException,耗时相近;禁用、锁定、凭据过期有各自的异常和事件;成功返回的是新对象,凭据被擦掉并多了 FACTOR_PASSWORD 权限;SecurityContext 默认只在请求线程里有,INHERITABLETHREADLOCAL 会让线程池里一直是第一次请求的用户。
实测 Spring Security 7 的 PasswordEncoder:BCrypt strength 每加 2 耗时约 ×4(10 是 70.1 ms,12 是 274.6 ms,14 是 1105.2 ms);DelegatingPasswordEncoder 用 {id} 前缀选算法,没有前缀会抛异常,upgradeEncoding 可以识别旧哈希;encode 拒绝超过 72 字节的密码,但 matches 会忽略 72 字节之后的内容;Pbkdf2、SCrypt、Argon2 的默认参数耗时。
实测 Spring Security 7 的登录态:表单登录后 SecurityContext 存在服务端 HttpSession 的 SPRING_SECURITY_CONTEXT,cookie 只有 JSESSIONID(HttpOnly);登录前后 session id 默认会变,关掉后不变;STATELESS 模式不创建会话;自己写登录接口只 setContext 而不 saveContext,下一个请求就没登录;退出后旧 cookie 失效。
实测 Spring Security 7 的 CSRF:默认令牌每次取到的值不同(96 字符的掩码)但都能通过校验;POST、DELETE 不带令牌 403,GET、HEAD、OPTIONS 放行;登录后旧令牌作废;Cookie 方案下把 36 字符的 cookie 值原样放进请求头会被默认处理器拒绝,换成明文处理器才通过;ignoringRequestMatchers 只放行指定路径。
实测 Spring Security 7 加 Spring MVC 的 CORS:MVC 里配了 CORS 而 Security 没有 .cors() 时,预检(不带凭据的 OPTIONS)被认证拦成 401,没有任何 CORS 头;加上 http.cors(withDefaults) 后预检 200 并返回 allow-origin、allow-methods、allow-headers、max-age 1800,不允许的 Origin、方法、请求头都是 403;需要凭据时用 allowedOriginPatterns。
实测 Spring Security 7 的方法级安全:@PreAuthorize 靠 CGLIB 代理实现,类内部自调用和 private 方法绕过检查,接口上的注解有效;@PostAuthorize 在方法执行之后才拒绝;@PreFilter 对不可变列表不报错也不过滤;@Secured 与 @RolesAllowed 在默认的 @EnableMethodSecurity 下完全不生效。
实测 Spring Security 7 的授权:hasRole("X") 等价于 hasAuthority("ROLE_X"),权限叫 ADMIN 的用户过不了 hasRole("ADMIN");hasRole("ROLE_ADMIN") 启动失败;规则按声明顺序第一个匹配的生效,写反会让 /a/admin/** 被 /a/** 的 USER 规则吞掉;anyRequest 之后不能再加 requestMatchers;RoleHierarchy 让 ADMIN 自动拥有 USER;自定义 AuthorizationManager 可以按请求信息决策。
实测 Spring Security 7 的 JWT 资源服务器:scope 映射成 SCOPE_ 权限、roles 声明默认被忽略;篡改、alg=none、HS256 混淆的令牌都被拒绝;默认的校验只看时间不看签发者,别的 iss 的令牌也会通过;时间偏差默认容忍 60 秒;JWKS 里 kid 不在缓存时会立即重新获取,切换到新密钥后旧密钥立即失效;JWKS 服务宕机时已缓存的密钥继续可用。
用 Spring Authorization Server 跑通授权码加 PKCE:公开客户端不需要 client_secret,换令牌必须带 code_verifier;授权码只能用一次;缺 code_challenge 或用 plain 会被拒绝;redirect_uri 未注册时不重定向;公开客户端拿不到 refresh_token,机密客户端的 refresh_token 默认被复用;access_token 是 300 秒的 RS256 JWT。
实测 ApplicationContext.refresh() 的 12 个步骤:BeanDefinition 在 invokeBeanFactoryPostProcessors 里从 6 个涨到 15 个(最慢的一步,76456 µs);BeanFactoryPostProcessor、BeanPostProcessor 本身最先被创建;普通单例在 finishBeanFactoryInitialization 里创建,prototype 与 @Lazy 的 Bean 不创建;refresh 失败时已创建的单例会被销毁。
实测 BeanDefinition:手写、@Component 扫描、@Bean 方法三种来源的定义类型不同(@Bean 的定义类是 null、只有 factoryMethod);BeanFactoryPostProcessor 能在创建之前把 scope 改成 prototype;子定义会和 abstract 父定义合并成 RootBeanDefinition;同名定义在纯 Spring 里默认悄悄覆盖,SpringApplication 的上下文默认不允许。