2
0

method-security.adoc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. [[jc-method]]
  2. = Method Security
  3. From version 2.0 onwards, Spring Security has improved support substantially for adding security to your service layer methods.
  4. It provides support for JSR-250 annotation security as well as the framework's original `@Secured` annotation.
  5. From 3.0, you can also make use of new xref:servlet/authorization/expression-based.adoc#el-access[expression-based annotations].
  6. You can apply security to a single bean, by using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer by using AspectJ style pointcuts.
  7. [[jc-enable-method-security]]
  8. == EnableMethodSecurity
  9. In Spring Security 5.6, we can enable annotation-based security using the `@EnableMethodSecurity` annotation on any `@Configuration` instance.
  10. This improves upon `@EnableGlobalMethodSecurity` in a number of ways. `@EnableMethodSecurity`:
  11. 1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.
  12. This simplifies reuse and customization.
  13. 2. Favors direct bean-based configuration, instead of requiring extending `GlobalMethodSecurityConfiguration` to customize beans
  14. 3. Is built using native Spring AOP, removing abstractions and allowing you to use Spring AOP building blocks to customize
  15. 4. Checks for conflicting annotations to ensure an unambiguous security configuration
  16. 5. Complies with JSR-250
  17. 6. Enables `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` by default
  18. [NOTE]
  19. ====
  20. For earlier versions, please read about similar support with <<jc-enable-global-method-security, @EnableGlobalMethodSecurity>>.
  21. ====
  22. For example, the following would enable Spring Security's `@PreAuthorize` annotation:
  23. .Method Security Configuration
  24. ====
  25. .Java
  26. [source,java,role="primary"]
  27. ----
  28. @Configuration
  29. @EnableMethodSecurity
  30. public class MethodSecurityConfig {
  31. // ...
  32. }
  33. ----
  34. .Kotlin
  35. [source,kotlin,role="secondary"]
  36. ----
  37. @Configuration
  38. @EnableMethodSecurity
  39. class MethodSecurityConfig {
  40. // ...
  41. }
  42. ----
  43. .Xml
  44. [source,xml,role="secondary"]
  45. ----
  46. <sec:method-security/>
  47. ----
  48. ====
  49. Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
  50. Spring Security's native annotation support defines a set of attributes for the method.
  51. These will be passed to the `DefaultAuthorizationMethodInterceptorChain` for it to make the actual decision:
  52. .Method Security Annotation Usage
  53. ====
  54. .Java
  55. [source,java,role="primary"]
  56. ----
  57. public interface BankService {
  58. @PreAuthorize("hasRole('USER')")
  59. Account readAccount(Long id);
  60. @PreAuthorize("hasRole('USER')")
  61. List<Account> findAccounts();
  62. @PreAuthorize("hasRole('TELLER')")
  63. Account post(Account account, Double amount);
  64. }
  65. ----
  66. .Kotlin
  67. [source,kotlin,role="secondary"]
  68. ----
  69. interface BankService {
  70. @PreAuthorize("hasRole('USER')")
  71. fun readAccount(id : Long) : Account
  72. @PreAuthorize("hasRole('USER')")
  73. fun findAccounts() : List<Account>
  74. @PreAuthorize("hasRole('TELLER')")
  75. fun post(account : Account, amount : Double) : Account
  76. }
  77. ----
  78. ====
  79. You can enable support for Spring Security's `@Secured` annotation using:
  80. .@Secured Configuration
  81. ====
  82. .Java
  83. [source,java,role="primary"]
  84. ----
  85. @Configuration
  86. @EnableMethodSecurity(securedEnabled = true)
  87. public class MethodSecurityConfig {
  88. // ...
  89. }
  90. ----
  91. .Kotlin
  92. [source,kotlin,role="secondary"]
  93. ----
  94. @Configuration
  95. @EnableMethodSecurity(securedEnabled = true)
  96. class MethodSecurityConfig {
  97. // ...
  98. }
  99. ----
  100. .Xml
  101. [source,xml,role="secondary"]
  102. ----
  103. <sec:method-security secured-enabled="true"/>
  104. ----
  105. ====
  106. or JSR-250 using:
  107. .JSR-250 Configuration
  108. ====
  109. .Java
  110. [source,java,role="primary"]
  111. ----
  112. @Configuration
  113. @EnableMethodSecurity(jsr250Enabled = true)
  114. public class MethodSecurityConfig {
  115. // ...
  116. }
  117. ----
  118. .Kotlin
  119. [source,kotlin,role="secondary"]
  120. ----
  121. @Configuration
  122. @EnableMethodSecurity(jsr250Enabled = true)
  123. class MethodSecurityConfig {
  124. // ...
  125. }
  126. ----
  127. .Xml
  128. [source,xml,role="secondary"]
  129. ----
  130. <sec:method-security jsr250-enabled="true"/>
  131. ----
  132. ====
  133. === Customizing Authorization
  134. Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support.
  135. [[jc-method-security-custom-expression-handler]]
  136. If you need to customize the way that expressions are handled, you can expose a custom `MethodSecurityExpressionHandler`, like so:
  137. .Custom MethodSecurityExpressionHandler
  138. ====
  139. .Java
  140. [source,java,role="primary"]
  141. ----
  142. @Bean
  143. static MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
  144. DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
  145. handler.setTrustResolver(myCustomTrustResolver);
  146. return handler;
  147. }
  148. ----
  149. .Kotlin
  150. [source,kotlin,role="secondary"]
  151. ----
  152. companion object {
  153. @Bean
  154. fun methodSecurityExpressionHandler() : MethodSecurityExpressionHandler {
  155. val handler = DefaultMethodSecurityExpressionHandler();
  156. handler.setTrustResolver(myCustomTrustResolver);
  157. return handler;
  158. }
  159. }
  160. ----
  161. .Xml
  162. [source,xml,role="secondary"]
  163. ----
  164. <sec:method-security>
  165. <sec:expression-handler ref="myExpressionHandler"/>
  166. </sec:method-security>
  167. <bean id="myExpressionHandler"
  168. class="org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler">
  169. <property name="trustResolver" ref="myCustomTrustResolver"/>
  170. </bean>
  171. ----
  172. ====
  173. [TIP]
  174. ====
  175. We expose `MethodSecurityExpressionHandler` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes
  176. ====
  177. Also, for role-based authorization, Spring Security adds a default `ROLE_` prefix, which is uses when evaluating expressions like `hasRole`.
  178. [[jc-method-security-custom-granted-authority-defaults]]
  179. You can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so:
  180. .Custom MethodSecurityExpressionHandler
  181. ====
  182. .Java
  183. [source,java,role="primary"]
  184. ----
  185. @Bean
  186. static GrantedAuthorityDefaults grantedAuthorityDefaults() {
  187. return new GrantedAuthorityDefaults("MYPREFIX_");
  188. }
  189. ----
  190. .Kotlin
  191. [source,kotlin,role="secondary"]
  192. ----
  193. companion object {
  194. @Bean
  195. fun grantedAuthorityDefaults() : GrantedAuthorityDefaults {
  196. return GrantedAuthorityDefaults("MYPREFIX_");
  197. }
  198. }
  199. ----
  200. .Xml
  201. [source,xml,role="secondary"]
  202. ----
  203. <sec:method-security/>
  204. <bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
  205. <constructor-arg value="MYPREFIX_"/>
  206. </bean>
  207. ----
  208. ====
  209. [TIP]
  210. ====
  211. We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes
  212. ====
  213. [[jc-method-security-custom-authorization-manager]]
  214. === Custom Authorization Managers
  215. Method authorization is a combination of before- and after-method authorization.
  216. [NOTE]
  217. ====
  218. Before-method authorization is performed before the method is invoked.
  219. If that authorization denies access, the method is not invoked, and an `AccessDeniedException` is thrown.
  220. After-method authorization is performed after the method is invoked, but before the method returns to the caller.
  221. If that authorization denies access, the value is not returned, and an `AccessDeniedException` is thrown
  222. ====
  223. To recreate what adding `@EnableMethodSecurity` does by default, you would publish the following configuration:
  224. .Full Pre-post Method Security Configuration
  225. ====
  226. .Java
  227. [source,java,role="primary"]
  228. ----
  229. @Configuration
  230. @EnableMethodSecurity(prePostEnabled = false)
  231. class MethodSecurityConfig {
  232. @Bean
  233. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  234. Advisor preFilterAuthorizationMethodInterceptor() {
  235. return new PreFilterAuthorizationMethodInterceptor();
  236. }
  237. @Bean
  238. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  239. Advisor preAuthorizeAuthorizationMethodInterceptor() {
  240. return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
  241. }
  242. @Bean
  243. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  244. Advisor postAuthorizeAuthorizationMethodInterceptor() {
  245. return AuthorizationManagerAfterMethodInterceptor.postAuthorize();
  246. }
  247. @Bean
  248. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  249. Advisor postFilterAuthorizationMethodInterceptor() {
  250. return new PostFilterAuthorizationMethodInterceptor();
  251. }
  252. }
  253. ----
  254. .Kotlin
  255. [source,kotlin,role="secondary"]
  256. ----
  257. @Configuration
  258. @EnableMethodSecurity(prePostEnabled = false)
  259. class MethodSecurityConfig {
  260. @Bean
  261. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  262. fun preFilterAuthorizationMethodInterceptor() : Advisor {
  263. return PreFilterAuthorizationMethodInterceptor();
  264. }
  265. @Bean
  266. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  267. fun preAuthorizeAuthorizationMethodInterceptor() : Advisor {
  268. return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
  269. }
  270. @Bean
  271. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  272. fun postAuthorizeAuthorizationMethodInterceptor() : Advisor {
  273. return AuthorizationManagerAfterMethodInterceptor.postAuthorize();
  274. }
  275. @Bean
  276. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  277. fun postFilterAuthorizationMethodInterceptor() : Advisor {
  278. return PostFilterAuthorizationMethodInterceptor();
  279. }
  280. }
  281. ----
  282. .Xml
  283. [source,xml,role="secondary"]
  284. ----
  285. <sec:method-security pre-post-enabled="false"/>
  286. <aop:config/>
  287. <bean id="preFilterAuthorizationMethodInterceptor"
  288. class="org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor"/>
  289. <bean id="preAuthorizeAuthorizationMethodInterceptor"
  290. class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor"
  291. factory-method="preAuthorize"/>
  292. <bean id="postAuthorizeAuthorizationMethodInterceptor"
  293. class="org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor"
  294. factory-method="postAuthorize"/>
  295. <bean id="postFilterAuthorizationMethodInterceptor"
  296. class="org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor"/>
  297. ----
  298. ====
  299. Notice that Spring Security's method security is built using Spring AOP.
  300. So, interceptors are invoked based on the order specified.
  301. This can be customized by calling `setOrder` on the interceptor instances like so:
  302. .Publish Custom Advisor
  303. ====
  304. .Java
  305. [source,java,role="primary"]
  306. ----
  307. @Bean
  308. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  309. Advisor postFilterAuthorizationMethodInterceptor() {
  310. PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationMethodInterceptor();
  311. interceptor.setOrder(AuthorizationInterceptorOrders.POST_AUTHORIZE.getOrder() - 1);
  312. return interceptor;
  313. }
  314. ----
  315. .Kotlin
  316. [source,kotlin,role="secondary"]
  317. ----
  318. @Bean
  319. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  320. fun postFilterAuthorizationMethodInterceptor() : Advisor {
  321. val interceptor = PostFilterAuthorizationMethodInterceptor();
  322. interceptor.setOrder(AuthorizationInterceptorOrders.POST_AUTHORIZE.getOrder() - 1);
  323. return interceptor;
  324. }
  325. ----
  326. .Xml
  327. [source,xml,role="secondary"]
  328. ----
  329. <bean id="postFilterAuthorizationMethodInterceptor"
  330. class="org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor">
  331. <property name="order"
  332. value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).POST_AUTHORIZE.getOrder() -1}"/>
  333. </bean>
  334. ----
  335. ====
  336. You may want to only support `@PreAuthorize` in your application, in which case you can do the following:
  337. .Only @PreAuthorize Configuration
  338. ====
  339. .Java
  340. [source,java,role="primary"]
  341. ----
  342. @Configuration
  343. @EnableMethodSecurity(prePostEnabled = false)
  344. class MethodSecurityConfig {
  345. @Bean
  346. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  347. Advisor preAuthorize() {
  348. return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
  349. }
  350. }
  351. ----
  352. .Kotlin
  353. [source,kotlin,role="secondary"]
  354. ----
  355. @Configuration
  356. @EnableMethodSecurity(prePostEnabled = false)
  357. class MethodSecurityConfig {
  358. @Bean
  359. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  360. fun preAuthorize() : Advisor {
  361. return AuthorizationManagerBeforeMethodInterceptor.preAuthorize()
  362. }
  363. }
  364. ----
  365. .Xml
  366. [source,xml,role="secondary"]
  367. ----
  368. <sec:method-security pre-post-enabled="false"/>
  369. <aop:config/>
  370. <bean id="preAuthorizeAuthorizationMethodInterceptor"
  371. class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor"
  372. factory-method="preAuthorize"/>
  373. ----
  374. ====
  375. Or, you may have a custom before-method `AuthorizationManager` that you want to add to the list.
  376. In this case, you will need to tell Spring Security both the `AuthorizationManager` and to which methods and classes your authorization manager applies.
  377. Thus, you can configure Spring Security to invoke your `AuthorizationManager` in between `@PreAuthorize` and `@PostAuthorize` like so:
  378. .Custom Before Advisor
  379. ====
  380. .Java
  381. [source,java,role="primary"]
  382. ----
  383. @Configuration
  384. @EnableMethodSecurity
  385. class MethodSecurityConfig {
  386. @Bean
  387. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  388. public Advisor customAuthorize() {
  389. JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
  390. pattern.setPattern("org.mycompany.myapp.service.*");
  391. AuthorizationManager<MethodInvocation> rule = AuthorityAuthorizationManager.isAuthenticated();
  392. AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(pattern, rule);
  393. interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
  394. return interceptor;
  395. }
  396. }
  397. ----
  398. .Kotlin
  399. [source,kotlin,role="secondary"]
  400. ----
  401. @Configuration
  402. @EnableMethodSecurity
  403. class MethodSecurityConfig {
  404. @Bean
  405. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  406. fun customAuthorize() : Advisor {
  407. val pattern = JdkRegexpMethodPointcut();
  408. pattern.setPattern("org.mycompany.myapp.service.*");
  409. val rule = AuthorityAuthorizationManager.isAuthenticated();
  410. val interceptor = AuthorizationManagerBeforeMethodInterceptor(pattern, rule);
  411. interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
  412. return interceptor;
  413. }
  414. }
  415. ----
  416. .Xml
  417. [source,xml,role="secondary"]
  418. ----
  419. <sec:method-security/>
  420. <aop:config/>
  421. <bean id="customAuthorize"
  422. class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor">
  423. <constructor-arg>
  424. <bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
  425. <property name="pattern" value="org.mycompany.myapp.service.*"/>
  426. </bean>
  427. </constructor-arg>
  428. <constructor-arg>
  429. <bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
  430. factory-method="isAuthenticated"/>
  431. </constructor-arg>
  432. <property name="order"
  433. value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1}"/>
  434. </bean>
  435. ----
  436. ====
  437. [TIP]
  438. ====
  439. You can place your interceptor in between Spring Security method interceptors using the order constants specified in `AuthorizationInterceptorsOrder`.
  440. ====
  441. The same can be done for after-method authorization.
  442. After-method authorization is generally concerned with analysing the return value to verify access.
  443. For example, you might have a method that confirms that the account requested actually belongs to the logged-in user like so:
  444. .@PostAuthorize example
  445. ====
  446. .Java
  447. [source,java,role="primary"]
  448. ----
  449. public interface BankService {
  450. @PreAuthorize("hasRole('USER')")
  451. @PostAuthorize("returnObject.owner == authentication.name")
  452. Account readAccount(Long id);
  453. }
  454. ----
  455. .Kotlin
  456. [source,kotlin,role="secondary"]
  457. ----
  458. interface BankService {
  459. @PreAuthorize("hasRole('USER')")
  460. @PostAuthorize("returnObject.owner == authentication.name")
  461. fun readAccount(id : Long) : Account
  462. }
  463. ----
  464. ====
  465. You can supply your own `AuthorizationMethodInterceptor` to customize how access to the return value is evaluated.
  466. For example, if you have your own custom annotation, you can configure it like so:
  467. .Custom After Advisor
  468. ====
  469. .Java
  470. [source,java,role="primary"]
  471. ----
  472. @Configuration
  473. @EnableMethodSecurity
  474. class MethodSecurityConfig {
  475. @Bean
  476. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  477. public Advisor customAuthorize(AuthorizationManager<MethodInvocationResult> rules) {
  478. AnnotationMatchingPointcut pattern = new AnnotationMatchingPointcut(MySecurityAnnotation.class);
  479. AuthorizationManagerAfterMethodInterceptor interceptor = new AuthorizationManagerAfterMethodInterceptor(pattern, rules);
  480. interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
  481. return interceptor;
  482. }
  483. }
  484. ----
  485. .Kotlin
  486. [source,kotlin,role="secondary"]
  487. ----
  488. @Configuration
  489. @EnableMethodSecurity
  490. class MethodSecurityConfig {
  491. @Bean
  492. @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  493. fun customAuthorize(rules : AuthorizationManager<MethodInvocationResult>) : Advisor {
  494. val pattern = AnnotationMatchingPointcut(MySecurityAnnotation::class.java);
  495. val interceptor = AuthorizationManagerAfterMethodInterceptor(pattern, rules);
  496. interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
  497. return interceptor;
  498. }
  499. }
  500. ----
  501. .Xml
  502. [source,xml,role="secondary"]
  503. ----
  504. <sec:method-security/>
  505. <aop:config/>
  506. <bean id="customAuthorize"
  507. class="org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor">
  508. <constructor-arg>
  509. <bean class="org.springframework.aop.support.annotation.AnnotationMethodMatcher">
  510. <constructor-arg value="#{T(org.mycompany.MySecurityAnnotation)}"/>
  511. </bean>
  512. </constructor-arg>
  513. <constructor-arg>
  514. <bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
  515. factory-method="isAuthenticated"/>
  516. </constructor-arg>
  517. <property name="order"
  518. value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1}"/>
  519. </bean>
  520. ----
  521. ====
  522. and it will be invoked after the `@PostAuthorize` interceptor.
  523. [[jc-enable-global-method-security]]
  524. == EnableGlobalMethodSecurity
  525. We can enable annotation-based security by using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance.
  526. The following example enables Spring Security's `@Secured` annotation:
  527. ====
  528. .Java
  529. [source,java,role="primary"]
  530. ----
  531. @Configuration
  532. @EnableGlobalMethodSecurity(securedEnabled = true)
  533. public class MethodSecurityConfig {
  534. // ...
  535. }
  536. ----
  537. .Kotlin
  538. [source,kotlin,role="secondary"]
  539. ----
  540. @Configuration
  541. @EnableGlobalMethodSecurity(securedEnabled = true)
  542. open class MethodSecurityConfig {
  543. // ...
  544. }
  545. ----
  546. ====
  547. Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
  548. Spring Security's native annotation support defines a set of attributes for the method.
  549. These are passed to the `AccessDecisionManager` for it to make the actual decision:
  550. ====
  551. .Java
  552. [source,java,role="primary"]
  553. ----
  554. public interface BankService {
  555. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  556. public Account readAccount(Long id);
  557. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  558. public Account[] findAccounts();
  559. @Secured("ROLE_TELLER")
  560. public Account post(Account account, double amount);
  561. }
  562. ----
  563. .Kotlin
  564. [source,kotlin,role="secondary"]
  565. ----
  566. interface BankService {
  567. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  568. fun readAccount(id: Long): Account
  569. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  570. fun findAccounts(): Array<Account>
  571. @Secured("ROLE_TELLER")
  572. fun post(account: Account, amount: Double): Account
  573. }
  574. ----
  575. ====
  576. Support for JSR-250 annotations can be enabled by using:
  577. ====
  578. .Java
  579. [source,java,role="primary"]
  580. ----
  581. @Configuration
  582. @EnableGlobalMethodSecurity(jsr250Enabled = true)
  583. public class MethodSecurityConfig {
  584. // ...
  585. }
  586. ----
  587. .Kotlin
  588. [source,kotlin,role="secondary"]
  589. ----
  590. @Configuration
  591. @EnableGlobalMethodSecurity(jsr250Enabled = true)
  592. open class MethodSecurityConfig {
  593. // ...
  594. }
  595. ----
  596. ====
  597. These are standards-based and let simple role-based constraints be applied but do not have the power Spring Security's native annotations.
  598. To use the new expression-based syntax, you would use:
  599. ====
  600. .Java
  601. [source,java,role="primary"]
  602. ----
  603. @Configuration
  604. @EnableGlobalMethodSecurity(prePostEnabled = true)
  605. public class MethodSecurityConfig {
  606. // ...
  607. }
  608. ----
  609. .Kotlin
  610. [source,kotlin,role="secondary"]
  611. ----
  612. @Configuration
  613. @EnableGlobalMethodSecurity(prePostEnabled = true)
  614. open class MethodSecurityConfig {
  615. // ...
  616. }
  617. ----
  618. ====
  619. The equivalent Java code is:
  620. ====
  621. .Java
  622. [source,java,role="primary"]
  623. ----
  624. public interface BankService {
  625. @PreAuthorize("isAnonymous()")
  626. public Account readAccount(Long id);
  627. @PreAuthorize("isAnonymous()")
  628. public Account[] findAccounts();
  629. @PreAuthorize("hasAuthority('ROLE_TELLER')")
  630. public Account post(Account account, double amount);
  631. }
  632. ----
  633. .Kotlin
  634. [source,kotlin,role="secondary"]
  635. ----
  636. interface BankService {
  637. @PreAuthorize("isAnonymous()")
  638. fun readAccount(id: Long): Account
  639. @PreAuthorize("isAnonymous()")
  640. fun findAccounts(): Array<Account>
  641. @PreAuthorize("hasAuthority('ROLE_TELLER')")
  642. fun post(account: Account, amount: Double): Account
  643. }
  644. ----
  645. ====
  646. == GlobalMethodSecurityConfiguration
  647. Sometimes, you may need to perform operations that are more complicated than are possible with the `@EnableGlobalMethodSecurity` annotation.
  648. For these instances, you can extend the `GlobalMethodSecurityConfiguration`, ensuring that the `@EnableGlobalMethodSecurity` annotation is present on your subclass.
  649. For example, if you wanted to provide a custom `MethodSecurityExpressionHandler`, you could use the following configuration:
  650. ====
  651. .Java
  652. [source,java,role="primary"]
  653. ----
  654. @Configuration
  655. @EnableGlobalMethodSecurity(prePostEnabled = true)
  656. public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
  657. @Override
  658. protected MethodSecurityExpressionHandler createExpressionHandler() {
  659. // ... create and return custom MethodSecurityExpressionHandler ...
  660. return expressionHandler;
  661. }
  662. }
  663. ----
  664. .Kotlin
  665. [source,kotlin,role="secondary"]
  666. ----
  667. @Configuration
  668. @EnableGlobalMethodSecurity(prePostEnabled = true)
  669. open class MethodSecurityConfig : GlobalMethodSecurityConfiguration() {
  670. override fun createExpressionHandler(): MethodSecurityExpressionHandler {
  671. // ... create and return custom MethodSecurityExpressionHandler ...
  672. return expressionHandler
  673. }
  674. }
  675. ----
  676. ====
  677. For additional information about methods that can be overridden, see the Javadoc for the {security-api-url}org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.html[`GlobalMethodSecurityConfiguration`] class.
  678. [[ns-global-method]]
  679. == The <global-method-security> Element
  680. This element is used to enable annotation-based security in your application (by setting the appropriate attributes on the element) and to group together security pointcut declarations that are applied across your entire application context.
  681. You should only declare one `<global-method-security>` element.
  682. The following declaration enables support for Spring Security's `@Secured`:
  683. ====
  684. [source,xml]
  685. ----
  686. <global-method-security secured-annotations="enabled" />
  687. ----
  688. ====
  689. Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
  690. Spring Security's native annotation support defines a set of attributes for the method.
  691. These are passed to the `AccessDecisionManager` for it to make the actual decision.
  692. The following example shows the `@Secured` annotation in a typical interface:
  693. ====
  694. .Java
  695. [source,java,role="primary"]
  696. ----
  697. public interface BankService {
  698. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  699. public Account readAccount(Long id);
  700. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  701. public Account[] findAccounts();
  702. @Secured("ROLE_TELLER")
  703. public Account post(Account account, double amount);
  704. }
  705. ----
  706. .Kotlin
  707. [source,kotlin,role="secondary"]
  708. ----
  709. interface BankService {
  710. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  711. fun readAccount(id: Long): Account
  712. @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
  713. fun findAccounts(): Array<Account>
  714. @Secured("ROLE_TELLER")
  715. fun post(account: Account, amount: Double): Account
  716. }
  717. ----
  718. ====
  719. Support for JSR-250 annotations can be enabled by using:
  720. ====
  721. [source,xml]
  722. ----
  723. <global-method-security jsr250-annotations="enabled" />
  724. ----
  725. ====
  726. These are standards-based and allow simple role-based constraints to be applied, but they do not have the power Spring Security's native annotations.
  727. To use the expression-based syntax, use:
  728. ====
  729. [source,xml]
  730. ----
  731. <global-method-security pre-post-annotations="enabled" />
  732. ----
  733. ====
  734. The equivalent Java code is:
  735. ====
  736. .Java
  737. [source,java,role="primary"]
  738. ----
  739. public interface BankService {
  740. @PreAuthorize("isAnonymous()")
  741. public Account readAccount(Long id);
  742. @PreAuthorize("isAnonymous()")
  743. public Account[] findAccounts();
  744. @PreAuthorize("hasAuthority('ROLE_TELLER')")
  745. public Account post(Account account, double amount);
  746. }
  747. ----
  748. .Kotlin
  749. [source,kotlin,role="secondary"]
  750. ----
  751. interface BankService {
  752. @PreAuthorize("isAnonymous()")
  753. fun readAccount(id: Long): Account
  754. @PreAuthorize("isAnonymous()")
  755. fun findAccounts(): Array<Account>
  756. @PreAuthorize("hasAuthority('ROLE_TELLER')")
  757. fun post(account: Account, amount: Double): Account
  758. }
  759. ----
  760. ====
  761. Expression-based annotations are a good choice if you need to define simple rules that go beyond checking the role names against the user's list of authorities.
  762. [NOTE]
  763. ====
  764. The annotated methods will only be secured for instances which are defined as Spring beans (in the same application context in which method-security is enabled).
  765. If you want to secure instances which are not created by Spring (using the `new` operator, for example) then you need to use AspectJ.
  766. ====
  767. [NOTE]
  768. ====
  769. You can enable more than one type of annotation in the same application, but only one type should be used for any interface or class as the behaviour will not be well-defined otherwise.
  770. If two annotations are found which apply to a particular method, then only one of them will be applied.
  771. ====
  772. [[ns-protect-pointcut]]
  773. == Adding Security Pointcuts by using protect-pointcut
  774. `protect-pointcut` is particularly powerful, as it lets you apply security to many beans with only a simple declaration.
  775. Consider the following example:
  776. ====
  777. [source,xml]
  778. ----
  779. <global-method-security>
  780. <protect-pointcut expression="execution(* com.mycompany.*Service.*(..))"
  781. access="ROLE_USER"/>
  782. </global-method-security>
  783. ----
  784. ====
  785. d.
  786. This configuration protects all methods on beans declared in the application context whose classes are in the `com.mycompany` package and whose class names end in `Service`.
  787. Only users with the `ROLE_USER` role can invoke these methods.
  788. As with URL matching, the most specific matches must come first in the list of pointcuts, as the first matching expression is used.
  789. Security annotations take precedence over pointcuts.