mockmvc.adoc 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953
  1. [[test-mockmvc]]
  2. == Spring MVC Test Integration
  3. Spring Security provides comprehensive integration with https://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test-framework[Spring MVC Test]
  4. [[test-mockmvc-setup]]
  5. === Setting Up MockMvc and Spring Security
  6. In order to use Spring Security with Spring MVC Test it is necessary to add the Spring Security `FilterChainProxy` as a `Filter`.
  7. It is also necessary to add Spring Security's `TestSecurityContextHolderPostProcessor` to support <<Running as a User in Spring MVC Test with Annotations,Running as a User in Spring MVC Test with Annotations>>.
  8. This can be done using Spring Security's `SecurityMockMvcConfigurers.springSecurity()`.
  9. For example:
  10. NOTE: Spring Security's testing support requires spring-test-4.1.3.RELEASE or greater.
  11. ====
  12. .Java
  13. [source,java,role="primary"]
  14. ----
  15. import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
  16. @RunWith(SpringJUnit4ClassRunner.class)
  17. @ContextConfiguration(classes = SecurityConfig.class)
  18. @WebAppConfiguration
  19. public class CsrfShowcaseTests {
  20. @Autowired
  21. private WebApplicationContext context;
  22. private MockMvc mvc;
  23. @Before
  24. public void setup() {
  25. mvc = MockMvcBuilders
  26. .webAppContextSetup(context)
  27. .apply(springSecurity()) // <1>
  28. .build();
  29. }
  30. ...
  31. ----
  32. .Kotlin
  33. [source,kotlin,role="secondary"]
  34. ----
  35. @RunWith(SpringJUnit4ClassRunner::class)
  36. @ContextConfiguration(classes = [SecurityConfig::class])
  37. @WebAppConfiguration
  38. class CsrfShowcaseTests {
  39. @Autowired
  40. private lateinit var context: WebApplicationContext
  41. private var mvc: MockMvc? = null
  42. @Before
  43. fun setup() {
  44. mvc = MockMvcBuilders
  45. .webAppContextSetup(context)
  46. .apply<DefaultMockMvcBuilder>(springSecurity()) // <1>
  47. .build()
  48. }
  49. // ...
  50. ----
  51. ====
  52. <1> `SecurityMockMvcConfigurers.springSecurity()` will perform all of the initial setup we need to integrate Spring Security with Spring MVC Test
  53. [[test-mockmvc-smmrpp]]
  54. === SecurityMockMvcRequestPostProcessors
  55. Spring MVC Test provides a convenient interface called a `RequestPostProcessor` that can be used to modify a request.
  56. Spring Security provides a number of `RequestPostProcessor` implementations that make testing easier.
  57. In order to use Spring Security's `RequestPostProcessor` implementations ensure the following static import is used:
  58. ====
  59. .Java
  60. [source,java,role="primary"]
  61. ----
  62. import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
  63. ----
  64. .Kotlin
  65. [source,kotlin,role="secondary"]
  66. ----
  67. import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*
  68. ----
  69. ====
  70. [[test-mockmvc-csrf]]
  71. ==== Testing with CSRF Protection
  72. When testing any non-safe HTTP methods and using Spring Security's CSRF protection, you must be sure to include a valid CSRF Token in the request.
  73. To specify a valid CSRF token as a request parameter using the following:
  74. ====
  75. .Java
  76. [source,java,role="primary"]
  77. ----
  78. mvc
  79. .perform(post("/").with(csrf()))
  80. ----
  81. .Kotlin
  82. [source,kotlin,role="secondary"]
  83. ----
  84. mvc.post("/") {
  85. with(csrf())
  86. }
  87. ----
  88. ====
  89. If you like you can include CSRF token in the header instead:
  90. ====
  91. .Java
  92. [source,java,role="primary"]
  93. ----
  94. mvc
  95. .perform(post("/").with(csrf().asHeader()))
  96. ----
  97. .Kotlin
  98. [source,kotlin,role="secondary"]
  99. ----
  100. mvc.post("/") {
  101. with(csrf().asHeader())
  102. }
  103. ----
  104. ====
  105. You can also test providing an invalid CSRF token using the following:
  106. ====
  107. .Java
  108. [source,java,role="primary"]
  109. ----
  110. mvc
  111. .perform(post("/").with(csrf().useInvalidToken()))
  112. ----
  113. .Kotlin
  114. [source,kotlin,role="secondary"]
  115. ----
  116. mvc.post("/") {
  117. with(csrf().useInvalidToken())
  118. }
  119. ----
  120. ====
  121. [[test-mockmvc-securitycontextholder]]
  122. ==== Running a Test as a User in Spring MVC Test
  123. It is often desirable to run tests as a specific user.
  124. There are two simple ways of populating the user:
  125. * <<Running as a User in Spring MVC Test with RequestPostProcessor,Running as a User in Spring MVC Test with RequestPostProcessor>>
  126. * <<Running as a User in Spring MVC Test with Annotations,Running as a User in Spring MVC Test with Annotations>>
  127. [[test-mockmvc-securitycontextholder-rpp]]
  128. ==== Running as a User in Spring MVC Test with RequestPostProcessor
  129. There are a number of options available to associate a user to the current `HttpServletRequest`.
  130. For example, the following will run as a user (which does not need to exist) with the username "user", the password "password", and the role "ROLE_USER":
  131. [NOTE]
  132. ====
  133. The support works by associating the user to the `HttpServletRequest`.
  134. To associate the request to the `SecurityContextHolder` you need to ensure that the `SecurityContextPersistenceFilter` is associated with the `MockMvc` instance.
  135. A few ways to do this are:
  136. * Invoking <<test-mockmvc-setup,apply(springSecurity())>>
  137. * Adding Spring Security's `FilterChainProxy` to `MockMvc`
  138. * Manually adding `SecurityContextPersistenceFilter` to the `MockMvc` instance may make sense when using `MockMvcBuilders.standaloneSetup`
  139. ====
  140. ====
  141. .Java
  142. [source,java,role="primary"]
  143. ----
  144. mvc
  145. .perform(get("/").with(user("user")))
  146. ----
  147. .Kotlin
  148. [source,kotlin,role="secondary"]
  149. ----
  150. mvc.get("/") {
  151. with(user("user"))
  152. }
  153. ----
  154. ====
  155. You can easily make customizations.
  156. For example, the following will run as a user (which does not need to exist) with the username "admin", the password "pass", and the roles "ROLE_USER" and "ROLE_ADMIN".
  157. ====
  158. .Java
  159. [source,java,role="primary"]
  160. ----
  161. mvc
  162. .perform(get("/admin").with(user("admin").password("pass").roles("USER","ADMIN")))
  163. ----
  164. .Kotlin
  165. [source,kotlin,role="secondary"]
  166. ----
  167. mvc.get("/admin") {
  168. with(user("admin").password("pass").roles("USER","ADMIN"))
  169. }
  170. ----
  171. ====
  172. If you have a custom `UserDetails` that you would like to use, you can easily specify that as well.
  173. For example, the following will use the specified `UserDetails` (which does not need to exist) to run with a `UsernamePasswordAuthenticationToken` that has a principal of the specified `UserDetails`:
  174. ====
  175. .Java
  176. [source,java,role="primary"]
  177. ----
  178. mvc
  179. .perform(get("/").with(user(userDetails)))
  180. ----
  181. .Kotlin
  182. [source,kotlin,role="secondary"]
  183. ----
  184. mvc.get("/") {
  185. with(user(userDetails))
  186. }
  187. ----
  188. ====
  189. You can run as anonymous user using the following:
  190. ====
  191. .Java
  192. [source,java,role="primary"]
  193. ----
  194. mvc
  195. .perform(get("/").with(anonymous()))
  196. ----
  197. .Kotlin
  198. [source,kotlin,role="secondary"]
  199. ----
  200. mvc.get("/") {
  201. with(anonymous())
  202. }
  203. ----
  204. ====
  205. This is especially useful if you are running with a default user and wish to process a few requests as an anonymous user.
  206. If you want a custom `Authentication` (which does not need to exist) you can do so using the following:
  207. ====
  208. .Java
  209. [source,java,role="primary"]
  210. ----
  211. mvc
  212. .perform(get("/").with(authentication(authentication)))
  213. ----
  214. .Kotlin
  215. [source,kotlin,role="secondary"]
  216. ----
  217. mvc.get("/") {
  218. with(authentication(authentication))
  219. }
  220. ----
  221. ====
  222. You can even customize the `SecurityContext` using the following:
  223. ====
  224. .Java
  225. [source,java,role="primary"]
  226. ----
  227. mvc
  228. .perform(get("/").with(securityContext(securityContext)))
  229. ----
  230. .Kotlin
  231. [source,kotlin,role="secondary"]
  232. ----
  233. mvc.get("/") {
  234. with(securityContext(securityContext))
  235. }
  236. ----
  237. ====
  238. We can also ensure to run as a specific user for every request by using ``MockMvcBuilders``'s default request.
  239. For example, the following will run as a user (which does not need to exist) with the username "admin", the password "password", and the role "ROLE_ADMIN":
  240. ====
  241. .Java
  242. [source,java,role="primary"]
  243. ----
  244. mvc = MockMvcBuilders
  245. .webAppContextSetup(context)
  246. .defaultRequest(get("/").with(user("user").roles("ADMIN")))
  247. .apply(springSecurity())
  248. .build();
  249. ----
  250. .Kotlin
  251. [source,kotlin,role="secondary"]
  252. ----
  253. mvc = MockMvcBuilders
  254. .webAppContextSetup(context)
  255. .defaultRequest<DefaultMockMvcBuilder>(get("/").with(user("user").roles("ADMIN")))
  256. .apply<DefaultMockMvcBuilder>(springSecurity())
  257. .build()
  258. ----
  259. ====
  260. If you find you are using the same user in many of your tests, it is recommended to move the user to a method.
  261. For example, you can specify the following in your own class named `CustomSecurityMockMvcRequestPostProcessors`:
  262. ====
  263. .Java
  264. [source,java,role="primary"]
  265. ----
  266. public static RequestPostProcessor rob() {
  267. return user("rob").roles("ADMIN");
  268. }
  269. ----
  270. .Kotlin
  271. [source,kotlin,role="secondary"]
  272. ----
  273. fun rob(): RequestPostProcessor {
  274. return user("rob").roles("ADMIN")
  275. }
  276. ----
  277. ====
  278. Now you can perform a static import on `SecurityMockMvcRequestPostProcessors` and use that within your tests:
  279. ====
  280. .Java
  281. [source,java,role="primary"]
  282. ----
  283. import static sample.CustomSecurityMockMvcRequestPostProcessors.*;
  284. ...
  285. mvc
  286. .perform(get("/").with(rob()))
  287. ----
  288. .Kotlin
  289. [source,kotlin,role="secondary"]
  290. ----
  291. import sample.CustomSecurityMockMvcRequestPostProcessors.*
  292. //...
  293. mvc.get("/") {
  294. with(rob())
  295. }
  296. ----
  297. ====
  298. ===== Running as a User in Spring MVC Test with Annotations
  299. As an alternative to using a `RequestPostProcessor` to create your user, you can use annotations described in <<Testing Method Security>>.
  300. For example, the following will run the test with the user with username "user", password "password", and role "ROLE_USER":
  301. ====
  302. .Java
  303. [source,java,role="primary"]
  304. ----
  305. @Test
  306. @WithMockUser
  307. public void requestProtectedUrlWithUser() throws Exception {
  308. mvc
  309. .perform(get("/"))
  310. ...
  311. }
  312. ----
  313. .Kotlin
  314. [source,kotlin,role="secondary"]
  315. ----
  316. @Test
  317. @WithMockUser
  318. fun requestProtectedUrlWithUser() {
  319. mvc
  320. .get("/")
  321. // ...
  322. }
  323. ----
  324. ====
  325. Alternatively, the following will run the test with the user with username "user", password "password", and role "ROLE_ADMIN":
  326. ====
  327. .Java
  328. [source,java,role="primary"]
  329. ----
  330. @Test
  331. @WithMockUser(roles="ADMIN")
  332. public void requestProtectedUrlWithUser() throws Exception {
  333. mvc
  334. .perform(get("/"))
  335. ...
  336. }
  337. ----
  338. .Kotlin
  339. [source,kotlin,role="secondary"]
  340. ----
  341. @Test
  342. @WithMockUser(roles = ["ADMIN"])
  343. fun requestProtectedUrlWithUser() {
  344. mvc
  345. .get("/")
  346. // ...
  347. }
  348. ----
  349. ====
  350. ==== Testing HTTP Basic Authentication
  351. While it has always been possible to authenticate with HTTP Basic, it was a bit tedious to remember the header name, format, and encode the values.
  352. Now this can be done using Spring Security's `httpBasic` `RequestPostProcessor`.
  353. For example, the snippet below:
  354. ====
  355. .Java
  356. [source,java,role="primary"]
  357. ----
  358. mvc
  359. .perform(get("/").with(httpBasic("user","password")))
  360. ----
  361. .Kotlin
  362. [source,kotlin,role="secondary"]
  363. ----
  364. mvc.get("/") {
  365. with(httpBasic("user","password"))
  366. }
  367. ----
  368. ====
  369. will attempt to use HTTP Basic to authenticate a user with the username "user" and the password "password" by ensuring the following header is populated on the HTTP Request:
  370. [source,text]
  371. ----
  372. Authorization: Basic dXNlcjpwYXNzd29yZA==
  373. ----
  374. [[testing-oauth2]]
  375. ==== Testing OAuth 2.0
  376. When it comes to OAuth 2.0, the same principles covered earlier still apply: Ultimately, it depends on what your method under test is expecting to be in the `SecurityContextHolder`.
  377. For example, for a controller that looks like this:
  378. ====
  379. .Java
  380. [source,java,role="primary"]
  381. ----
  382. @GetMapping("/endpoint")
  383. public String foo(Principal user) {
  384. return user.getName();
  385. }
  386. ----
  387. .Kotlin
  388. [source,kotlin,role="secondary"]
  389. ----
  390. @GetMapping("/endpoint")
  391. fun foo(user: Principal): String {
  392. return user.name
  393. }
  394. ----
  395. ====
  396. There's nothing OAuth2-specific about it, so you will likely be able to simply <<test-method-withmockuser,use `@WithMockUser`>> and be fine.
  397. But, in cases where your controllers are bound to some aspect of Spring Security's OAuth 2.0 support, like the following:
  398. ====
  399. .Java
  400. [source,java,role="primary"]
  401. ----
  402. @GetMapping("/endpoint")
  403. public String foo(@AuthenticationPrincipal OidcUser user) {
  404. return user.getIdToken().getSubject();
  405. }
  406. ----
  407. .Kotlin
  408. [source,kotlin,role="secondary"]
  409. ----
  410. @GetMapping("/endpoint")
  411. fun foo(@AuthenticationPrincipal user: OidcUser): String {
  412. return user.idToken.subject
  413. }
  414. ----
  415. ====
  416. then Spring Security's test support can come in handy.
  417. [[testing-oidc-login]]
  418. ==== Testing OIDC Login
  419. Testing the method above with Spring MVC Test would require simulating some kind of grant flow with an authorization server.
  420. Certainly this would be a daunting task, which is why Spring Security ships with support for removing this boilerplate.
  421. For example, we can tell Spring Security to include a default `OidcUser` using the `SecurityMockMvcRequestPostProcessors#oidcLogin` method, like so:
  422. ====
  423. .Java
  424. [source,java,role="primary"]
  425. ----
  426. mvc
  427. .perform(get("/endpoint").with(oidcLogin()));
  428. ----
  429. .Kotlin
  430. [source,kotlin,role="secondary"]
  431. ----
  432. mvc.get("/endpoint") {
  433. with(oidcLogin())
  434. }
  435. ----
  436. ====
  437. What this will do is configure the associated `MockHttpServletRequest` with an `OidcUser` that includes a simple `OidcIdToken`, `OidcUserInfo`, and `Collection` of granted authorities.
  438. Specifically, it will include an `OidcIdToken` with a `sub` claim set to `user`:
  439. ====
  440. .Java
  441. [source,java,role="primary"]
  442. ----
  443. assertThat(user.getIdToken().getClaim("sub")).isEqualTo("user");
  444. ----
  445. .Kotlin
  446. [source,kotlin,role="secondary"]
  447. ----
  448. assertThat(user.idToken.getClaim<String>("sub")).isEqualTo("user")
  449. ----
  450. ====
  451. an `OidcUserInfo` with no claims set:
  452. ====
  453. .Java
  454. [source,java,role="primary"]
  455. ----
  456. assertThat(user.getUserInfo().getClaims()).isEmpty();
  457. ----
  458. .Kotlin
  459. [source,kotlin,role="secondary"]
  460. ----
  461. assertThat(user.userInfo.claims).isEmpty()
  462. ----
  463. ====
  464. and a `Collection` of authorities with just one authority, `SCOPE_read`:
  465. ====
  466. .Java
  467. [source,java,role="primary"]
  468. ----
  469. assertThat(user.getAuthorities()).hasSize(1);
  470. assertThat(user.getAuthorities()).containsExactly(new SimpleGrantedAuthority("SCOPE_read"));
  471. ----
  472. .Kotlin
  473. [source,kotlin,role="secondary"]
  474. ----
  475. assertThat(user.authorities).hasSize(1)
  476. assertThat(user.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read"))
  477. ----
  478. ====
  479. Spring Security does the necessary work to make sure that the `OidcUser` instance is available for <<mvc-authentication-principal,the `@AuthenticationPrincipal` annotation>>.
  480. Further, it also links that `OidcUser` to a simple instance of `OAuth2AuthorizedClient` that it deposits into an mock `OAuth2AuthorizedClientRepository`.
  481. This can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>..
  482. [[testing-oidc-login-authorities]]
  483. ===== Configuring Authorities
  484. In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
  485. In this case, you can supply what granted authorities you need using the `authorities()` method:
  486. ====
  487. .Java
  488. [source,java,role="primary"]
  489. ----
  490. mvc
  491. .perform(get("/endpoint")
  492. .with(oidcLogin()
  493. .authorities(new SimpleGrantedAuthority("SCOPE_message:read"))
  494. )
  495. );
  496. ----
  497. .Kotlin
  498. [source,kotlin,role="secondary"]
  499. ----
  500. mvc.get("/endpoint") {
  501. with(oidcLogin()
  502. .authorities(SimpleGrantedAuthority("SCOPE_message:read"))
  503. )
  504. }
  505. ----
  506. ====
  507. [[testing-oidc-login-claims]]
  508. ===== Configuring Claims
  509. And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.
  510. Let's say, for example, that you've got a `user_id` claim that indicates the user's id in your system.
  511. You might access it like so in a controller:
  512. ====
  513. .Java
  514. [source,java,role="primary"]
  515. ----
  516. @GetMapping("/endpoint")
  517. public String foo(@AuthenticationPrincipal OidcUser oidcUser) {
  518. String userId = oidcUser.getIdToken().getClaim("user_id");
  519. // ...
  520. }
  521. ----
  522. .Kotlin
  523. [source,kotlin,role="secondary"]
  524. ----
  525. @GetMapping("/endpoint")
  526. fun foo(@AuthenticationPrincipal oidcUser: OidcUser): String {
  527. val userId = oidcUser.idToken.getClaim<String>("user_id")
  528. // ...
  529. }
  530. ----
  531. ====
  532. In that case, you'd want to specify that claim with the `idToken()` method:
  533. ====
  534. .Java
  535. [source,java,role="primary"]
  536. ----
  537. mvc
  538. .perform(get("/endpoint")
  539. .with(oidcLogin()
  540. .idToken(token -> token.claim("user_id", "1234"))
  541. )
  542. );
  543. ----
  544. .Kotlin
  545. [source,kotlin,role="secondary"]
  546. ----
  547. mvc.get("/endpoint") {
  548. with(oidcLogin()
  549. .idToken {
  550. it.claim("user_id", "1234")
  551. }
  552. )
  553. }
  554. ----
  555. ====
  556. since `OidcUser` collects its claims from `OidcIdToken`.
  557. [[testing-oidc-login-user]]
  558. ===== Additional Configurations
  559. There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
  560. * `userInfo(OidcUserInfo.Builder)` - For configuring the `OidcUserInfo` instance
  561. * `clientRegistration(ClientRegistration)` - For configuring the associated `OAuth2AuthorizedClient` with a given `ClientRegistration`
  562. * `oidcUser(OidcUser)` - For configuring the complete `OidcUser` instance
  563. That last one is handy if you:
  564. 1. Have your own implementation of `OidcUser`, or
  565. 2. Need to change the name attribute
  566. For example, let's say that your authorization server sends the principal name in the `user_name` claim instead of the `sub` claim.
  567. In that case, you can configure an `OidcUser` by hand:
  568. ====
  569. .Java
  570. [source,java,role="primary"]
  571. ----
  572. OidcUser oidcUser = new DefaultOidcUser(
  573. AuthorityUtils.createAuthorityList("SCOPE_message:read"),
  574. OidcIdToken.withTokenValue("id-token").claim("user_name", "foo_user").build(),
  575. "user_name");
  576. mvc
  577. .perform(get("/endpoint")
  578. .with(oidcLogin().oidcUser(oidcUser))
  579. );
  580. ----
  581. .Kotlin
  582. [source,kotlin,role="secondary"]
  583. ----
  584. val oidcUser: OidcUser = DefaultOidcUser(
  585. AuthorityUtils.createAuthorityList("SCOPE_message:read"),
  586. OidcIdToken.withTokenValue("id-token").claim("user_name", "foo_user").build(),
  587. "user_name"
  588. )
  589. mvc.get("/endpoint") {
  590. with(oidcLogin().oidcUser(oidcUser))
  591. }
  592. ----
  593. ====
  594. [[testing-oauth2-login]]
  595. ==== Testing OAuth 2.0 Login
  596. As with <<testing-oidc-login,testing OIDC login>>, testing OAuth 2.0 Login presents a similar challenge of mocking a grant flow.
  597. And because of that, Spring Security also has test support for non-OIDC use cases.
  598. Let's say that we've got a controller that gets the logged-in user as an `OAuth2User`:
  599. ====
  600. .Java
  601. [source,java,role="primary"]
  602. ----
  603. @GetMapping("/endpoint")
  604. public String foo(@AuthenticationPrincipal OAuth2User oauth2User) {
  605. return oauth2User.getAttribute("sub");
  606. }
  607. ----
  608. .Kotlin
  609. [source,kotlin,role="secondary"]
  610. ----
  611. @GetMapping("/endpoint")
  612. fun foo(@AuthenticationPrincipal oauth2User: OAuth2User): String? {
  613. return oauth2User.getAttribute("sub")
  614. }
  615. ----
  616. ====
  617. In that case, we can tell Spring Security to include a default `OAuth2User` using the `SecurityMockMvcRequestPostProcessors#oauth2User` method, like so:
  618. ====
  619. .Java
  620. [source,java,role="primary"]
  621. ----
  622. mvc
  623. .perform(get("/endpoint").with(oauth2Login()));
  624. ----
  625. .Kotlin
  626. [source,kotlin,role="secondary"]
  627. ----
  628. mvc.get("/endpoint") {
  629. with(oauth2Login())
  630. }
  631. ----
  632. ====
  633. What this will do is configure the associated `MockHttpServletRequest` with an `OAuth2User` that includes a simple `Map` of attributes and `Collection` of granted authorities.
  634. Specifically, it will include a `Map` with a key/value pair of `sub`/`user`:
  635. ====
  636. .Java
  637. [source,java,role="primary"]
  638. ----
  639. assertThat((String) user.getAttribute("sub")).isEqualTo("user");
  640. ----
  641. .Kotlin
  642. [source,kotlin,role="secondary"]
  643. ----
  644. assertThat(user.getAttribute<String>("sub")).isEqualTo("user")
  645. ----
  646. ====
  647. and a `Collection` of authorities with just one authority, `SCOPE_read`:
  648. ====
  649. .Java
  650. [source,java,role="primary"]
  651. ----
  652. assertThat(user.getAuthorities()).hasSize(1);
  653. assertThat(user.getAuthorities()).containsExactly(new SimpleGrantedAuthority("SCOPE_read"));
  654. ----
  655. .Kotlin
  656. [source,kotlin,role="secondary"]
  657. ----
  658. assertThat(user.authorities).hasSize(1)
  659. assertThat(user.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read"))
  660. ----
  661. ====
  662. Spring Security does the necessary work to make sure that the `OAuth2User` instance is available for <<mvc-authentication-principal,the `@AuthenticationPrincipal` annotation>>.
  663. Further, it also links that `OAuth2User` to a simple instance of `OAuth2AuthorizedClient` that it deposits in a mock `OAuth2AuthorizedClientRepository`.
  664. This can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>.
  665. [[testing-oauth2-login-authorities]]
  666. ===== Configuring Authorities
  667. In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
  668. In this case, you can supply what granted authorities you need using the `authorities()` method:
  669. ====
  670. .Java
  671. [source,java,role="primary"]
  672. ----
  673. mvc
  674. .perform(get("/endpoint")
  675. .with(oauth2Login()
  676. .authorities(new SimpleGrantedAuthority("SCOPE_message:read"))
  677. )
  678. );
  679. ----
  680. .Kotlin
  681. [source,kotlin,role="secondary"]
  682. ----
  683. mvc.get("/endpoint") {
  684. with(oauth2Login()
  685. .authorities(SimpleGrantedAuthority("SCOPE_message:read"))
  686. )
  687. }
  688. ----
  689. ====
  690. [[testing-oauth2-login-claims]]
  691. ===== Configuring Claims
  692. And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.
  693. Let's say, for example, that you've got a `user_id` attribute that indicates the user's id in your system.
  694. You might access it like so in a controller:
  695. ====
  696. .Java
  697. [source,java,role="primary"]
  698. ----
  699. @GetMapping("/endpoint")
  700. public String foo(@AuthenticationPrincipal OAuth2User oauth2User) {
  701. String userId = oauth2User.getAttribute("user_id");
  702. // ...
  703. }
  704. ----
  705. .Kotlin
  706. [source,kotlin,role="secondary"]
  707. ----
  708. @GetMapping("/endpoint")
  709. fun foo(@AuthenticationPrincipal oauth2User: OAuth2User): String {
  710. val userId = oauth2User.getAttribute<String>("user_id")
  711. // ...
  712. }
  713. ----
  714. ====
  715. In that case, you'd want to specify that attribute with the `attributes()` method:
  716. ====
  717. .Java
  718. [source,java,role="primary"]
  719. ----
  720. mvc
  721. .perform(get("/endpoint")
  722. .with(oauth2Login()
  723. .attributes(attrs -> attrs.put("user_id", "1234"))
  724. )
  725. );
  726. ----
  727. .Kotlin
  728. [source,kotlin,role="secondary"]
  729. ----
  730. mvc.get("/endpoint") {
  731. with(oauth2Login()
  732. .attributes { attrs -> attrs["user_id"] = "1234" }
  733. )
  734. }
  735. ----
  736. ====
  737. [[testing-oauth2-login-user]]
  738. ===== Additional Configurations
  739. There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
  740. * `clientRegistration(ClientRegistration)` - For configuring the associated `OAuth2AuthorizedClient` with a given `ClientRegistration`
  741. * `oauth2User(OAuth2User)` - For configuring the complete `OAuth2User` instance
  742. That last one is handy if you:
  743. 1. Have your own implementation of `OAuth2User`, or
  744. 2. Need to change the name attribute
  745. For example, let's say that your authorization server sends the principal name in the `user_name` claim instead of the `sub` claim.
  746. In that case, you can configure an `OAuth2User` by hand:
  747. ====
  748. .Java
  749. [source,java,role="primary"]
  750. ----
  751. OAuth2User oauth2User = new DefaultOAuth2User(
  752. AuthorityUtils.createAuthorityList("SCOPE_message:read"),
  753. Collections.singletonMap("user_name", "foo_user"),
  754. "user_name");
  755. mvc
  756. .perform(get("/endpoint")
  757. .with(oauth2Login().oauth2User(oauth2User))
  758. );
  759. ----
  760. .Kotlin
  761. [source,kotlin,role="secondary"]
  762. ----
  763. val oauth2User: OAuth2User = DefaultOAuth2User(
  764. AuthorityUtils.createAuthorityList("SCOPE_message:read"),
  765. mapOf(Pair("user_name", "foo_user")),
  766. "user_name"
  767. )
  768. mvc.get("/endpoint") {
  769. with(oauth2Login().oauth2User(oauth2User))
  770. }
  771. ----
  772. ====
  773. [[testing-oauth2-client]]
  774. ==== Testing OAuth 2.0 Clients
  775. Independent of how your user authenticates, you may have other tokens and client registrations that are in play for the request you are testing.
  776. For example, your controller may be relying on the client credentials grant to get a token that isn't associated with the user at all:
  777. ====
  778. .Java
  779. [source,java,role="primary"]
  780. ----
  781. @GetMapping("/endpoint")
  782. public String foo(@RegisteredOAuth2AuthorizedClient("my-app") OAuth2AuthorizedClient authorizedClient) {
  783. return this.webClient.get()
  784. .attributes(oauth2AuthorizedClient(authorizedClient))
  785. .retrieve()
  786. .bodyToMono(String.class)
  787. .block();
  788. }
  789. ----
  790. .Kotlin
  791. [source,kotlin,role="secondary"]
  792. ----
  793. @GetMapping("/endpoint")
  794. fun foo(@RegisteredOAuth2AuthorizedClient("my-app") authorizedClient: OAuth2AuthorizedClient?): String? {
  795. return this.webClient.get()
  796. .attributes(oauth2AuthorizedClient(authorizedClient))
  797. .retrieve()
  798. .bodyToMono(String::class.java)
  799. .block()
  800. }
  801. ----
  802. ====
  803. Simulating this handshake with the authorization server could be cumbersome.
  804. Instead, you can use `SecurityMockMvcRequestPostProcessor#oauth2Client` to add a `OAuth2AuthorizedClient` into a mock `OAuth2AuthorizedClientRepository`:
  805. ====
  806. .Java
  807. [source,java,role="primary"]
  808. ----
  809. mvc
  810. .perform(get("/endpoint").with(oauth2Client("my-app")));
  811. ----
  812. .Kotlin
  813. [source,kotlin,role="secondary"]
  814. ----
  815. mvc.get("/endpoint") {
  816. with(
  817. oauth2Client("my-app")
  818. )
  819. }
  820. ----
  821. ====
  822. What this will do is create an `OAuth2AuthorizedClient` that has a simple `ClientRegistration`, `OAuth2AccessToken`, and resource owner name.
  823. Specifically, it will include a `ClientRegistration` with a client id of "test-client" and client secret of "test-secret":
  824. ====
  825. .Java
  826. [source,java,role="primary"]
  827. ----
  828. assertThat(authorizedClient.getClientRegistration().getClientId()).isEqualTo("test-client");
  829. assertThat(authorizedClient.getClientRegistration().getClientSecret()).isEqualTo("test-secret");
  830. ----
  831. .Kotlin
  832. [source,kotlin,role="secondary"]
  833. ----
  834. assertThat(authorizedClient.clientRegistration.clientId).isEqualTo("test-client")
  835. assertThat(authorizedClient.clientRegistration.clientSecret).isEqualTo("test-secret")
  836. ----
  837. ====
  838. a resource owner name of "user":
  839. ====
  840. .Java
  841. [source,java,role="primary"]
  842. ----
  843. assertThat(authorizedClient.getPrincipalName()).isEqualTo("user");
  844. ----
  845. .Kotlin
  846. [source,kotlin,role="secondary"]
  847. ----
  848. assertThat(authorizedClient.principalName).isEqualTo("user")
  849. ----
  850. ====
  851. and an `OAuth2AccessToken` with just one scope, `read`:
  852. ====
  853. .Java
  854. [source,java,role="primary"]
  855. ----
  856. assertThat(authorizedClient.getAccessToken().getScopes()).hasSize(1);
  857. assertThat(authorizedClient.getAccessToken().getScopes()).containsExactly("read");
  858. ----
  859. .Kotlin
  860. [source,kotlin,role="secondary"]
  861. ----
  862. assertThat(authorizedClient.accessToken.scopes).hasSize(1)
  863. assertThat(authorizedClient.accessToken.scopes).containsExactly("read")
  864. ----
  865. ====
  866. The client can then be retrieved as normal using `@RegisteredOAuth2AuthorizedClient` in a controller method.
  867. [[testing-oauth2-client-scopes]]
  868. ===== Configuring Scopes
  869. In many circumstances, the OAuth 2.0 access token comes with a set of scopes.
  870. If your controller inspects these, say like so:
  871. ====
  872. .Java
  873. [source,java,role="primary"]
  874. ----
  875. @GetMapping("/endpoint")
  876. public String foo(@RegisteredOAuth2AuthorizedClient("my-app") OAuth2AuthorizedClient authorizedClient) {
  877. Set<String> scopes = authorizedClient.getAccessToken().getScopes();
  878. if (scopes.contains("message:read")) {
  879. return this.webClient.get()
  880. .attributes(oauth2AuthorizedClient(authorizedClient))
  881. .retrieve()
  882. .bodyToMono(String.class)
  883. .block();
  884. }
  885. // ...
  886. }
  887. ----
  888. .Kotlin
  889. [source,kotlin,role="secondary"]
  890. ----
  891. @GetMapping("/endpoint")
  892. fun foo(@RegisteredOAuth2AuthorizedClient("my-app") authorizedClient: OAuth2AuthorizedClient): String? {
  893. val scopes = authorizedClient.accessToken.scopes
  894. if (scopes.contains("message:read")) {
  895. return webClient.get()
  896. .attributes(oauth2AuthorizedClient(authorizedClient))
  897. .retrieve()
  898. .bodyToMono(String::class.java)
  899. .block()
  900. }
  901. // ...
  902. }
  903. ----
  904. ====
  905. then you can configure the scope using the `accessToken()` method:
  906. ====
  907. .Java
  908. [source,java,role="primary"]
  909. ----
  910. mvc
  911. .perform(get("/endpoint")
  912. .with(oauth2Client("my-app")
  913. .accessToken(new OAuth2AccessToken(BEARER, "token", null, null, Collections.singleton("message:read"))))
  914. )
  915. );
  916. ----
  917. .Kotlin
  918. [source,kotlin,role="secondary"]
  919. ----
  920. mvc.get("/endpoint") {
  921. with(oauth2Client("my-app")
  922. .accessToken(OAuth2AccessToken(BEARER, "token", null, null, Collections.singleton("message:read")))
  923. )
  924. }
  925. ----
  926. ====
  927. [[testing-oauth2-client-registration]]
  928. ===== Additional Configurations
  929. There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:
  930. * `principalName(String)` - For configuring the resource owner name
  931. * `clientRegistration(Consumer<ClientRegistration.Builder>)` - For configuring the associated `ClientRegistration`
  932. * `clientRegistration(ClientRegistration)` - For configuring the complete `ClientRegistration`
  933. That last one is handy if you want to use a real `ClientRegistration`
  934. For example, let's say that you are wanting to use one of your app's `ClientRegistration` definitions, as specified in your `application.yml`.
  935. In that case, your test can autowire the `ClientRegistrationRepository` and look up the one your test needs:
  936. ====
  937. .Java
  938. [source,java,role="primary"]
  939. ----
  940. @Autowired
  941. ClientRegistrationRepository clientRegistrationRepository;
  942. // ...
  943. mvc
  944. .perform(get("/endpoint")
  945. .with(oauth2Client()
  946. .clientRegistration(this.clientRegistrationRepository.findByRegistrationId("facebook"))));
  947. ----
  948. .Kotlin
  949. [source,kotlin,role="secondary"]
  950. ----
  951. @Autowired
  952. lateinit var clientRegistrationRepository: ClientRegistrationRepository
  953. // ...
  954. mvc.get("/endpoint") {
  955. with(oauth2Client("my-app")
  956. .clientRegistration(clientRegistrationRepository.findByRegistrationId("facebook"))
  957. )
  958. }
  959. ----
  960. ====
  961. [[testing-jwt]]
  962. ==== Testing JWT Authentication
  963. In order to make an authorized request on a resource server, you need a bearer token.
  964. If your resource server is configured for JWTs, then this would mean that the bearer token needs to be signed and then encoded according to the JWT specification.
  965. All of this can be quite daunting, especially when this isn't the focus of your test.
  966. Fortunately, there are a number of simple ways that you can overcome this difficulty and allow your tests to focus on authorization and not on representing bearer tokens.
  967. We'll look at two of them now:
  968. ===== `jwt() RequestPostProcessor`
  969. The first way is via a `RequestPostProcessor`.
  970. The simplest of these would look something like this:
  971. ====
  972. .Java
  973. [source,java,role="primary"]
  974. ----
  975. mvc
  976. .perform(get("/endpoint").with(jwt()));
  977. ----
  978. .Kotlin
  979. [source,kotlin,role="secondary"]
  980. ----
  981. mvc.get("/endpoint") {
  982. with(jwt())
  983. }
  984. ----
  985. ====
  986. What this will do is create a mock `Jwt`, passing it correctly through any authentication APIs so that it's available for your authorization mechanisms to verify.
  987. By default, the `JWT` that it creates has the following characteristics:
  988. [source,json]
  989. ----
  990. {
  991. "headers" : { "alg" : "none" },
  992. "claims" : {
  993. "sub" : "user",
  994. "scope" : "read"
  995. }
  996. }
  997. ----
  998. And the resulting `Jwt`, were it tested, would pass in the following way:
  999. ====
  1000. .Java
  1001. [source,java,role="primary"]
  1002. ----
  1003. assertThat(jwt.getTokenValue()).isEqualTo("token");
  1004. assertThat(jwt.getHeaders().get("alg")).isEqualTo("none");
  1005. assertThat(jwt.getSubject()).isEqualTo("sub");
  1006. ----
  1007. .Kotlin
  1008. [source,kotlin,role="secondary"]
  1009. ----
  1010. assertThat(jwt.tokenValue).isEqualTo("token")
  1011. assertThat(jwt.headers["alg"]).isEqualTo("none")
  1012. assertThat(jwt.subject).isEqualTo("sub")
  1013. ----
  1014. ====
  1015. These values can, of course be configured.
  1016. Any headers or claims can be configured with their corresponding methods:
  1017. ====
  1018. .Java
  1019. [source,java,role="primary"]
  1020. ----
  1021. mvc
  1022. .perform(get("/endpoint")
  1023. .with(jwt().jwt(jwt -> jwt.header("kid", "one").claim("iss", "https://idp.example.org"))));
  1024. ----
  1025. .Kotlin
  1026. [source,kotlin,role="secondary"]
  1027. ----
  1028. mvc.get("/endpoint") {
  1029. with(
  1030. jwt().jwt { jwt -> jwt.header("kid", "one").claim("iss", "https://idp.example.org") }
  1031. )
  1032. }
  1033. ----
  1034. ====
  1035. ====
  1036. .Java
  1037. [source,java,role="primary"]
  1038. ----
  1039. mvc
  1040. .perform(get("/endpoint")
  1041. .with(jwt().jwt(jwt -> jwt.claims(claims -> claims.remove("scope")))));
  1042. ----
  1043. .Kotlin
  1044. [source,kotlin,role="secondary"]
  1045. ----
  1046. mvc.get("/endpoint") {
  1047. with(
  1048. jwt().jwt { jwt -> jwt.claims { claims -> claims.remove("scope") } }
  1049. )
  1050. }
  1051. ----
  1052. ====
  1053. The `scope` and `scp` claims are processed the same way here as they are in a normal bearer token request.
  1054. However, this can be overridden simply by providing the list of `GrantedAuthority` instances that you need for your test:
  1055. ====
  1056. .Java
  1057. [source,java,role="primary"]
  1058. ----
  1059. mvc
  1060. .perform(get("/endpoint")
  1061. .with(jwt().authorities(new SimpleGrantedAuthority("SCOPE_messages"))));
  1062. ----
  1063. .Kotlin
  1064. [source,kotlin,role="secondary"]
  1065. ----
  1066. mvc.get("/endpoint") {
  1067. with(
  1068. jwt().authorities(SimpleGrantedAuthority("SCOPE_messages"))
  1069. )
  1070. }
  1071. ----
  1072. ====
  1073. Or, if you have a custom `Jwt` to `Collection<GrantedAuthority>` converter, you can also use that to derive the authorities:
  1074. ====
  1075. .Java
  1076. [source,java,role="primary"]
  1077. ----
  1078. mvc
  1079. .perform(get("/endpoint")
  1080. .with(jwt().authorities(new MyConverter())));
  1081. ----
  1082. .Kotlin
  1083. [source,kotlin,role="secondary"]
  1084. ----
  1085. mvc.get("/endpoint") {
  1086. with(
  1087. jwt().authorities(MyConverter())
  1088. )
  1089. }
  1090. ----
  1091. ====
  1092. You can also specify a complete `Jwt`, for which `{security-api-url}org/springframework/security/oauth2/jwt/Jwt.Builder.html[Jwt.Builder]` comes quite handy:
  1093. ====
  1094. .Java
  1095. [source,java,role="primary"]
  1096. ----
  1097. Jwt jwt = Jwt.withTokenValue("token")
  1098. .header("alg", "none")
  1099. .claim("sub", "user")
  1100. .claim("scope", "read")
  1101. .build();
  1102. mvc
  1103. .perform(get("/endpoint")
  1104. .with(jwt().jwt(jwt)));
  1105. ----
  1106. .Kotlin
  1107. [source,kotlin,role="secondary"]
  1108. ----
  1109. val jwt: Jwt = Jwt.withTokenValue("token")
  1110. .header("alg", "none")
  1111. .claim("sub", "user")
  1112. .claim("scope", "read")
  1113. .build()
  1114. mvc.get("/endpoint") {
  1115. with(
  1116. jwt().jwt(jwt)
  1117. )
  1118. }
  1119. ----
  1120. ====
  1121. ===== `authentication()` `RequestPostProcessor`
  1122. The second way is by using the `authentication()` `RequestPostProcessor`.
  1123. Essentially, you can instantiate your own `JwtAuthenticationToken` and provide it in your test, like so:
  1124. ====
  1125. .Java
  1126. [source,java,role="primary"]
  1127. ----
  1128. Jwt jwt = Jwt.withTokenValue("token")
  1129. .header("alg", "none")
  1130. .claim("sub", "user")
  1131. .build();
  1132. Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("SCOPE_read");
  1133. JwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities);
  1134. mvc
  1135. .perform(get("/endpoint")
  1136. .with(authentication(token)));
  1137. ----
  1138. .Kotlin
  1139. [source,kotlin,role="secondary"]
  1140. ----
  1141. val jwt = Jwt.withTokenValue("token")
  1142. .header("alg", "none")
  1143. .claim("sub", "user")
  1144. .build()
  1145. val authorities: Collection<GrantedAuthority> = AuthorityUtils.createAuthorityList("SCOPE_read")
  1146. val token = JwtAuthenticationToken(jwt, authorities)
  1147. mvc.get("/endpoint") {
  1148. with(
  1149. authentication(token)
  1150. )
  1151. }
  1152. ----
  1153. ====
  1154. Note that as an alternative to these, you can also mock the `JwtDecoder` bean itself with a `@MockBean` annotation.
  1155. [[testing-opaque-token]]
  1156. ==== Testing Opaque Token Authentication
  1157. Similar to <<testing-jwt,JWTs>>, opaque tokens require an authorization server in order to verify their validity, which can make testing more difficult.
  1158. To help with that, Spring Security has test support for opaque tokens.
  1159. Let's say that we've got a controller that retrieves the authentication as a `BearerTokenAuthentication`:
  1160. ====
  1161. .Java
  1162. [source,java,role="primary"]
  1163. ----
  1164. @GetMapping("/endpoint")
  1165. public String foo(BearerTokenAuthentication authentication) {
  1166. return (String) authentication.getTokenAttributes().get("sub");
  1167. }
  1168. ----
  1169. .Kotlin
  1170. [source,kotlin,role="secondary"]
  1171. ----
  1172. @GetMapping("/endpoint")
  1173. fun foo(authentication: BearerTokenAuthentication): String {
  1174. return authentication.tokenAttributes["sub"] as String
  1175. }
  1176. ----
  1177. ====
  1178. In that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `SecurityMockMvcRequestPostProcessors#opaqueToken` method, like so:
  1179. ====
  1180. .Java
  1181. [source,java,role="primary"]
  1182. ----
  1183. mvc
  1184. .perform(get("/endpoint").with(opaqueToken()));
  1185. ----
  1186. .Kotlin
  1187. [source,kotlin,role="secondary"]
  1188. ----
  1189. mvc.get("/endpoint") {
  1190. with(opaqueToken())
  1191. }
  1192. ----
  1193. ====
  1194. What this will do is configure the associated `MockHttpServletRequest` with a `BearerTokenAuthentication` that includes a simple `OAuth2AuthenticatedPrincipal`, `Map` of attributes, and `Collection` of granted authorities.
  1195. Specifically, it will include a `Map` with a key/value pair of `sub`/`user`:
  1196. ====
  1197. .Java
  1198. [source,java,role="primary"]
  1199. ----
  1200. assertThat((String) token.getTokenAttributes().get("sub")).isEqualTo("user");
  1201. ----
  1202. .Kotlin
  1203. [source,kotlin,role="secondary"]
  1204. ----
  1205. assertThat(token.tokenAttributes["sub"] as String).isEqualTo("user")
  1206. ----
  1207. ====
  1208. and a `Collection` of authorities with just one authority, `SCOPE_read`:
  1209. ====
  1210. .Java
  1211. [source,java,role="primary"]
  1212. ----
  1213. assertThat(token.getAuthorities()).hasSize(1);
  1214. assertThat(token.getAuthorities()).containsExactly(new SimpleGrantedAuthority("SCOPE_read"));
  1215. ----
  1216. .Kotlin
  1217. [source,kotlin,role="secondary"]
  1218. ----
  1219. assertThat(token.authorities).hasSize(1)
  1220. assertThat(token.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read"))
  1221. ----
  1222. ====
  1223. Spring Security does the necessary work to make sure that the `BearerTokenAuthentication` instance is available for your controller methods.
  1224. [[testing-opaque-token-authorities]]
  1225. ===== Configuring Authorities
  1226. In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.
  1227. In this case, you can supply what granted authorities you need using the `authorities()` method:
  1228. ====
  1229. .Java
  1230. [source,java,role="primary"]
  1231. ----
  1232. mvc
  1233. .perform(get("/endpoint")
  1234. .with(opaqueToken()
  1235. .authorities(new SimpleGrantedAuthority("SCOPE_message:read"))
  1236. )
  1237. );
  1238. ----
  1239. .Kotlin
  1240. [source,kotlin,role="secondary"]
  1241. ----
  1242. mvc.get("/endpoint") {
  1243. with(opaqueToken()
  1244. .authorities(SimpleGrantedAuthority("SCOPE_message:read"))
  1245. )
  1246. }
  1247. ----
  1248. ====
  1249. [[testing-opaque-token-attributes]]
  1250. ===== Configuring Claims
  1251. And while granted authorities are quite common across all of Spring Security, we also have attributes in the case of OAuth 2.0.
  1252. Let's say, for example, that you've got a `user_id` attribute that indicates the user's id in your system.
  1253. You might access it like so in a controller:
  1254. ====
  1255. .Java
  1256. [source,java,role="primary"]
  1257. ----
  1258. @GetMapping("/endpoint")
  1259. public String foo(BearerTokenAuthentication authentication) {
  1260. String userId = (String) authentication.getTokenAttributes().get("user_id");
  1261. // ...
  1262. }
  1263. ----
  1264. .Kotlin
  1265. [source,kotlin,role="secondary"]
  1266. ----
  1267. @GetMapping("/endpoint")
  1268. fun foo(authentication: BearerTokenAuthentication): String {
  1269. val userId = authentication.tokenAttributes["user_id"] as String
  1270. // ...
  1271. }
  1272. ----
  1273. ====
  1274. In that case, you'd want to specify that attribute with the `attributes()` method:
  1275. ====
  1276. .Java
  1277. [source,java,role="primary"]
  1278. ----
  1279. mvc
  1280. .perform(get("/endpoint")
  1281. .with(opaqueToken()
  1282. .attributes(attrs -> attrs.put("user_id", "1234"))
  1283. )
  1284. );
  1285. ----
  1286. .Kotlin
  1287. [source,kotlin,role="secondary"]
  1288. ----
  1289. mvc.get("/endpoint") {
  1290. with(opaqueToken()
  1291. .attributes { attrs -> attrs["user_id"] = "1234" }
  1292. )
  1293. }
  1294. ----
  1295. ====
  1296. [[testing-opaque-token-principal]]
  1297. ===== Additional Configurations
  1298. There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects.
  1299. One such is `principal(OAuth2AuthenticatedPrincipal)`, which you can use to configure the complete `OAuth2AuthenticatedPrincipal` instance that underlies the `BearerTokenAuthentication`
  1300. It's handy if you:
  1301. 1. Have your own implementation of `OAuth2AuthenticatedPrincipal`, or
  1302. 2. Want to specify a different principal name
  1303. For example, let's say that your authorization server sends the principal name in the `user_name` attribute instead of the `sub` attribute.
  1304. In that case, you can configure an `OAuth2AuthenticatedPrincipal` by hand:
  1305. ====
  1306. .Java
  1307. [source,java,role="primary"]
  1308. ----
  1309. Map<String, Object> attributes = Collections.singletonMap("user_name", "foo_user");
  1310. OAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(
  1311. (String) attributes.get("user_name"),
  1312. attributes,
  1313. AuthorityUtils.createAuthorityList("SCOPE_message:read"));
  1314. mvc
  1315. .perform(get("/endpoint")
  1316. .with(opaqueToken().principal(principal))
  1317. );
  1318. ----
  1319. .Kotlin
  1320. [source,kotlin,role="secondary"]
  1321. ----
  1322. val attributes: Map<String, Any> = Collections.singletonMap("user_name", "foo_user")
  1323. val principal: OAuth2AuthenticatedPrincipal = DefaultOAuth2AuthenticatedPrincipal(
  1324. attributes["user_name"] as String?,
  1325. attributes,
  1326. AuthorityUtils.createAuthorityList("SCOPE_message:read")
  1327. )
  1328. mvc.get("/endpoint") {
  1329. with(opaqueToken().principal(principal))
  1330. }
  1331. ----
  1332. ====
  1333. Note that as an alternative to using `opaqueToken()` test support, you can also mock the `OpaqueTokenIntrospector` bean itself with a `@MockBean` annotation.
  1334. === SecurityMockMvcRequestBuilders
  1335. Spring MVC Test also provides a `RequestBuilder` interface that can be used to create the `MockHttpServletRequest` used in your test.
  1336. Spring Security provides a few `RequestBuilder` implementations that can be used to make testing easier.
  1337. In order to use Spring Security's `RequestBuilder` implementations ensure the following static import is used:
  1338. ====
  1339. .Java
  1340. [source,java,role="primary"]
  1341. ----
  1342. import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
  1343. ----
  1344. .Kotlin
  1345. [source,kotlin,role="secondary"]
  1346. ----
  1347. import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*
  1348. ----
  1349. ====
  1350. ==== Testing Form Based Authentication
  1351. You can easily create a request to test a form based authentication using Spring Security's testing support.
  1352. For example, the following will submit a POST to "/login" with the username "user", the password "password", and a valid CSRF token:
  1353. ====
  1354. .Java
  1355. [source,java,role="primary"]
  1356. ----
  1357. mvc
  1358. .perform(formLogin())
  1359. ----
  1360. .Kotlin
  1361. [source,kotlin,role="secondary"]
  1362. ----
  1363. mvc
  1364. .perform(formLogin())
  1365. ----
  1366. ====
  1367. It is easy to customize the request.
  1368. For example, the following will submit a POST to "/auth" with the username "admin", the password "pass", and a valid CSRF token:
  1369. ====
  1370. .Java
  1371. [source,java,role="primary"]
  1372. ----
  1373. mvc
  1374. .perform(formLogin("/auth").user("admin").password("pass"))
  1375. ----
  1376. .Kotlin
  1377. [source,kotlin,role="secondary"]
  1378. ----
  1379. mvc
  1380. .perform(formLogin("/auth").user("admin").password("pass"))
  1381. ----
  1382. ====
  1383. We can also customize the parameters names that the username and password are included on.
  1384. For example, this is the above request modified to include the username on the HTTP parameter "u" and the password on the HTTP parameter "p".
  1385. ====
  1386. .Java
  1387. [source,java,role="primary"]
  1388. ----
  1389. mvc
  1390. .perform(formLogin("/auth").user("u","admin").password("p","pass"))
  1391. ----
  1392. .Kotlin
  1393. [source,kotlin,role="secondary"]
  1394. ----
  1395. mvc
  1396. .perform(formLogin("/auth").user("u","admin").password("p","pass"))
  1397. ----
  1398. ====
  1399. [[test-logout]]
  1400. ==== Testing Logout
  1401. While fairly trivial using standard Spring MVC Test, you can use Spring Security's testing support to make testing log out easier.
  1402. For example, the following will submit a POST to "/logout" with a valid CSRF token:
  1403. ====
  1404. .Java
  1405. [source,java,role="primary"]
  1406. ----
  1407. mvc
  1408. .perform(logout())
  1409. ----
  1410. .Kotlin
  1411. [source,kotlin,role="secondary"]
  1412. ----
  1413. mvc
  1414. .perform(logout())
  1415. ----
  1416. ====
  1417. You can also customize the URL to post to.
  1418. For example, the snippet below will submit a POST to "/signout" with a valid CSRF token:
  1419. ====
  1420. .Java
  1421. [source,java,role="primary"]
  1422. ----
  1423. mvc
  1424. .perform(logout("/signout"))
  1425. ----
  1426. .Kotlin
  1427. [source,kotlin,role="secondary"]
  1428. ----
  1429. mvc
  1430. .perform(logout("/signout"))
  1431. ----
  1432. ====
  1433. === SecurityMockMvcResultMatchers
  1434. At times it is desirable to make various security related assertions about a request.
  1435. To accommodate this need, Spring Security Test support implements Spring MVC Test's `ResultMatcher` interface.
  1436. In order to use Spring Security's `ResultMatcher` implementations ensure the following static import is used:
  1437. ====
  1438. .Java
  1439. [source,java,role="primary"]
  1440. ----
  1441. import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
  1442. ----
  1443. .Kotlin
  1444. [source,kotlin,role="secondary"]
  1445. ----
  1446. import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*
  1447. ----
  1448. ====
  1449. ==== Unauthenticated Assertion
  1450. At times it may be valuable to assert that there is no authenticated user associated with the result of a `MockMvc` invocation.
  1451. For example, you might want to test submitting an invalid username and password and verify that no user is authenticated.
  1452. You can easily do this with Spring Security's testing support using something like the following:
  1453. ====
  1454. .Java
  1455. [source,java,role="primary"]
  1456. ----
  1457. mvc
  1458. .perform(formLogin().password("invalid"))
  1459. .andExpect(unauthenticated());
  1460. ----
  1461. .Kotlin
  1462. [source,kotlin,role="secondary"]
  1463. ----
  1464. mvc
  1465. .perform(formLogin().password("invalid"))
  1466. .andExpect { unauthenticated() }
  1467. ----
  1468. ====
  1469. ==== Authenticated Assertion
  1470. It is often times that we must assert that an authenticated user exists.
  1471. For example, we may want to verify that we authenticated successfully.
  1472. We could verify that a form based login was successful with the following snippet of code:
  1473. ====
  1474. .Java
  1475. [source,java,role="primary"]
  1476. ----
  1477. mvc
  1478. .perform(formLogin())
  1479. .andExpect(authenticated());
  1480. ----
  1481. .Kotlin
  1482. [source,kotlin,role="secondary"]
  1483. ----
  1484. mvc
  1485. .perform(formLogin())
  1486. .andExpect { authenticated() }
  1487. ----
  1488. ====
  1489. If we wanted to assert the roles of the user, we could refine our previous code as shown below:
  1490. ====
  1491. .Java
  1492. [source,java,role="primary"]
  1493. ----
  1494. mvc
  1495. .perform(formLogin().user("admin"))
  1496. .andExpect(authenticated().withRoles("USER","ADMIN"));
  1497. ----
  1498. .Kotlin
  1499. [source,kotlin,role="secondary"]
  1500. ----
  1501. mvc
  1502. .perform(formLogin())
  1503. .andExpect { authenticated().withRoles("USER","ADMIN") }
  1504. ----
  1505. ====
  1506. Alternatively, we could verify the username:
  1507. ====
  1508. .Java
  1509. [source,java,role="primary"]
  1510. ----
  1511. mvc
  1512. .perform(formLogin().user("admin"))
  1513. .andExpect(authenticated().withUsername("admin"));
  1514. ----
  1515. .Kotlin
  1516. [source,kotlin,role="secondary"]
  1517. ----
  1518. mvc
  1519. .perform(formLogin().user("admin"))
  1520. .andExpect { authenticated().withUsername("admin") }
  1521. ----
  1522. ====
  1523. We can also combine the assertions:
  1524. ====
  1525. .Java
  1526. [source,java,role="primary"]
  1527. ----
  1528. mvc
  1529. .perform(formLogin().user("admin"))
  1530. .andExpect(authenticated().withUsername("admin").withRoles("USER", "ADMIN"));
  1531. ----
  1532. .Kotlin
  1533. [source,kotlin,role="secondary"]
  1534. ----
  1535. mvc
  1536. .perform(formLogin().user("admin"))
  1537. .andExpect { authenticated().withUsername("admin").withRoles("USER", "ADMIN") }
  1538. ----
  1539. ====
  1540. We can also make arbitrary assertions on the authentication
  1541. ====
  1542. .Java
  1543. [source,java,role="primary"]
  1544. ----
  1545. mvc
  1546. .perform(formLogin())
  1547. .andExpect(authenticated().withAuthentication(auth ->
  1548. assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken.class)));
  1549. ----
  1550. .Kotlin
  1551. [source,kotlin,role="secondary"]
  1552. ----
  1553. mvc
  1554. .perform(formLogin())
  1555. .andExpect {
  1556. authenticated().withAuthentication { auth ->
  1557. assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken::class.java) }
  1558. }
  1559. }
  1560. ----
  1561. ====
  1562. === SecurityMockMvcResultHandlers
  1563. Spring Security provides a few ``ResultHandler``s implementations.
  1564. In order to use Spring Security's ``ResultHandler``s implementations ensure the following static import is used:
  1565. [source,java]
  1566. ----
  1567. import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.*;
  1568. ----
  1569. ==== Exporting the SecurityContext
  1570. Often times we want to query a repository to see if some `MockMvc` request actually persisted in the database.
  1571. In some cases our repository query uses the <<data,Spring Data Integration>> to filter the results based on current user's username or any other property.
  1572. Let's see an example:
  1573. A repository interface:
  1574. [source,java]
  1575. ----
  1576. private interface MessageRepository extends JpaRepository<Message, Long> {
  1577. @Query("SELECT m.content FROM Message m WHERE m.sentBy = ?#{ principal?.name }")
  1578. List<String> findAllUserMessages();
  1579. }
  1580. ----
  1581. Our test scenario:
  1582. [source,java]
  1583. ----
  1584. mvc
  1585. .perform(post("/message")
  1586. .content("New Message")
  1587. .contentType(MediaType.TEXT_PLAIN)
  1588. )
  1589. .andExpect(status().isOk());
  1590. List<String> userMessages = messageRepository.findAllUserMessages();
  1591. assertThat(userMessages).hasSize(1);
  1592. ----
  1593. This test won't pass because after our request finishes, the `SecurityContextHolder` will be cleared out by the filter chain.
  1594. We can then export the `TestSecurityContextHolder` to our `SecurityContextHolder` and use it as we want:
  1595. [source,java]
  1596. ----
  1597. mvc
  1598. .perform(post("/message")
  1599. .content("New Message")
  1600. .contentType(MediaType.TEXT_PLAIN)
  1601. )
  1602. .andDo(exportTestSecurityContext())
  1603. .andExpect(status().isOk());
  1604. List<String> userMessages = messageRepository.findAllUserMessages();
  1605. assertThat(userMessages).hasSize(1);
  1606. ----
  1607. [NOTE]
  1608. ====
  1609. Remember to clear the `SecurityContextHolder` between your tests, or it may leak amongst them
  1610. ====