1
0

Split into per-article modules and add the method-security module

Moves the existing virtual-thread/context-propagation project into
context-propagation/ and adds method-security/ for the Spring Security 7
method-security article: nine runnable demos, fourteen assertions, and every
transcript the article quotes, regenerated by scripts/run-all.sh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSrsDSRKVsY588yFiMJMo9
This commit is contained in:
2026-08-25 02:01:29 +00:00
parent 9f950bffa9
commit 5e9e7f1b12
65 changed files with 4088 additions and 119 deletions

View File

@@ -0,0 +1,88 @@
package com.ankurm.methodsec;
import java.lang.reflect.Method;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.core.parameters.P;
/**
* Demo 9 -- {@code #parameterName} depends on a compiler flag.
*
* <p>{@code @PreAuthorize("#owner == authentication.name")} resolves {@code #owner} by looking
* up the method's parameter names through a {@code ParameterNameDiscoverer}. Parameter names
* survive compilation only when {@code javac} is given {@code -parameters}. Without it the
* name is {@code arg0}, {@code #owner} resolves to nothing, and the comparison is false --
* every call is denied.
*
* <p>{@code scripts/run-all.sh} compiles this module twice and runs this class from both
* builds, so {@code docs/output/demo9-with-parameters.txt} and
* {@code docs/output/demo9-without-parameters.txt} are the same code under the two flags.
*
* <p>Chapter:
* <a href="../../../../../../docs/02-spel-reference.md">docs/02-spel-reference.md</a>.
*/
public class Demo9ParameterNames {
public static void main(String[] args) throws Exception {
Support.banner("Demo 9 -- #parameterName and the -parameters compiler flag");
Method byName = Owned.class.getMethod("byParameterName", String.class);
Method byAlias = Owned.class.getMethod("byParameterAlias", String.class);
System.out.println(" compiled with -parameters : " + byName.getParameters()[0].isNamePresent());
System.out.println(" byParameterName param[0] : " + byName.getParameters()[0].getName());
System.out.println(" byParameterAlias param[0] : " + byAlias.getParameters()[0].getName()
+ " (annotated @P(\"o\"))");
try (var ctx = new AnnotationConfigApplicationContext(Config.class)) {
Owned owned = ctx.getBean(Owned.class);
Support.login("alice", "ROLE_USER");
Support.heading("alice calling with her own name");
Support.attempt("#owner == authentication.name", () -> owned.byParameterName("alice"));
Support.attempt("#o == authentication.name (@P(\"o\"))", () -> owned.byParameterAlias("alice"));
Support.heading("alice calling with somebody else's name");
Support.attempt("#owner == authentication.name", () -> owned.byParameterName("bob"));
Support.attempt("#o == authentication.name (@P(\"o\"))", () -> owned.byParameterAlias("bob"));
}
finally {
Support.logout();
}
System.out.println();
System.out.println(" Without -parameters the first expression denies BOTH calls -- it fails");
System.out.println(" closed, which is the good direction, but it fails silently in the sense");
System.out.println(" that nothing tells you the rule is not the rule you wrote. @P(\"o\") does");
System.out.println(" not depend on the flag, because the name is in the class file either way.");
}
@Configuration
@EnableMethodSecurity
static class Config {
@Bean
Owned owned() {
return new Owned();
}
}
public static class Owned {
@PreAuthorize("#owner == authentication.name")
public String byParameterName(String owner) {
return "ok";
}
@PreAuthorize("#o == authentication.name")
public String byParameterAlias(@P("o") String owner) {
return "ok";
}
}
}