or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-configuration.mdcore-annotations.mdhttp-configurers.mdindex.mdmethod-security.mdoauth2-configuration.mdsecurity-builders.md

method-security.mddocs/

0

# Method Security Configuration

1

2

Spring Security Config provides comprehensive method-level security configuration through annotations and configuration classes. Method security enables fine-grained access control at the method level using annotations like @PreAuthorize, @PostAuthorize, @Secured, and JSR-250 annotations.

3

4

## Method Security Annotations

5

6

### @EnableMethodSecurity

7

8

Modern method-level security configuration annotation (Spring Security 5.6+).

9

10

```java { .api }

11

@Target(ElementType.TYPE)

12

@Retention(RetentionPolicy.RUNTIME)

13

@Documented

14

@Import(MethodSecurityConfiguration.class)

15

public @interface EnableMethodSecurity {

16

/**

17

* Determines if Spring Security's pre/post annotations should be enabled.

18

* @return true if pre/post annotations are enabled, false otherwise. Default is true.

19

*/

20

boolean prePostEnabled() default true;

21

22

/**

23

* Determines if Spring Security's @Secured annotation should be enabled.

24

* @return true if @Secured is enabled, false otherwise. Default is false.

25

*/

26

boolean securedEnabled() default false;

27

28

/**

29

* Determines if JSR-250 annotations should be enabled.

30

* @return true if JSR-250 annotations are enabled, false otherwise. Default is false.

31

*/

32

boolean jsr250Enabled() default false;

33

34

/**

35

* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed

36

* to standard Java interface-based proxies.

37

* @return true to use CGLIB proxies, false for JDK proxies. Default is false.

38

*/

39

boolean proxyTargetClass() default false;

40

41

/**

42

* Indicate how security advice should be applied.

43

* @return the advice mode. Default is PROXY.

44

*/

45

AdviceMode mode() default AdviceMode.PROXY;

46

47

/**

48

* Indicate the offset in the order for SecurityMethodInterceptor.

49

* @return the offset value. Default is 0.

50

*/

51

int offset() default 0;

52

}

53

```

54

55

**Usage Example:**

56

57

```java

58

@Configuration

59

@EnableMethodSecurity(

60

prePostEnabled = true,

61

securedEnabled = true,

62

jsr250Enabled = true

63

)

64

public class MethodSecurityConfig {

65

66

@Bean

67

public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {

68

DefaultMethodSecurityExpressionHandler expressionHandler =

69

new DefaultMethodSecurityExpressionHandler();

70

expressionHandler.setPermissionEvaluator(customPermissionEvaluator());

71

return expressionHandler;

72

}

73

}

74

```

75

76

### @EnableGlobalMethodSecurity (Deprecated)

77

78

Legacy method-level security configuration annotation.

79

80

```java { .api }

81

@Target(ElementType.TYPE)

82

@Retention(RetentionPolicy.RUNTIME)

83

@Documented

84

@Import(GlobalMethodSecurityConfiguration.class)

85

@Deprecated

86

public @interface EnableGlobalMethodSecurity {

87

boolean prePostEnabled() default false;

88

boolean securedEnabled() default false;

89

boolean jsr250Enabled() default false;

90

boolean proxyTargetClass() default false;

91

AdviceMode mode() default AdviceMode.PROXY;

92

int order() default Ordered.LOWEST_PRECEDENCE;

93

}

94

```

95

96

### @EnableReactiveMethodSecurity

97

98

Reactive method security configuration for WebFlux applications.

99

100

```java { .api }

101

@Target(ElementType.TYPE)

102

@Retention(RetentionPolicy.RUNTIME)

103

@Documented

104

@Import(ReactiveMethodSecurityConfiguration.class)

105

public @interface EnableReactiveMethodSecurity {

106

/**

107

* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed

108

* to standard Java interface-based proxies.

109

* @return true to use CGLIB proxies, false for JDK proxies. Default is false.

110

*/

111

boolean proxyTargetClass() default false;

112

113

/**

114

* Indicate how security advice should be applied.

115

* @return the advice mode. Default is PROXY.

116

*/

117

AdviceMode mode() default AdviceMode.PROXY;

118

119

/**

120

* Indicate the order in which the SecurityMethodInterceptor should be applied.

121

* @return the order value. Default is LOWEST_PRECEDENCE.

122

*/

123

int order() default Ordered.LOWEST_PRECEDENCE;

124

125

/**

126

* Indicate whether to use AuthorizationManager for reactive method security.

127

* @return true to use AuthorizationManager, false for legacy approach. Default is true.

128

*/

129

boolean useAuthorizationManager() default true;

130

}

131

```

132

133

## Method Security Configuration Classes

134

135

### GlobalMethodSecurityConfiguration

136

137

Base class for method security configuration (legacy approach).

138

139

```java { .api }

140

public abstract class GlobalMethodSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

141

142

// Configuration Methods

143

protected void configure(AuthenticationManagerBuilder auth) throws Exception {}

144

145

// Access Decision Manager

146

protected AccessDecisionManager accessDecisionManager() {

147

List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();

148

ExpressionBasedPreInvocationAdvice expressionAdvice =

149

new ExpressionBasedPreInvocationAdvice();

150

expressionAdvice.setExpressionHandler(getExpressionHandler());

151

152

if (prePostEnabled()) {

153

decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));

154

}

155

if (jsr250Enabled()) {

156

decisionVoters.add(new Jsr250Voter());

157

}

158

159

return new AffirmativeBased(decisionVoters);

160

}

161

162

// Expression Handler

163

protected MethodSecurityExpressionHandler createExpressionHandler() {

164

DefaultMethodSecurityExpressionHandler expressionHandler =

165

new DefaultMethodSecurityExpressionHandler();

166

if (authenticationTrustResolver != null) {

167

expressionHandler.setTrustResolver(authenticationTrustResolver);

168

}

169

if (roleHierarchy != null) {

170

expressionHandler.setRoleHierarchy(roleHierarchy);

171

}

172

if (permissionEvaluator != null) {

173

expressionHandler.setPermissionEvaluator(permissionEvaluator);

174

}

175

return expressionHandler;

176

}

177

178

// Configuration Properties

179

protected boolean prePostEnabled() { return false; }

180

protected boolean securedEnabled() { return false; }

181

protected boolean jsr250Enabled() { return false; }

182

183

// Authentication Manager

184

protected AuthenticationManager authenticationManager() throws Exception {

185

return authenticationManagerBuilder.build();

186

}

187

188

// Run As Manager

189

protected RunAsManager runAsManager() { return null; }

190

191

// Permission Evaluator

192

protected PermissionEvaluator permissionEvaluator() { return null; }

193

194

// Role Hierarchy

195

protected RoleHierarchy roleHierarchy() { return null; }

196

197

// Authentication Trust Resolver

198

protected AuthenticationTrustResolver authenticationTrustResolver() { return null; }

199

}

200

```

201

202

**Usage Example:**

203

204

```java

205

@Configuration

206

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)

207

public class CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration {

208

209

@Override

210

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

211

auth.userDetailsService(customUserDetailsService())

212

.passwordEncoder(passwordEncoder());

213

}

214

215

@Override

216

protected MethodSecurityExpressionHandler createExpressionHandler() {

217

DefaultMethodSecurityExpressionHandler expressionHandler =

218

new DefaultMethodSecurityExpressionHandler();

219

expressionHandler.setPermissionEvaluator(customPermissionEvaluator());

220

expressionHandler.setRoleHierarchy(roleHierarchy());

221

return expressionHandler;

222

}

223

224

@Override

225

protected AccessDecisionManager accessDecisionManager() {

226

List<AccessDecisionVoter<?>> decisionVoters = Arrays.asList(

227

new RoleVoter(),

228

new AuthenticatedVoter(),

229

new WebExpressionVoter()

230

);

231

return new AffirmativeBased(decisionVoters);

232

}

233

234

@Bean

235

public RoleHierarchy roleHierarchy() {

236

RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();

237

roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER > ROLE_GUEST");

238

return roleHierarchy;

239

}

240

241

@Bean

242

public PermissionEvaluator customPermissionEvaluator() {

243

return new CustomPermissionEvaluator();

244

}

245

}

246

```

247

248

### ReactiveMethodSecurityConfiguration

249

250

Configuration class for reactive method security.

251

252

```java { .api }

253

@Configuration

254

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)

255

public class ReactiveMethodSecurityConfiguration {

256

257

@Bean

258

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)

259

public ReactiveMethodSecurityInterceptor methodSecurityInterceptor(

260

ReactiveAuthorizationManager<MethodInvocation> authorizationManager) {

261

ReactiveMethodSecurityInterceptor interceptor =

262

new ReactiveMethodSecurityInterceptor();

263

interceptor.setAuthorizationManager(authorizationManager);

264

return interceptor;

265

}

266

267

@Bean

268

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)

269

public ReactiveAuthorizationManager<MethodInvocation> methodAuthorizationManager() {

270

return new PrePostAdviceReactiveMethodInterceptor();

271

}

272

}

273

```

274

275

## Method Security Infrastructure

276

277

### MethodSecurityMetadataSourceAdvisorRegistrar

278

279

Registrar for method security metadata source advisors.

280

281

```java { .api }

282

public final class MethodSecurityMetadataSourceAdvisorRegistrar implements ImportBeanDefinitionRegistrar {

283

284

@Override

285

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,

286

BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {

287

288

// Register method security advisor

289

BeanDefinition advisor = BeanDefinitionBuilder

290

.rootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class)

291

.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)

292

.getBeanDefinition();

293

294

registry.registerBeanDefinition("methodSecurityAdvisor", advisor);

295

}

296

}

297

```

298

299

### AuthorizationProxyConfiguration

300

301

Configuration for authorization proxies in method security.

302

303

```java { .api }

304

@Configuration

305

@ConditionalOnClass(AuthorizationManager.class)

306

public class AuthorizationProxyConfiguration {

307

308

@Bean

309

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)

310

public AuthorizationAdvisor authorizationAdvisor(

311

AuthorizationManager<MethodInvocation> authorizationManager) {

312

return new AuthorizationAdvisor(authorizationManager);

313

}

314

315

@Bean

316

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)

317

public AuthorizationManager<MethodInvocation> methodAuthorizationManager() {

318

return new PrePostAdviceAuthorizationManager();

319

}

320

}

321

```

322

323

## Security Expressions and Evaluation

324

325

### MethodSecurityExpressionHandler

326

327

Interface for handling method security expressions.

328

329

```java { .api }

330

public interface MethodSecurityExpressionHandler extends SecurityExpressionHandler<MethodInvocation> {

331

332

/**

333

* Filter the collection or array returned from a method invocation.

334

* @param filterObject the collection or array to filter

335

* @param expression the expression to evaluate for each element

336

* @param ctx the evaluation context

337

* @return the filtered collection or array

338

*/

339

Object filter(Object filterObject, Expression expression, EvaluationContext ctx);

340

341

/**

342

* Create an evaluation context for method invocation.

343

* @param auth the current authentication

344

* @param mi the method invocation

345

* @return the evaluation context

346

*/

347

EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi);

348

349

/**

350

* Set the permission evaluator for object-level security.

351

* @param permissionEvaluator the permission evaluator

352

*/

353

void setPermissionEvaluator(PermissionEvaluator permissionEvaluator);

354

355

/**

356

* Set the role hierarchy for inherited roles.

357

* @param roleHierarchy the role hierarchy

358

*/

359

void setRoleHierarchy(RoleHierarchy roleHierarchy);

360

361

/**

362

* Set the trust resolver for authentication state evaluation.

363

* @param trustResolver the authentication trust resolver

364

*/

365

void setTrustResolver(AuthenticationTrustResolver trustResolver);

366

}

367

```

368

369

### DefaultMethodSecurityExpressionHandler

370

371

Default implementation of method security expression handler.

372

373

```java { .api }

374

public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>

375

implements MethodSecurityExpressionHandler, BeanFactoryAware {

376

377

private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();

378

private RoleHierarchy roleHierarchy;

379

private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();

380

381

@Override

382

public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {

383

MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());

384

MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(auth);

385

root.setPermissionEvaluator(permissionEvaluator);

386

root.setTrustResolver(trustResolver);

387

root.setRoleHierarchy(roleHierarchy);

388

ctx.setRootObject(root);

389

return ctx;

390

}

391

392

@Override

393

public Object filter(Object filterObject, Expression expression, EvaluationContext ctx) {

394

if (filterObject instanceof Collection) {

395

return filterCollection((Collection<?>) filterObject, expression, ctx);

396

} else if (filterObject.getClass().isArray()) {

397

return filterArray((Object[]) filterObject, expression, ctx);

398

}

399

return filterObject;

400

}

401

402

// Setter methods

403

public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {

404

this.permissionEvaluator = permissionEvaluator;

405

}

406

407

public void setRoleHierarchy(RoleHierarchy roleHierarchy) {

408

this.roleHierarchy = roleHierarchy;

409

}

410

411

public void setTrustResolver(AuthenticationTrustResolver trustResolver) {

412

this.trustResolver = trustResolver;

413

}

414

}

415

```

416

417

## Method Security Annotations Usage

418

419

### Pre/Post Authorization Annotations

420

421

Security annotations for method-level access control.

422

423

```java { .api }

424

// Pre-authorization annotation

425

@Target({ElementType.METHOD, ElementType.TYPE})

426

@Retention(RetentionPolicy.RUNTIME)

427

@Inherited

428

@Documented

429

public @interface PreAuthorize {

430

/**

431

* Spring Expression Language (SpEL) expression used to determine

432

* if a method invocation is allowed.

433

* @return the SpEL expression

434

*/

435

String value();

436

}

437

438

// Post-authorization annotation

439

@Target({ElementType.METHOD, ElementType.TYPE})

440

@Retention(RetentionPolicy.RUNTIME)

441

@Inherited

442

@Documented

443

public @interface PostAuthorize {

444

/**

445

* Spring Expression Language (SpEL) expression used to determine

446

* if a method return value should be returned to the caller.

447

* @return the SpEL expression

448

*/

449

String value();

450

}

451

452

// Pre-filtering annotation

453

@Target({ElementType.METHOD, ElementType.TYPE})

454

@Retention(RetentionPolicy.RUNTIME)

455

@Inherited

456

@Documented

457

public @interface PreFilter {

458

/**

459

* Spring Expression Language (SpEL) expression used to filter

460

* method arguments before method invocation.

461

* @return the SpEL expression

462

*/

463

String value();

464

465

/**

466

* Name of the argument to filter when there are multiple arguments.

467

* @return the argument name

468

*/

469

String filterTarget() default "";

470

}

471

472

// Post-filtering annotation

473

@Target({ElementType.METHOD, ElementType.TYPE})

474

@Retention(RetentionPolicy.RUNTIME)

475

@Inherited

476

@Documented

477

public @interface PostFilter {

478

/**

479

* Spring Expression Language (SpEL) expression used to filter

480

* the method return value.

481

* @return the SpEL expression

482

*/

483

String value();

484

}

485

486

// Secured annotation

487

@Target({ElementType.METHOD, ElementType.TYPE})

488

@Retention(RetentionPolicy.RUNTIME)

489

@Inherited

490

@Documented

491

public @interface Secured {

492

/**

493

* List of roles/authorities required to invoke the secured method.

494

* @return the required roles/authorities

495

*/

496

String[] value();

497

}

498

```

499

500

**Usage Examples:**

501

502

```java

503

@Service

504

public class DocumentService {

505

506

// Pre-authorization: Check role before method execution

507

@PreAuthorize("hasRole('ADMIN')")

508

public void deleteDocument(Long documentId) {

509

// Implementation

510

}

511

512

// Pre-authorization: Check ownership

513

@PreAuthorize("@documentService.isOwner(authentication.name, #documentId)")

514

public Document getDocument(Long documentId) {

515

// Implementation

516

}

517

518

// Post-authorization: Filter return value based on ownership

519

@PostAuthorize("returnObject.owner == authentication.name or hasRole('ADMIN')")

520

public Document loadDocument(Long documentId) {

521

// Implementation

522

}

523

524

// Pre-filtering: Filter collection arguments

525

@PreFilter("hasPermission(filterObject, 'READ')")

526

public List<Document> processDocuments(List<Document> documents) {

527

// Process only documents the user can read

528

return documents;

529

}

530

531

// Post-filtering: Filter collection return values

532

@PostFilter("hasPermission(filterObject, 'READ')")

533

public List<Document> findAllDocuments() {

534

// Return only documents the user can read

535

return documentRepository.findAll();

536

}

537

538

// Secured annotation: Simple role-based access

539

@Secured({"ROLE_USER", "ROLE_ADMIN"})

540

public List<Document> getUserDocuments() {

541

// Implementation

542

}

543

544

// JSR-250 annotations

545

@RolesAllowed("ADMIN")

546

public void adminOnlyMethod() {

547

// Implementation

548

}

549

550

@PermitAll

551

public String publicMethod() {

552

return "Public access";

553

}

554

555

@DenyAll

556

public void restrictedMethod() {

557

// This method is never accessible

558

}

559

}

560

```

561

562

## Custom Permission Evaluation

563

564

### PermissionEvaluator Interface

565

566

Interface for custom permission evaluation logic.

567

568

```java { .api }

569

public interface PermissionEvaluator {

570

/**

571

* Determine whether the user has the given permission for the domain object.

572

* @param authentication the user authentication

573

* @param targetDomainObject the domain object

574

* @param permission the permission to check

575

* @return true if access is granted, false otherwise

576

*/

577

boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission);

578

579

/**

580

* Determine whether the user has the given permission for the domain object

581

* identified by its identifier.

582

* @param authentication the user authentication

583

* @param targetId the identifier of the domain object

584

* @param targetType the type of the domain object

585

* @param permission the permission to check

586

* @return true if access is granted, false otherwise

587

*/

588

boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);

589

}

590

```

591

592

**Custom Permission Evaluator Example:**

593

594

```java

595

@Component

596

public class CustomPermissionEvaluator implements PermissionEvaluator {

597

598

@Autowired

599

private PermissionService permissionService;

600

601

@Override

602

public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {

603

if (authentication == null || targetDomainObject == null || !(permission instanceof String)) {

604

return false;

605

}

606

607

String username = authentication.getName();

608

String permissionName = (String) permission;

609

610

if (targetDomainObject instanceof Document) {

611

Document document = (Document) targetDomainObject;

612

return permissionService.hasDocumentPermission(username, document.getId(), permissionName);

613

}

614

615

return false;

616

}

617

618

@Override

619

public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {

620

if (authentication == null || targetId == null || targetType == null || !(permission instanceof String)) {

621

return false;

622

}

623

624

String username = authentication.getName();

625

String permissionName = (String) permission;

626

627

if ("Document".equals(targetType)) {

628

return permissionService.hasDocumentPermission(username, (Long) targetId, permissionName);

629

}

630

631

return false;

632

}

633

}

634

635

// Usage in service methods

636

@Service

637

public class DocumentService {

638

639

@PreAuthorize("hasPermission(#documentId, 'Document', 'READ')")

640

public Document getDocument(Long documentId) {

641

return documentRepository.findById(documentId);

642

}

643

644

@PreAuthorize("hasPermission(#document, 'WRITE')")

645

public Document updateDocument(Document document) {

646

return documentRepository.save(document);

647

}

648

}

649

```

650

651

## Advanced Method Security Patterns

652

653

### Class-Level Security

654

655

Apply security annotations at the class level for all methods:

656

657

```java

658

@Service

659

@PreAuthorize("hasRole('USER')")

660

public class UserService {

661

662

// All methods require USER role by default

663

public List<User> getAllUsers() {

664

return userRepository.findAll();

665

}

666

667

// Override class-level security

668

@PreAuthorize("hasRole('ADMIN')")

669

public void deleteUser(Long userId) {

670

userRepository.deleteById(userId);

671

}

672

673

// No additional security beyond class level

674

public User getCurrentUser(Authentication auth) {

675

return userRepository.findByUsername(auth.getName());

676

}

677

}

678

```

679

680

### Security with Complex Expressions

681

682

Use complex SpEL expressions for sophisticated authorization logic:

683

684

```java

685

@Service

686

public class ProjectService {

687

688

@PreAuthorize("hasRole('ADMIN') or " +

689

"(hasRole('PROJECT_MANAGER') and @projectService.isProjectManager(authentication.name, #projectId)) or " +

690

"(hasRole('DEVELOPER') and @projectService.isProjectMember(authentication.name, #projectId))")

691

public Project getProject(Long projectId) {

692

return projectRepository.findById(projectId);

693

}

694

695

@PreAuthorize("@projectService.canEditProject(authentication, #project)")

696

public Project updateProject(Project project) {

697

return projectRepository.save(project);

698

}

699

700

public boolean canEditProject(Authentication auth, Project project) {

701

String username = auth.getName();

702

return hasRole(auth, "ADMIN") ||

703

(hasRole(auth, "PROJECT_MANAGER") && isProjectManager(username, project.getId())) ||

704

(hasRole(auth, "DEVELOPER") && isProjectLead(username, project.getId()));

705

}

706

}

707

```

708

709

### Reactive Method Security

710

711

Method security for reactive applications:

712

713

```java

714

@Service

715

public class ReactiveDocumentService {

716

717

@PreAuthorize("hasRole('USER')")

718

public Mono<Document> getDocument(String documentId) {

719

return documentRepository.findById(documentId);

720

}

721

722

@PreAuthorize("hasPermission(#document, 'WRITE')")

723

public Mono<Document> saveDocument(Document document) {

724

return documentRepository.save(document);

725

}

726

727

@PostFilter("hasPermission(filterObject, 'READ')")

728

public Flux<Document> findAllDocuments() {

729

return documentRepository.findAll();

730

}

731

}

732

```