|
2 | 2 | = What's New in Spring Security 6.3 |
3 | 3 |
|
4 | 4 | Spring Security 6.3 provides a number of new features. |
5 | | -Below are the highlights of the release. |
| 5 | +Below are the highlights of the release, or you can view https://github.com/spring-projects/spring-security/releases[the release notes] for a detailed listing of each feature and bug fix. |
6 | 6 |
|
7 | | -== General |
| 7 | +== Passive JDK Serialization Support |
8 | 8 |
|
9 | | -- https://spring.io/blog/2024/01/19/spring-security-6-3-adds-passive-jdk-serialization-deserialization-for[blog post] - Added Passive JDK Serialization/Deserialization for Seamless Upgrades |
| 9 | +When it comes to its support for JDK-serialized security components, Spring Security has historically been quite aggressive, supporting each serialization version for only one Spring Security minor version. |
| 10 | +This meant that if you had JDK-serialized security components, then they would need to be evacuated before upgrading to the next Spring Security version since they would no longer be deserializable. |
10 | 11 |
|
11 | | -== Authentication |
12 | | - |
13 | | -- https://github.com/spring-projects/spring-security/issues/7395[gh-7395] - xref:features/authentication/password-storage.adoc#authentication-compromised-password-check[docs] - Add Compromised Password Checker |
| 12 | +Now that Spring Security performs a minor release every six months, this became a much larger pain point. |
| 13 | +To address that, Spring Security now will https://spring.io/blog/2024/01/19/spring-security-6-3-adds-passive-jdk-serialization-deserialization-for[maintain passivity with JDK serialization], like it does with JSON serialization, making for more seamless upgrades. |
14 | 14 |
|
15 | 15 | == Authorization |
16 | 16 |
|
17 | | -- https://github.com/spring-projects/spring-security/issues/14596[gh-14596] - xref:servlet/authorization/method-security.adoc[docs] - Add Programmatic Proxy Support for Method Security |
18 | | -- https://github.com/spring-projects/spring-security/issues/14597[gh-14597] - xref:servlet/authorization/method-security.adoc[docs] - Add Securing of Return Values |
19 | | -- https://github.com/spring-projects/spring-security/issues/14601[gh-14601] - xref:servlet/authorization/method-security.adoc#fallback-values-authorization-denied[docs] - Add Authorization Denied Handlers for Method Security |
| 17 | +An ongoing theme for the last several releases has been to refactor and improve Spring Security's authorization subsystem. |
| 18 | +Starting with replacing the `AccessDecisionManager` API with `AuthorizationManager` it's now come to the point where we are able to add several exciting new features. |
20 | 19 |
|
21 | | -== Configuration |
| 20 | +=== Annotation Parameters - https://github.com/spring-projects/spring-security/issues/14480[#14480] |
22 | 21 |
|
23 | | -- https://github.com/spring-projects/spring-security/issues/6192[gh-6192] - xref:reactive/authentication/concurrent-sessions-control.adoc[(docs)] - Add Concurrent Sessions Control on WebFlux |
| 22 | +The first 6.3 feature is https://github.com/spring-projects/spring-security/issues/14480[support for annotation parameters]. |
| 23 | +Consider Spring Security's support for xref:servlet/authorization/method-security.adoc#meta-annotations[meta-annotations] like this one: |
24 | 24 |
|
25 | | -== CAS |
| 25 | +[tabs] |
| 26 | +====== |
| 27 | +Java:: |
| 28 | ++ |
| 29 | +[source,java,role="primary"] |
| 30 | +---- |
| 31 | +@Retention(RetentionPolicy.RUNTIME) |
| 32 | +@Target(ElementType.METHOD) |
| 33 | +@PreAuthorize("hasAuthority('SCOPE_message:read')") |
| 34 | +public @interface HasMessageRead {} |
| 35 | +---- |
26 | 36 |
|
27 | | -- https://github.com/spring-projects/spring-security/pull/14193[gh-14193] - Added support for CAS Gateway Authentication |
| 37 | +Kotlin:: |
| 38 | ++ |
| 39 | +.Kotlin |
| 40 | +[source,kotlin,role="secondary"] |
| 41 | +---- |
| 42 | +@Retention(RetentionPolicy.RUNTIME) |
| 43 | +@Target(ElementType.METHOD) |
| 44 | +@PreAuthorize("hasAuthority('SCOPE_message:read')") |
| 45 | +annotation class HasMessageRead |
| 46 | +---- |
| 47 | +====== |
| 48 | + |
| 49 | +Before this release, something like this is only helpful when it is used widely across the codebase. |
| 50 | +But now, xref:servlet/authorization/method-security.adoc#_templating_meta_annotation_expressions[you can add parameters] like so: |
| 51 | + |
| 52 | +[tabs] |
| 53 | +====== |
| 54 | +Java:: |
| 55 | ++ |
| 56 | +[source,java,role="primary"] |
| 57 | +---- |
| 58 | +@Retention(RetentionPolicy.RUNTIME) |
| 59 | +@Target(ElementType.METHOD) |
| 60 | +@PreAuthorize("hasAuthority('SCOPE_{scope}')") |
| 61 | +public @interface HasScope { |
| 62 | + String scope(); |
| 63 | +} |
| 64 | +---- |
| 65 | +
|
| 66 | +Kotlin:: |
| 67 | ++ |
| 68 | +[source,kotlin,role="secondary"] |
| 69 | +---- |
| 70 | +@Retention(RetentionPolicy.RUNTIME) |
| 71 | +@Target(ElementType.METHOD) |
| 72 | +@PreAuthorize("hasAuthority('SCOPE_{scope}')") |
| 73 | +annotation class HasScope (val scope:String) |
| 74 | +---- |
| 75 | +====== |
| 76 | + |
| 77 | +making it possible to do things like this: |
| 78 | + |
| 79 | +[tabs] |
| 80 | +====== |
| 81 | +Java:: |
| 82 | ++ |
| 83 | +[source,java,role="primary"] |
| 84 | +---- |
| 85 | +@HasScope("message:read") |
| 86 | +public String method() { ... } |
| 87 | +---- |
| 88 | +
|
| 89 | +Kotlin:: |
| 90 | ++ |
| 91 | +[source,kotlin,role="secondary"] |
| 92 | +---- |
| 93 | +@HasScope("message:read") |
| 94 | +fun method(): String { ... } |
| 95 | +---- |
| 96 | +====== |
| 97 | + |
| 98 | +and apply your SpEL expression in several more places. |
| 99 | + |
| 100 | +=== Secure Return Values - https://github.com/spring-projects/spring-security/issues/14596[#14596], https://github.com/spring-projects/spring-security/issues/14597[#14597] |
| 101 | + |
| 102 | +Since the early days of Spring Security, you've been able to xref:servlet/authorization/method-security.adoc#use-preauthorize[annotate Spring beans with `@PreAuthorize` and `@PostAuthorize`]. |
| 103 | +But controllers, services, and repositories are not the only things you care to secure. |
| 104 | +For example, what about a domain object `Order` where only admins should be able to call the `Order#getPayment` method? |
| 105 | + |
| 106 | +Now in 6.3, https://github.com/spring-projects/spring-security/issues/14597[you can annotate those methods], too. |
| 107 | +First, annotate the `getPayment` method like you would a Spring bean: |
| 108 | + |
| 109 | +[tabs] |
| 110 | +====== |
| 111 | +Java:: |
| 112 | ++ |
| 113 | +[source,java,role="primary"] |
| 114 | +---- |
| 115 | +public class Order { |
| 116 | +
|
| 117 | + @HasScope("payment:read") |
| 118 | + Payment getPayment() { ... } |
| 119 | +
|
| 120 | +} |
| 121 | +---- |
| 122 | +
|
| 123 | +Kotlin:: |
| 124 | ++ |
| 125 | +[source,kotlin,role="secondary"] |
| 126 | +---- |
| 127 | +class Order { |
| 128 | +
|
| 129 | + @HasScope("payment:read") |
| 130 | + fun getPayment(): Payment { ... } |
| 131 | +
|
| 132 | +} |
| 133 | +---- |
| 134 | +====== |
| 135 | + |
| 136 | +And then xref:servlet/authorization/method-security.adoc#authorize-object[annotate your Spring Data repository with `@AuthorizeReturnObject`] like so: |
| 137 | + |
| 138 | +[tabs] |
| 139 | +====== |
| 140 | +Java:: |
| 141 | ++ |
| 142 | +[source,java,role="primary"] |
| 143 | +---- |
| 144 | +public interface OrderRepository implements CrudRepository<Order, String> { |
| 145 | +
|
| 146 | + @AuthorizeReturnObject |
| 147 | + Optional<Order> findOrderById(String id); |
| 148 | +
|
| 149 | +} |
| 150 | +---- |
| 151 | +
|
| 152 | +Kotlin:: |
| 153 | ++ |
| 154 | +[source,kotlin,role="secondary"] |
| 155 | +---- |
| 156 | +
|
| 157 | +interface OrderRepository : CrudRepository<Order, String> { |
| 158 | + @AuthorizeReturnObject |
| 159 | + fun findOrderById(id: String?): Optional<Order?>? |
| 160 | +} |
| 161 | +---- |
| 162 | +====== |
| 163 | + |
| 164 | +At that point, Spring Security will protect any `Order` returned from `findOrderById` by way of https://github.com/spring-projects/spring-security/issues/14596[proxying the `Order` instance]. |
28 | 165 |
|
29 | | -== Crypto |
| 166 | +=== Error Handling - https://github.com/spring-projects/spring-security/issues/14598[#14598], https://github.com/spring-projects/spring-security/issues/14600[#14600], https://github.com/spring-projects/spring-security/issues/14601[#14601] |
30 | 167 |
|
31 | | -- https://github.com/spring-projects/spring-security/issues/14202[gh-14202] - Migrated spring-security-rsa into spring-security-crypto |
| 168 | +In this release, you can also https://github.com/spring-projects/spring-security/issues/14601[intercept and handle failure at the method level] with its last new method security annotation. |
32 | 169 |
|
33 | | -== OAuth2 |
| 170 | +When you xref:servlet/authorization/method-security.adoc#fallback-values-authorization-denied[annotate a method with `@HandleAuthorizationDenied`] like so: |
34 | 171 |
|
| 172 | +[tabs] |
| 173 | +====== |
| 174 | +Java:: |
| 175 | ++ |
| 176 | +[source,java,role="primary"] |
| 177 | +---- |
| 178 | +public class Payment { |
| 179 | + @HandleAuthorizationDenied(handlerClass=Mask.class) |
| 180 | + @PreAuthorize("hasAuthority('card:read')") |
| 181 | + public String getCreditCardNumber() { ... } |
| 182 | +} |
| 183 | +---- |
| 184 | +
|
| 185 | +Kotlin:: |
| 186 | ++ |
| 187 | +[source,kotlin,role="secondary"] |
| 188 | +---- |
| 189 | +class Payment { |
| 190 | + @HandleAuthorizationDenied(handlerClass=Mask.class) |
| 191 | + @PreAuthorize("hasAuthority('card:read')") |
| 192 | + fun getCreditCardNumber(): String { ... } |
| 193 | +} |
| 194 | +---- |
| 195 | +====== |
| 196 | + |
| 197 | +and publish a `Mask` bean: |
| 198 | + |
| 199 | +[tabs] |
| 200 | +====== |
| 201 | +Java:: |
| 202 | ++ |
| 203 | +[source,java,role="primary"] |
| 204 | +---- |
| 205 | +@Component |
| 206 | +public class Mask implements MethodAuthorizationDeniedHandler { |
| 207 | + @Override |
| 208 | + public Object handleDeniedInvocation(MethodInvocation invocation, AuthorizationResult result) { |
| 209 | + return "***"; |
| 210 | + } |
| 211 | +} |
| 212 | +---- |
| 213 | +
|
| 214 | +Kotlin:: |
| 215 | ++ |
| 216 | +[source,kotlin,role="secondary"] |
| 217 | +---- |
| 218 | +@Component |
| 219 | +class Mask : MethodAuthorizationDeniedHandler { |
| 220 | + fun handleDeniedInvocation(invocation: MethodInvocation?, result: AuthorizationResult?): Any = "***" |
| 221 | +} |
| 222 | +---- |
| 223 | +====== |
| 224 | + |
| 225 | +then any unauthorized call to `Payment#getCreditCardNumber` will return `\***` instead of the number. |
| 226 | + |
| 227 | +You can see all these features at work together in https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/data[the latest Spring Security Data sample]. |
| 228 | + |
| 229 | +== Compromised Password Checking - https://github.com/spring-projects/spring-security/issues/7395[#7395] |
| 230 | + |
| 231 | +If you are going to let users pick passwords, it's critical to ensure that such a password isn't already compromised. |
| 232 | +Spring Security 6.3 makes this as simple as xref:features/authentication/password-storage.adoc#authentication-compromised-password-check[publishing a `CompromisedPasswordChecker` bean]: |
| 233 | + |
| 234 | +[tabs] |
| 235 | +====== |
| 236 | +Java:: |
| 237 | ++ |
| 238 | +[source,java,role="primary"] |
| 239 | +---- |
| 240 | +@Bean |
| 241 | +public CompromisedPasswordChecker compromisedPasswordChecker() { |
| 242 | + return new HaveIBeenPwnedRestApiPasswordChecker(); |
| 243 | +} |
| 244 | +---- |
| 245 | +
|
| 246 | +Kotlin:: |
| 247 | ++ |
| 248 | +[source,kotlin,role="secondary"] |
| 249 | +---- |
| 250 | +@Bean |
| 251 | +fun compromisedPasswordChecker(): CompromisedPasswordChecker = HaveIBeenPwnedRestApiPasswordChecker() |
| 252 | +---- |
| 253 | +====== |
| 254 | + |
| 255 | +== `spring-security-rsa` is now part of Spring Security - https://github.com/spring-projects/spring-security/issues/14202[#14202] |
| 256 | + |
| 257 | +Since 2017, Spring Security has been undergoing a long-standing initiative to fold various Spring Security extensions into Spring Security proper. |
| 258 | +In 6.3, `spring-security-rsa` becomes the latest of these projects which will help the team maintain and add features to it, long-term. |
| 259 | + |
| 260 | +`spring-security-rsa` provides a number of https://github.com/spring-projects/spring-security/blob/main/crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptor.java[handy `BytesEncryptor`] https://github.com/spring-projects/spring-security/blob/main/crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaRawEncryptor.java[implementations] as well as https://github.com/spring-projects/spring-security/blob/main/crypto/src/main/java/org/springframework/security/crypto/encrypt/KeyStoreKeyFactory.java[a simpler API for working with ``KeyStore``s]. |
| 261 | + |
| 262 | + |
| 263 | +== OAuth 2.0 Token Exchange Grant - https://github.com/spring-projects/spring-security/issues/5199[#5199] |
| 264 | + |
| 265 | +One of https://github.com/spring-projects/spring-security/issues/5199[the most highly-voted OAuth 2.0 features] in Spring Security is now in place in 6.3, which is the support for https://datatracker.ietf.org/doc/html/rfc8693#section-2[the OAuth 2.0 Token Exchange grant]. |
| 266 | + |
| 267 | +For xref:servlet/oauth2/client/authorization-grants.adoc#token-exchange-grant-access-token[any client configured for token exchange], you can activate it in Spring Security by adding a `TokenExchangeAuthorizedClientProvider` instance to your `OAuth2AuthorizedClientManager` like so: |
| 268 | + |
| 269 | +[tabs] |
| 270 | +====== |
| 271 | +Java:: |
| 272 | ++ |
| 273 | +[source,java,role="primary"] |
| 274 | +---- |
| 275 | +@Bean |
| 276 | +public OAuth2AuthorizedClientProvider tokenExchange() { |
| 277 | + return new TokenExchangeOAuth2AuthorizedClientProvider(); |
| 278 | +} |
| 279 | +---- |
| 280 | +
|
| 281 | +Kotlin:: |
| 282 | ++ |
| 283 | +[source,kotlin,role="secondary"] |
| 284 | +---- |
| 285 | +@Bean |
| 286 | +fun tokenExchange(): OAuth2AuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider() |
| 287 | +---- |
| 288 | +====== |
| 289 | + |
| 290 | +and then xref:servlet/oauth2/client/authorized-clients.adoc#oauth2Client-registered-authorized-client[use the `@RegisteredOAuth2AuthorizedClient` annotation] as per usual to retrieve the appropriate token with the expanded privileges your resource server needs. |
| 291 | + |
| 292 | +== Additional Highlights |
| 293 | + |
| 294 | +- https://github.com/spring-projects/spring-security/pull/14655[gh-14655] - Add `DelegatingAuthenticationConverter |
| 295 | +- Add Concurrent Sessions Control on WebFlux - https://github.com/spring-projects/spring-security/issues/6192[gh-6192] - xref:reactive/authentication/concurrent-sessions-control.adoc[(docs)] |
| 296 | +- https://github.com/spring-projects/spring-security/pull/14193[gh-14193] - Added support for CAS Gateway Authentication |
35 | 297 | - https://github.com/spring-projects/spring-security/issues/13259[gh-13259] - Customize when UserInfo is called |
36 | 298 | - https://github.com/spring-projects/spring-security/pull/14168[gh-14168] - Introduce Customizable AuthorizationFailureHandler in OAuth2AuthorizationRequestRedirectFilter |
37 | | -- https://github.com/spring-projects/spring-security/issues/5199[gh-5199], https://github.com/spring-projects/spring-security/issues/14701[gh-14701] - Add support for OAuth 2.0 Token Exchange Grant |
38 | 299 | - https://github.com/spring-projects/spring-security/issues/14672[gh-14672] - Customize mapping the OidcUser from OidcUserRequest and OidcUserInfo |
| 300 | +- https://github.com/spring-projects/spring-security/issues/10538[gh-10538] - Support Certificate-Bound JWT Access Token Validation |
| 301 | +- https://github.com/spring-projects/spring-security/pull/14265[gh-14265] - Support Nexted username in UserInfo response |
| 302 | +- https://github.com/spring-projects/spring-security/pull/14265[gh-14449] - Add `SecurityContext` argument resolver |
39 | 303 |
|
40 | | -== Documentation |
41 | | - |
42 | | -- https://github.com/spring-projects/spring-security/pull/14263[gh-14263] - xref:servlet/authentication/passwords/caching.adoc[(docs)] - Add documentation for CachingUserDetailsService |
| 304 | +And for an exhaustive list, please see the release notes for https://github.com/spring-projects/spring-security/releases/tag/6.3.0-RC1[6.3.0-RC1], https://github.com/spring-projects/spring-security/releases/tag/6.3.0-M3[6.3.0-M3], https://github.com/spring-projects/spring-security/releases/tag/6.3.0-M2[6.3.0-M2], and https://github.com/spring-projects/spring-security/releases/tag/6.3.0-M1[6.3.0-M1]. |
| 305 | +` |
0 commit comments