{"id":333178,"date":"2022-05-16T15:01:14","date_gmt":"2022-05-16T15:01:14","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=333178"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=333178","title":{"rendered":"<span>Thymeleaf + Spring WebFlux + Spring Security<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/b83\/0f6\/18c\/b830f618c6c1267e96bb5c3cd241e710.jpg\" width=\"780\" height=\"439\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b83\/0f6\/18c\/b830f618c6c1267e96bb5c3cd241e710.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>Thymeleaf \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0434\u0430\u0432\u043d\u043e, \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c 10 \u043b\u0435\u0442 \u043d\u0430\u0437\u0430\u0434, \u043d\u043e \u043e\u043d \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u0432\u0435\u0441\u044c\u043c\u0430 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u0435\u043d \u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f. \u0428\u0430\u0431\u043b\u043e\u043d\u044b Thymeleaf \u0443\u0434\u043e\u0431\u043d\u044b \u0442\u0435\u043c, \u0447\u0442\u043e \u043f\u0440\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043e\u043d\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0435 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0438 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Spring WebFlux \u0441 Thymeleaf, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 Okta OIDC, \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 CSRF-\u0430\u0442\u0430\u043a \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u0439.<\/p>\n<p>\u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/httpie.io\/\"><u>HTTPie 3.0.2<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/jdk.java.net\/java-se-ri\/11\"><u>Java 11<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/cli.okta.com\/\"><u>Okta CLI 0.10.0<\/u><\/a><\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Thymeleaf?<\/h3>\n<p>Thymeleaf \u2014 \u044d\u0442\u043e \u043e\u043f\u0435\u043d\u0441\u043e\u0440\u0441\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043a\u0430\u043a \u0432\u0435\u0431, \u0442\u0430\u043a \u0438 \u0434\u0440\u0443\u0433\u0438\u0445, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0414\u0430\u043d\u0438\u044d\u043b\u0435\u043c \u0424\u0435\u0440\u043d\u0430\u043d\u0434\u0435\u0441\u043e\u043c (Daniel Fern\u00e1ndez). \u0428\u0430\u0431\u043b\u043e\u043d\u044b \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 HTML \u0438 \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u043e Spring MVC, Spring Security \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u043c\u0438. \u0412 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0435\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u043e Spring WebFlux, \u043d\u043e \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0431 \u044d\u0442\u043e\u043c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u0430\u043b\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438. Thymeleaf-\u0441\u0442\u0430\u0440\u0442\u0435\u0440 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 <a href=\"https:\/\/docs.spring.io\/spring-framework\/docs\/current\/reference\/html\/web-reactive.html#webflux-view-thymeleaf\"><u>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443<\/u><\/a> template engine, template resolver \u0438 reactive view resolver.\u00a0<\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 Thymeleaf \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u0432 \u0441\u0435\u0431\u044f:<\/p>\n<ul>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0443 \u0441 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430\u043c\u0438: \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u0438 \u0448\u0430\u0431\u043b\u043e\u043d\u0430. \u041c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u0440\u0438 \u043e\u0442\u0432\u0435\u0442\u0435 \u043d\u0430 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0422\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c &#171;\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442&#187;: \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437\u043d\u044b\u0445 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0444\u043e\u0440\u043c \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432-\u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 \u043f\u043e\u043b\u044f \u0444\u043e\u0440\u043c\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044f\u0437\u044b\u043a\u0430 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439 Thymeleaf <a href=\"https:\/\/www.thymeleaf.org\/doc\/tutorials\/3.0\/usingthymeleaf.html#standard-expression-syntax\"><u>Standard Expression Syntax<\/u><\/a>.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0446\u0438\u043a\u043b\u043e\u0432 \u0438 \u0443\u0441\u043b\u043e\u0432\u043d\u044b\u0445 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0439.<\/p>\n<\/li>\n<\/ul>\n<h3>Spring WebFlux-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 Thymeleaf<\/h3>\n<p>\u041c\u044b \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0435 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Spring Boot \u0441 Thymeleaf. \u0417\u0430\u0433\u043e\u0442\u043e\u0432\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <a href=\"https:\/\/start.spring.io\/\"><u>Spring Initializr<\/u><\/a> \u0438\u043b\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b HTTPie:<\/p>\n<pre><code>https -d start.spring.io\/starter.zip bootVersion==2.6.4 \\   baseDir==thymeleaf-security \\   groupId==com.okta.developer.thymeleaf-security \\   artifactId==thymeleaf-security \\   name==thymeleaf-security \\   packageName==com.okta.developer.demo \\   javaVersion==11 \\   dependencies==webflux,okta,thymeleaf,devtools<\/code><\/pre>\n<p>\u0423 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 Maven-\u043f\u0440\u043e\u0435\u043a\u0442. \u0420\u0430\u0441\u043f\u0430\u043a\u0443\u0439\u0442\u0435 \u0435\u0433\u043e \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0430\u0440\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439: <code>thymeleaf-extras-springsecurity5<\/code> \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 Spring Security \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u0445 \u0438 <code>spring-security-test<\/code> \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"java\">&lt;dependency>     &lt;groupId>org.thymeleaf.extras&lt;\/groupId>     &lt;artifactId>thymeleaf-extras-springsecurity5&lt;\/artifactId>     &lt;version>3.0.4.RELEASE&lt;\/version> &lt;\/dependency> &lt;dependency>     &lt;groupId>org.springframework.security&lt;\/groupId>     &lt;artifactId>spring-security-test&lt;\/artifactId>     &lt;scope>test&lt;\/scope> &lt;\/dependency><\/code><\/pre>\n<h3>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e OpenID Connect<\/h3>\n<p>\u0412\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 Okta. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 <a href=\"https:\/\/cli.okta.com\/\"><u>Okta CLI<\/u><\/a> \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 <code>okta register<\/code> \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c, \u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <code>okta login<\/code>. \u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 <code>okta apps create<\/code>.\u00a0<\/p>\n<p>\u0418\u043c\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (Application name) \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u043e \u0432\u0430\u0448\u0435\u043c\u0443 \u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u044e. \u0422\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (Type of Application) \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 <strong>Web<\/strong>. Framework of Application \u2014 <strong>Okta Spring Boot Starter<\/strong>. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 Redirect URI \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0445\u043e\u0434\u0430 (Login Redirect) \u043d\u0430 <code>http:\/\/localhost:8080\/login\/oauth2\/code\/okta<\/code> \u0438 \u0432\u044b\u0445\u043e\u0434\u0430 (Logout Redirect) \u043d\u0430 <code>http:\/\/localhost:8080<\/code>.<\/p>\n<p>Okta CLI \u0441\u043e\u0437\u0434\u0430\u0441\u0442 OIDC Web App \u0432 \u0432\u0430\u0448\u0435\u0439 Okta Org, \u0434\u043e\u0431\u0430\u0432\u0438\u0442 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0432\u0430\u043c\u0438 URI \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u0433\u0440\u0443\u043f\u043f\u0435 Everyone. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 \u044d\u0442\u043e:<\/p>\n<pre><code>Okta application configuration has been written to:    \/path\/to\/app\/src\/main\/resources\/application.properties<\/code><\/pre>\n<p>\u0420\u0435\u043a\u0432\u0438\u0437\u0438\u0442\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>src\/main\/resources\/application.properties<\/code>.<\/p>\n<pre><code>okta.oauth2.issuer=https:\/\/dev-133337.okta.com\/oauth2\/default okta.oauth2.client-id=0oab8eb55Kb9jdMIr5d6 okta.oauth2.client-secret=NEVER-SHOW-SECRETS<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Okta Admin Console. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u0441\u043c. \u0440\u0430\u0437\u0434\u0435\u043b <a href=\"https:\/\/developer.okta.com\/docs\/guides\/sign-into-web-app\/springboot\/create-okta-application\/\"><u>Create a Spring Boot App<\/u><\/a> \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0435\u043c <code>application.properties<\/code> \u0432 <code>application.yml<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:<\/p>\n<pre><code>spring:   thymeleaf:     prefix: file:src\/main\/resources\/templates\/     security:     oauth2:       client:         provider:           okta:             user-name-attribute: email  okta:   oauth2:     issuer: https:\/\/{yourOktaDomain}\/oauth2\/default     client-id: {clientId}     client-secret: {clientSecret}     scopes:       - email       - openid<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043f\u043e\u043a\u0430 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d scope <code>profile<\/code>. \u0414\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 OpenID Connect <a href=\"https:\/\/openid.net\/specs\/openid-connect-basic-1_0.html#Scopes\"><u>\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e openid<\/u><\/a>. \u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>thymeleaf.prefix<\/code> \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0433\u043e\u0440\u044f\u0447\u0443\u044e \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432, \u0435\u0441\u043b\u0438 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c <code>spring-boot-devtools<\/code>.<\/p>\n<h3>\u0428\u0430\u0431\u043b\u043e\u043d\u044b Thymeleaf<\/h3>\n<p>\u0414\u043b\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 <code>src\/main\/resources\/templates<\/code> \u0438 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <code>home.html<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head>     &lt;title>User Details&lt;\/title>     &lt;!--\/*\/ &lt;th:block th:include=\"head :: head\"\/> \/*\/--> &lt;\/head> &lt;body id=\"samples\"> &lt;div th:replace=\"menu :: menu\">&lt;\/div>  &lt;div id=\"content\" class=\"container\">     &lt;h2>Okta Hosted Login + Spring Boot Example&lt;\/h2>      &lt;div th:unless=\"${#authorization.expression('isAuthenticated()')}\" class=\"text fw-light fs-6 lh-1\">         &lt;p>Hello!&lt;\/p>         &lt;p>If you're viewing this page then you have successfully configured and started this example server.&lt;\/p>         &lt;p>This example shows you how to use the &lt;a href=\"https:\/\/github.com\/okta\/okta-spring-boot\">Okta Spring Boot             Starter&lt;\/a> to add the &lt;a             href=\"https:\/\/developer.okta.com\/docs\/guides\/implement-grant-type\/authcode\/main\/\">Authorization             Code Flow&lt;\/a> to your application.&lt;\/p>         &lt;p>When you click the login button below, you will be redirected to the login page on your Okta org. After you             authenticate, you will be returned to this application.&lt;\/p>     &lt;\/div>      &lt;div th:if=\"${#authorization.expression('isAuthenticated()')}\" class=\"text fw-light fs-6 lh-1\">         &lt;p>Welcome home, &lt;span th:text=\"${#authentication.principal.name}\">Joe Coder&lt;\/span>!&lt;\/p>         &lt;p>You have successfully authenticated against your Okta org, and have been redirected back to this            application.&lt;\/p>     &lt;\/div>      &lt;form th:unless=\"${#authorization.expression('isAuthenticated()')}\" method=\"get\"            th:action=\"@{\/oauth2\/authorization\/okta}\">         &lt;button id=\"login-button\" class=\"btn btn-primary\" type=\"submit\">Sign In&lt;\/button>     &lt;\/form>  &lt;\/div> &lt;\/body> &lt;!--\/*\/ &lt;th:block th:include=\"footer :: footer\"\/> \/*\/--> &lt;\/html><\/code><\/pre>\n<p>\u0412 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u043e\u043c \u0432\u044b\u0448\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u0435 \u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0435\u0433 <code>&lt;th:block\/><\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0438 \u043d\u0438\u0436\u043d\u0435\u0433\u043e \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b\u043e\u0432, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0432 <code>header.html<\/code> \u0438 <code>footer.html<\/code>. \u041e\u043d\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 Bootstrap \u0434\u043b\u044f \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432. \u0422\u0430\u043a\u0436\u0435 \u0432\u043c\u0435\u0441\u0442\u043e <code>&lt;div th:replace ...><\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u043c\u0435\u043d\u044e.<\/p>\n<p>\u0423\u0441\u043b\u043e\u0432\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f <code>th:if<\/code> \u0438 <code>th:unless<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d, \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 &#171;<strong>Sign In<\/strong>&#171;. \u0418\u043d\u0430\u0447\u0435 \u2014 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d head.html:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head th:fragment=\"head\">     &lt;meta charset=\"utf-8\"\/>     &lt;meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"\/>     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\/>     &lt;link href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.1.3\/dist\/css\/bootstrap.min.css\" rel=\"stylesheet\"            integrity=\"sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3\" crossorigin=\"anonymous\">     &lt;link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap-icons@1.8.1\/font\/bootstrap-icons.css\"> &lt;\/head> &lt;body> &lt;p>Nothing to see here, move along.&lt;\/p> &lt;\/body> &lt;\/html><\/code><\/pre>\n<p>\u0418 <code>footer.html<\/code>:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head> &lt;\/head> &lt;body> &lt;p>Nothing to see here, move along.&lt;\/p> &lt;\/body> &lt;footer th:fragment=\"footer\">     &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.1.3\/dist\/js\/bootstrap.bundle.min.js\"               integrity=\"sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p\"              crossorigin=\"anonymous\">&lt;\/script> &lt;\/footer> &lt;\/html><\/code><\/pre>\n<p>\u0410 \u0442\u0430\u043a\u0436\u0435 \u0448\u0430\u0431\u043b\u043e\u043d <code>menu.html<\/code> \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430 \u043c\u0435\u043d\u044e:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\">  &lt;body id=\"samples\"> &lt;nav class=\"navbar border mb-4 navbar-expand-lg navbar-light bg-light\" th:fragment=\"menu\">     &lt;div class=\"container-fluid\">         &lt;button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\"                 data-bs-target=\"#navbarSupportedContent\" aria-controls=\"navbarSupportedContent\"                 aria-expanded=\"false\" aria-label=\"Toggle navigation\">             &lt;span class=\"navbar-toggler-icon\">&lt;\/span>         &lt;\/button>         &lt;div class=\"collapse navbar-collapse\" id=\"navbarSupportedContent\">             &lt;ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">                 &lt;li class=\"nav-item\">&lt;a class=\"nav-link\" th:href=\"@{\/}\">Home&lt;\/a>&lt;\/li>             &lt;\/ul>             &lt;form class=\"d-flex\" method=\"post\" th:action=\"@{\/logout}\"                   th:if=\"${#authorization.expression('isAuthenticated()')}\">                 &lt;input class=\"form-control me-2\" type=\"hidden\" th:name=\"${_csrf.parameterName}\"                        th:value=\"${_csrf.token}\"\/>                 &lt;button id=\"logout-button\" type=\"submit\" class=\"btn btn-danger\">Logout&lt;\/button>             &lt;\/form>         &lt;\/div>     &lt;\/div> &lt;\/nav> &lt;\/body> &lt;\/html><\/code><\/pre>\n<h3>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440<\/h3>\n<p>\u0414\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 <code>home<\/code> \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>com.okta.developer.demo<\/code> \u043a\u043b\u0430\u0441\u0441 <code>HomeController<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code class=\"java\">package com.okta.developer.demo;  import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.reactive.result.view.Rendering; import reactor.core.publisher.Mono;  import java.util.List; import java.util.stream.Collectors;  @Controller public class HomeController {      private static Logger logger = LoggerFactory.getLogger(HomeController.class);      @GetMapping(\"\/\")     public Mono&lt;Rendering> home(Authentication authentication) {         List&lt;String> authorities = authentication.getAuthorities()                 .stream()                 .map(scope -> scope.toString())                 .collect(Collectors.toList());         return Mono.just(Rendering.view(\"home\").modelAttribute(\"authorities\", authorities).build());     } }<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>home<\/code> \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0432 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f (authorities) \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438<\/h3>\n<p>Okta-\u0441\u0442\u0430\u0440\u0442\u0435\u0440 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a\u043e \u0432\u0441\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c. \u041d\u0430\u043c \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043a\u043b\u0430\u0441\u0441 <code>SecurityConfiguration<\/code> \u0432 \u0442\u043e\u0442 \u0436\u0435 \u043f\u0430\u043a\u0435\u0442, \u0447\u0442\u043e \u0438 \u0440\u0430\u043d\u044c\u0448\u0435.<\/p>\n<pre><code class=\"java\">package com.okta.developer.demo;  import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler; import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;  import java.net.URI;  @EnableWebFluxSecurity @EnableReactiveMethodSecurity public class SecurityConfiguration {      @Bean     public ServerLogoutSuccessHandler logoutSuccessHandler(){         RedirectServerLogoutSuccessHandler handler = new RedirectServerLogoutSuccessHandler();         handler.setLogoutSuccessUrl(URI.create(\"\/\"));         return handler;     }      @Bean     public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {         http             .authorizeExchange().pathMatchers(\"\/\").permitAll().and().anonymous()             .and().authorizeExchange().anyExchange().authenticated()             .and().oauth2Client()             .and().oauth2Login()             .and().logout().logoutSuccessHandler(logoutSuccessHandler());          return http.build();     } }<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0432\u0441\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043a \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 (\/), \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043c\u043e\u0433\u043b\u0438 \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0438\u0442\u044c\u0441\u044f.<\/p>\n<h3>\u0417\u0430\u043f\u0443\u0441\u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h3>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Maven:<\/p>\n<p><code>.\/mvnw spring-boot:run<\/code><\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"http:\/\/localhost:8080\"><u>http:\/\/localhost:8080<\/u><\/a> \u2014 \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 home \u0438 \u043a\u043d\u043e\u043f\u043a\u0443 &#171;<strong>Sign In<\/strong>&#171;. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u0438 \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u044c\u0442\u0435\u0441\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 Okta. \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u0430 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 home \u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d23\/5be\/da3\/d235beda36c87d7fafc9d846b0633df9.png\" width=\"1000\" height=\"250\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d23\/5be\/da3\/d235beda36c87d7fafc9d846b0633df9.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>\u0417\u0430\u0449\u0438\u0442\u0430 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0448\u0430\u0431\u043b\u043e\u043d <code>userProfile.html<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e <code>claim<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445\u0441\u044f \u0432 ID \u0442\u043e\u043a\u0435\u043d\u0435, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u043e\u043c Okta, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f (authorities), \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 Spring Security \u043e\u0442 \u0442\u043e\u043a\u0435\u043d\u0430.<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head>     &lt;title>User Details&lt;\/title>     &lt;!--\/*\/ &lt;th:block th:include=\"head :: head\"\/> \/*\/--> &lt;\/head> &lt;body id=\"samples\"> &lt;div th:replace=\"menu :: menu\">&lt;\/div>  &lt;div id=\"content\" class=\"container\">      &lt;div>         &lt;h2>My Profile&lt;\/h2>         &lt;p>Hello, &lt;span th:text=\"${#authentication.principal.attributes['name']}\">Joe Coder&lt;\/span>. Below is the              information that was read with your &lt;a              href=\"https:\/\/developer.okta.com\/docs\/api\/resources\/oidc.html#get-user-information\">ID Token&lt;\/a>.         &lt;\/p>         &lt;p>This route is protected with the annotation &lt;code>@PreAuthorize(\"hasAuthority('SCOPE_profile')\")&lt;\/code>,              which will ensure that this page cannot be accessed until you have authenticated, and have the scope &lt;code>profile&lt;\/code>.&lt;\/p>     &lt;\/div>     &lt;table class=\"table table-striped\">         &lt;thead>         &lt;tr>             &lt;th>Claim&lt;\/th>             &lt;th>Value&lt;\/th>         &lt;\/tr>         &lt;\/thead>         &lt;tbody>         &lt;tr th:each=\"item : ${details}\">               &lt;td th:text=\"${item.key}\">Key&lt;\/td>               &lt;td th:id=\"${'claim-' + item.key}\" th:text=\"${item.value}\">Value&lt;\/td>           &lt;\/tr>         &lt;\/tbody>     &lt;\/table>      &lt;table class=\"table table-striped\">         &lt;thead>         &lt;tr>             &lt;th>Spring Security Authorities&lt;\/th>         &lt;\/tr>         &lt;\/thead>         &lt;tbody>         &lt;tr th:each=\"scope : ${#authentication.authorities}\">               &lt;td>&lt;code th:text=\"${scope}\">Authority&lt;\/code>&lt;\/td>         &lt;\/tr>         &lt;\/tbody>     &lt;\/table>  &lt;\/div> &lt;\/body> &lt;!--\/*\/ &lt;th:block th:include=\"footer :: footer\"\/> \/*\/--> &lt;\/html><\/code><\/pre>\n<p>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0432 <code>HomeController<\/code> \u043c\u0430\u043f\u043f\u0438\u043d\u0433:<\/p>\n<pre><code class=\"java\">@GetMapping(\"\/profile\") @PreAuthorize(\"hasAuthority('SCOPE_profile')\") public Mono&lt;Rendering> userDetails(OAuth2AuthenticationToken authentication) {     return Mono.just(Rendering.view(\"userProfile\")         .modelAttribute(\"details\", authentication.getPrincipal().getAttributes())         .build()); }<\/code><\/pre>\n<p>\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f <code>@PreAuthorize<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e SpEL (Spring Expression Language). \u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u0430. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0441 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f\u043c\u0438 <code>SCOPE_profile<\/code> \u0441\u043c\u043e\u0433\u0443\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 <code>userProfile<\/code>. \u042d\u0442\u043e \u0437\u0430\u0449\u0438\u0442\u0430 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<p>\u041d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0435 <code>home.html<\/code> \u0441\u0441\u044b\u043b\u043a\u0443 \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 <code>userProfile<\/code> \u043f\u043e\u0441\u043b\u0435 &#171;You successfully \u2026&#187;. \u0421\u0441\u044b\u043b\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0441 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f\u043c\u0438 (authority) <code>SCOPE_profile<\/code>.<\/p>\n<pre><code>&lt;p>You have successfully authenticated against your Okta org, and have been redirected back to this application.&lt;\/p> &lt;p th:if=\"${#lists.contains(authorities, 'SCOPE_profile')}\">Visit the &lt;a th:href=\"@{\/profile}\">My Profile&lt;\/a> page in this application to view the information retrieved with your OAuth Access Token.&lt;\/p><\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e\u00a0 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0442\u0430\u043a \u043a\u0430\u043a \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432\u0440\u043e\u0434\u0435 <code>${#authorization.expression('hasRole(''SCOPE_profile'')')}<\/code> \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0432 WebFlux \u0438\u0437-\u0437\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0432 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u043c Spring Security (Spring Security 5.6). \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438: <code>[isAuthenticated(), isFullyAuthenticated(), isAnonymous(), isRememberMe()]<\/code>.<\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437. \u041f\u043e\u0441\u043b\u0435 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432\u044b \u043d\u0435 \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u043d\u043e\u0432\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443, \u043d\u043e \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u0442\u0435 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/localhost:8080\/profile<\/code>, \u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 HTTP ERROR 403 Forbidden \u2014 \u0434\u043e\u0441\u0442\u0443\u043f \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d. \u042d\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u0432 <code>application.yml<\/code> \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 scope \u0434\u043b\u044f <code>email<\/code> \u0438 <code>openid<\/code>, \u0430 profile \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043a\u0435\u043d\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (access token). \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 scope \u0432 <code>application.yml<\/code>, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>userProfile<\/code> \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u0442\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/68e\/c38\/ded\/68ec38deda4de1c94f7e77177c41b713.png\" width=\"942\" height=\"1418\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/68e\/c38\/ded\/68ec38deda4de1c94f7e77177c41b713.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, Spring Security \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0433\u0440\u0443\u043f\u043f\u044b, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435\u0441\u044f \u0432 <code>claim<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0435 scope \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u0439 (authorities). \u0423 scope \u043f\u0440\u0435\u0444\u0438\u043a\u0441 <code>SCOPE_<\/code>. \u041f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Okta CLI \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0433\u0440\u0443\u043f\u043f\u044b <code>ROLE_ADMIN<\/code> \u0438 <code>ROLE_USER<\/code>, \u0438 \u0432\u0430\u0448\u0430 \u0443\u0447\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u044d\u0442\u0438 \u0433\u0440\u0443\u043f\u043f\u044b.<\/p>\n<h2>\u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 CSRF-\u0430\u0442\u0430\u043a<\/h2>\n<p>\u0410\u0442\u0430\u043a\u0430 CSRF (Cross-site request forgery, \u043c\u0435\u0436\u0441\u0430\u0439\u0442\u043e\u0432\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u043b\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430) \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0444\u043e\u0440\u043c\u044b \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0437\u043b\u043e\u0443\u043c\u044b\u0448\u043b\u0435\u043d\u043d\u0438\u043a\u0430 \u043d\u0430 \u0441\u0430\u0439\u0442-\u0436\u0435\u0440\u0442\u0432\u0443, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d, \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043e\u0442 \u043b\u0438\u0446\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432\u0440\u0435\u0434\u043e\u043d\u043e\u0441\u043d\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f.<\/p>\n<p>\u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 CSRF\u00a0\u0432 Spring Security \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043a\u0430\u043a \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u043b\u0435\u0442-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f WebFlux. \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0437\u0430\u0449\u0438\u0442\u044b \u2014 <a href=\"https:\/\/docs.spring.io\/spring-security\/reference\/features\/exploits\/csrf.html#csrf-protection-stp\"><u>Synchronizer Token Pattern<\/u><\/a>. \u0412 \u043a\u0430\u0436\u0434\u044b\u0439 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u2014 CSRF-\u0442\u043e\u043a\u0435\u043d. \u0422\u043e\u043a\u0435\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0447\u0430\u0441\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043d\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HTTP-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0438\u043b\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0437\u0430\u0449\u0438\u0442\u0443 \u043e\u0442 CSRF, \u0441\u043e\u0437\u0434\u0430\u0432 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u043e\u043f\u0440\u043e\u0441\u043e\u0432. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d <code>quiz.html<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head>     &lt;title>Thymeleaf Quiz&lt;\/title>     &lt;!--\/*\/ &lt;th:block th:include=\"head :: head\"\/> \/*\/--> &lt;\/head> &lt;body id=\"samples\"> &lt;div th:replace=\"menu :: menu\">&lt;\/div>  &lt;div id=\"content\" class=\"container\">     &lt;div>         &lt;h2>Select the right answer&lt;\/h2>     &lt;\/div>     &lt;form action=\"#\" th:action=\"@{\/quiz}\" th:object=\"${quiz}\"           method=\"post\" class=\"col-md-4 fw-light\">         &lt;ul>             &lt;li th:errors=\"*{answer}\" \/>         &lt;\/ul>         &lt;div class=\"col-md-12\">             &lt;h3>What is Thymeleaf?&lt;\/h3>         &lt;\/div>         &lt;div class=\"col-md-12 form-check\">             &lt;input class=\"form-check-input\" type=\"radio\" th:field=\"*{answer}\" value=\"A\" id=\"check-1-1\"\/>             &lt;label class=\"form-check-label\" for=\"check-1-1\">                 &lt;strong>A.&lt;\/strong> A server-side Java template engine             &lt;\/label>         &lt;\/div>         &lt;div class=\"col-md-12 form-check\">             &lt;input class=\"form-check-input\" type=\"radio\" th:field=\"*{answer}\" value=\"B\" id=\"check-1-2\"\/>             &lt;label class=\"form-check-label\" for=\"check-1-2\">                 &lt;strong>B.&lt;\/strong> A markup language             &lt;\/label>         &lt;\/div>         &lt;div class=\"col-md-12 form-check\">             &lt;input class=\"form-check-input\" type=\"radio\" th:field=\"*{answer}\" value=\"C\" id=\"check-1-3\"\/>             &lt;label class=\"form-check-label\" for=\"check-1-3\">                 &lt;strong>C.&lt;\/strong> A web framework             &lt;\/label>         &lt;\/div>         &lt;div class=\"col-md-12 mt-4 mb-4\">             &lt;p>Your CSRF token is: &lt;span th:text=\"${_csrf.token}\"\/>&lt;\/p>         &lt;\/div>         &lt;div class=\"col-md-12\">             &lt;button type=\"submit\" class=\"btn btn-primary\">Submit&lt;\/button>         &lt;\/div>     &lt;\/form> &lt;\/div> &lt;\/body> &lt;!--\/*\/ &lt;th:block th:include=\"footer :: footer\"\/> \/*\/--> &lt;\/html><\/code><\/pre>\n<p>\u0422\u043e\u043a\u0435\u043d CSRF \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0432 \u0443\u0447\u0435\u0431\u043d\u044b\u0445 \u0446\u0435\u043b\u044f\u0445 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u043c \u0435\u0433\u043e \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0435 <code>quiz.html<\/code>.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d <code>result.html<\/code> \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u043e\u043f\u0440\u043e\u0441\u0430:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head>     &lt;title>Thymeleaf Quiz Submission&lt;\/title>     &lt;!--\/*\/ &lt;th:block th:include=\"head :: head\"\/> \/*\/--> &lt;\/head> &lt;body id=\"samples\"> &lt;div th:replace=\"menu :: menu\">&lt;\/div> &lt;div class=\"container\" id=\"content\">     &lt;div class=\"text-center\">         &lt;i class=\"bi-balloon-heart-fill\" style=\"font-size: 6rem; color: green;\" th:if=${quiz.answer=='A'}>&lt;\/i>         &lt;i class=\"bi-x-circle-fill\" style=\"font-size: 6rem; color: red;\" th:unless=${quiz.answer=='A'}>&lt;\/i>         &lt;div class=\"panel mt-4 text-center\">             &lt;div class=\"panel-body\">                 &lt;h4>Your selected answer is &lt;strong>                     &lt;span th:text=\"${quiz.answer}\">&lt;\/span>                 &lt;\/strong>&lt;\/h4>                 &lt;p th:if=${quiz.answer=='A'}>Good Job!&lt;\/p>             &lt;\/div>         &lt;\/div>         &lt;div class=\"panel mt-4 text-center\" th:unless=${quiz.answer=='A'}>             &lt;div class=\"panel-body\">                 &lt;p>It is not the right answer&lt;\/p>                 &lt;p>&lt;a th:href=\"@{\/quiz}\">Try again!&lt;\/a>&lt;\/p>             &lt;\/div>         &lt;\/div>     &lt;\/div> &lt;\/div> &lt;\/body> &lt;!--\/*\/ &lt;th:block th:include=\"footer :: footer\"\/> \/*\/--> &lt;\/html><\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043a\u043b\u0430\u0441\u0441 <code>QuizSubmission<\/code> \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u0430:<\/p>\n<pre><code class=\"java\">package com.okta.developer.demo;  public class QuizSubmission {      private String answer;      public String getAnswer() {         return answer;     }      public void setAnswer(String answer) {         this.answer = answer;     } }<\/code><\/pre>\n<p>\u0418 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 <code>QuizController<\/code> \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043e\u043f\u0440\u043e\u0441\u0430 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u044b:<\/p>\n<pre><code class=\"java\">package com.okta.developer.demo;  import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.reactive.result.view.Rendering; import reactor.core.publisher.Mono;  @Controller public class QuizController {      private static Logger logger = LoggerFactory.getLogger(QuizController.class);      @GetMapping(\"\/quiz\")     @PreAuthorize(\"hasAuthority('SCOPE_quiz')\")     public Mono&lt;Rendering> showQuiz() {         return Mono.just(Rendering.view(\"quiz\").modelAttribute(\"quiz\", new QuizSubmission()).build());     }      @PostMapping(path = \"\/quiz\", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})     @PreAuthorize(\"hasAuthority('SCOPE_quiz')\")     public Mono&lt;Rendering> saveQuiz(QuizSubmission quizSubmission) {         return Mono.just(Rendering.view(\"result\").modelAttribute(\"quiz\", quizSubmission).build());     } }<\/code><\/pre>\n<p>\u0412 \u043d\u043e\u0432\u043e\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 \u0438 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u0445 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043e\u043f\u0440\u043e\u0441\u0443 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0441 \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u044f\u043c\u0438 <code>SCOPE_quiz<\/code>. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u0432 \u0448\u0430\u0431\u043b\u043e\u043d <code>home.html<\/code> \u043f\u043e\u0441\u043b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u043f\u0440\u043e\u0444\u0438\u043b\u044c:<\/p>\n<pre><code>&lt;p>You have successfully authenticated against your Okta org, and have been redirected back to this application.&lt;\/p> &lt;p th:if=\"${#lists.contains(authorities, 'SCOPE_profile')}\">Visit the &lt;a th:href=\"@{\/profile}\">My Profile&lt;\/a> page in this application to view the information retrieved with your OAuth Access Token.&lt;\/p> &lt;p th:if=\"${#lists.contains(authorities, 'SCOPE_quiz')}\">Visit the &lt;a th:href=\"@{\/quiz}\">Thymeleaf Quiz&lt;\/a> to test Cross-Site Request Forgery (CSRF) protection.&lt;\/p><\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0437\u0430\u0449\u0438\u0442\u0443 \u043e\u0442 CSRF \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u0435\u0441\u0442\u0430. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 <code>QuizControllerTest<\/code> \u0432 <code>src\/test\/java<\/code> \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>com.okta.developer.demo<\/code>:<\/p>\n<pre><code class=\"java\">package com.okta.developer.demo;  import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; import org.springframework.http.MediaType; import org.springframework.test.web.reactive.server.WebTestClient;  import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOidcLogin;  @WebFluxTest public class QuizControllerTest {      @Autowired     private WebTestClient client;      @Test     void testPostQuiz_noCSRFToken() throws Exception {         QuizSubmission quizSubmission = new QuizSubmission();         this.client.mutateWith(mockOidcLogin())                 .post().uri(\"\/quiz\")                 .exchange()                 .expectStatus().isForbidden()                 .expectBody().returnResult()                 .toString().contains(\"An expected CSRF token cannot be found\");     }      @Test     void testPostQuiz() throws Exception {         this.client.mutateWith(csrf()).mutateWith(mockOidcLogin())                 .post().uri(\"\/quiz\")                 .contentType(MediaType.APPLICATION_FORM_URLENCODED)                 .exchange().expectStatus().isOk();     }      @Test     void testGetQuiz_noAuth() throws Exception {         this.client.get().uri(\"\/quiz\").exchange().expectStatus().is3xxRedirection();     }      @Test     void testGetQuiz() throws Exception {         this.client.mutateWith(mockOidcLogin())                 .get().uri(\"\/quiz\").exchange().expectStatus().isOk();     } }<\/code><\/pre>\n<p>\u0422\u0435\u0441\u0442 <code>testPostQuiz_noCSRFToken()<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043e\u043f\u0440\u043e\u0441 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u0431\u0435\u0437 CSRF-\u0442\u043e\u043a\u0435\u043d\u0430, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d. \u0412\u0442\u043e\u0440\u043e\u0439 \u0442\u0435\u0441\u0442 <code>testPostQuiz()<\/code> \u2014 \u0442\u043e\u043a\u0435\u043d CSRF \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a \u0444\u0438\u043a\u0442\u0438\u0432\u043d\u043e\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>mutateWith(csrf())<\/code>. \u0417\u0434\u0435\u0441\u044c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 \u2014 HTTP 200 OK. \u0422\u0440\u0435\u0442\u0438\u0439 \u0442\u0435\u0441\u0442 <code>testGetQuiz_noAuth()<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d (\u0432 \u0444\u043e\u0440\u043c\u0443 \u0432\u0445\u043e\u0434\u0430 Okta), \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d. \u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0442\u0435\u0441\u0442 <code>testGetQuiz()<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043e\u043f\u0440\u043e\u0441\u0443, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e OIDC.<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 <code>quiz<\/code> \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c scope \u0438\u043b\u0438 scope, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u0432 Okta, \u0432\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0435\u0435 \u0434\u043b\u044f default-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u0434 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 Okta Admin Console \u0432 \u043c\u0435\u043d\u044e <strong>Security > API<\/strong>, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 default. \u041d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 <strong>Scopes<\/strong> \u043d\u0430\u0436\u043c\u0438\u0442\u0435 <strong>Add Scope<\/strong>. \u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f (Name) quiz \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 (Display phrase). \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 <strong>Create<\/strong>. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438 \u043b\u043e\u0433\u0438\u043d\u0435 \u0447\u0435\u0440\u0435\u0437 OIDC \u043c\u043e\u0436\u043d\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c scope <code>quiz<\/code>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/576\/296\/42e\/57629642e9f2a791c58f6abc086ce6a9.png\" width=\"695\" height=\"624\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/576\/296\/42e\/57629642e9f2a791c58f6abc086ce6a9.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f scope <code>quiz<\/code> \u0432 <code>application.yml<\/code>, \u0438 \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u2014 \u0432\u044b \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0442\u0435\u0441\u0442. \u0415\u0441\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c GET-\u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/localhost:8080\/quiz<\/code>, \u0442\u043e \u043e\u0442\u0432\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 403 Forbidden.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 <code>quiz<\/code> \u0432 \u0441\u043f\u0438\u0441\u043e\u043a scopes \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 Okta \u0432 <code>application.yml<\/code>. \u041e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code>spring:   security:     oauth2:       client:         provider:           okta:             user-name-attribute: email  okta:   oauth2:     issuer: https:\/\/{yourOktaDomain}\/oauth2\/default     client-id: {clientId}     client-secret: {clientSecret}     scopes:       - email       - openid       - profile       - quiz<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437. \u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 &#171;Visit the <strong>Thymeleaf Quiz<\/strong> to test Cross-Site Request Forgery (CSRF) protection&#187;. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 \u0441\u0441\u044b\u043b\u043a\u0443 \u2014 \u0432\u044b \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u0442\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 quiz:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/718\/55a\/614\/71855a614b1431dfb98ced360cc5cc30.png\" width=\"1117\" height=\"720\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/718\/55a\/614\/71855a614b1431dfb98ced360cc5cc30.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Spring Security\u00a0 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 CSRF-\u0442\u043e\u043a\u0435\u043d \u0432 \u0444\u043e\u0440\u043c\u0443 \u0432 \u0432\u0438\u0434\u0435 \u0441\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 <code>&lt;input type=\"hidden\" name=\"_csrf\" value=\"...\"><\/code>.<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e HTTPie \u0438 \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f \u0435\u0449\u0435 \u0440\u0430\u0437, \u0447\u0442\u043e CSRF-\u0437\u0430\u0449\u0438\u0442\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<pre><code>$ http POST http:\/\/localhost:8080\/  HTTP\/1.1 403 Forbidden Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: text\/plain Expires: 0 Pragma: no-cache Referrer-Policy: no-referrer X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1 ; mode=block content-length: 38  An expected CSRF token cannot be found<\/code><\/pre>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0444\u0430\u043a\u0442 \u2014 CSRF-\u0437\u0430\u0449\u0438\u0442\u0430 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u043d\u0435\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 Spring Security.<\/p>\n<h4>\u0411\u043e\u043b\u044c\u0448\u0435 \u043e Spring Boot \u0438 Spring Security<\/h4>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0432\u0430\u043c \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u043e\u0441\u044c \u044d\u0442\u043e \u043a\u0440\u0430\u0442\u043a\u043e\u0435 \u0432\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0432 Thymeleaf \u0438 \u0432\u044b \u0443\u0437\u043d\u0430\u043b\u0438, \u043a\u0430\u043a \u0437\u0430\u0449\u0438\u0442\u0438\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Spring Security. \u0412\u044b \u0442\u0430\u043a\u0436\u0435 \u0443\u0431\u0435\u0434\u0438\u043b\u0438\u0441\u044c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043b\u0435\u0433\u043a\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c OIDC-\u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Okta. \u0423\u0437\u043d\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043e Spring Boot Security \u0438 OIDC \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044f\u0445:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/developer.okta.com\/blog\/2021\/10\/04\/spring-boot-spa\"><u>Learn How to Build a Single-Page App with Vue and Spring Boot<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/developer.okta.com\/blog\/2021\/06\/01\/kubernetes-spring-boot-jhipster\"><u>Kubernetes to the Cloud with Spring Boot and JHipster<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/developer.okta.com\/blog\/2021\/09\/16\/spring-native-okta-starter\"><u>Spring Native in Action with the Okta Spring Boot Starter<\/u><\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 <a href=\"https:\/\/github.com\/oktadev\/okta-thymeleaf-security-example\"><u>GitHub<\/u><\/a>.<\/p>\n<hr\/>\n<p>\u0412\u0441\u0435\u0445, \u0434\u043e\u0447\u0438\u0442\u0430\u0432\u0448\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044e \u0434\u043e \u043a\u043e\u043d\u0446\u0430, \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0435\u043c \u043d\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0435 \u0437\u0430\u043d\u044f\u0442\u0438\u0435 \u00abValidation Framework \u0432 Spring\u00bb. \u041d\u0430 \u0437\u0430\u043d\u044f\u0442\u0438\u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c javax.validation, \u0432 Spring \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0441 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044f\u043c\u0438. \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u2014 <a href=\"https:\/\/otus.pw\/0bg6\/\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/otus\/blog\/665952\/\"> https:\/\/habr.com\/ru\/company\/otus\/blog\/665952\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>Thymeleaf \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0434\u0430\u0432\u043d\u043e, \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c 10 \u043b\u0435\u0442 \u043d\u0430\u0437\u0430\u0434, \u043d\u043e \u043e\u043d \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u0432\u0435\u0441\u044c\u043c\u0430 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u0435\u043d \u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f. \u0428\u0430\u0431\u043b\u043e\u043d\u044b Thymeleaf \u0443\u0434\u043e\u0431\u043d\u044b \u0442\u0435\u043c, \u0447\u0442\u043e \u043f\u0440\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043e\u043d\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0435 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0438 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Spring WebFlux \u0441 Thymeleaf, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 Okta OIDC, \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 CSRF-\u0430\u0442\u0430\u043a \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u043c\u043e\u0447\u0438\u0439.<\/p>\n<p>\u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/httpie.io\/\"><u>HTTPie 3.0.2<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/jdk.java.net\/java-se-ri\/11\"><u>Java 11<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/cli.okta.com\/\"><u>Okta CLI 0.10.0<\/u><\/a><\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Thymeleaf?<\/h3>\n<p>Thymeleaf \u2014 \u044d\u0442\u043e \u043e\u043f\u0435\u043d\u0441\u043e\u0440\u0441\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043a\u0430\u043a \u0432\u0435\u0431, \u0442\u0430\u043a \u0438 \u0434\u0440\u0443\u0433\u0438\u0445, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0414\u0430\u043d\u0438\u044d\u043b\u0435\u043c \u0424\u0435\u0440\u043d\u0430\u043d\u0434\u0435\u0441\u043e\u043c (Daniel Fern\u00e1ndez). \u0428\u0430\u0431\u043b\u043e\u043d\u044b \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 HTML \u0438 \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u043e Spring MVC, Spring Security \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u043c\u0438. \u0412 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0435\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u043e Spring WebFlux, \u043d\u043e \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0431 \u044d\u0442\u043e\u043c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u0430\u043b\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438. Thymeleaf-\u0441\u0442\u0430\u0440\u0442\u0435\u0440 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 <a href=\"https:\/\/docs.spring.io\/spring-framework\/docs\/current\/reference\/html\/web-reactive.html#webflux-view-thymeleaf\"><u>\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443<\/u><\/a> template engine, template resolver \u0438 reactive view resolver.\u00a0<\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 Thymeleaf \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u0432 \u0441\u0435\u0431\u044f:<\/p>\n<ul>\n<li>\n<p>\u0420\u0430\u0431\u043e\u0442\u0443 \u0441 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430\u043c\u0438: \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u0438 \u0448\u0430\u0431\u043b\u043e\u043d\u0430. \u041c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u0440\u0438 \u043e\u0442\u0432\u0435\u0442\u0435 \u043d\u0430 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0422\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c &#171;\u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442&#187;: \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437\u043d\u044b\u0445 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0444\u043e\u0440\u043c \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432-\u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 \u043f\u043e\u043b\u044f \u0444\u043e\u0440\u043c\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044f\u0437\u044b\u043a\u0430 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439 Thymeleaf <a href=\"https:\/\/www.thymeleaf.org\/doc\/tutorials\/3.0\/usingthymeleaf.html#standard-expression-syntax\"><u>Standard Expression Syntax<\/u><\/a>.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0446\u0438\u043a\u043b\u043e\u0432 \u0438 \u0443\u0441\u043b\u043e\u0432\u043d\u044b\u0445 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0439.<\/p>\n<\/li>\n<\/ul>\n<h3>Spring WebFlux-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 Thymeleaf<\/h3>\n<p>\u041c\u044b \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u043d\u043e\u0435 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Spring Boot \u0441 Thymeleaf. \u0417\u0430\u0433\u043e\u0442\u043e\u0432\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <a href=\"https:\/\/start.spring.io\/\"><u>Spring Initializr<\/u><\/a> \u0438\u043b\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b HTTPie:<\/p>\n<pre><code>https -d start.spring.io\/starter.zip bootVersion==2.6.4 \\   baseDir==thymeleaf-security \\   groupId==com.okta.developer.thymeleaf-security \\   artifactId==thymeleaf-security \\   name==thymeleaf-security \\   packageName==com.okta.developer.demo \\   javaVersion==11 \\   dependencies==webflux,okta,thymeleaf,devtools<\/code><\/pre>\n<p>\u0423 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 Maven-\u043f\u0440\u043e\u0435\u043a\u0442. \u0420\u0430\u0441\u043f\u0430\u043a\u0443\u0439\u0442\u0435 \u0435\u0433\u043e \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0430\u0440\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439: <code>thymeleaf-extras-springsecurity5<\/code> \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 Spring Security \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u0445 \u0438 <code>spring-security-test<\/code> \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"java\">&lt;dependency>     &lt;groupId>org.thymeleaf.extras&lt;\/groupId>     &lt;artifactId>thymeleaf-extras-springsecurity5&lt;\/artifactId>     &lt;version>3.0.4.RELEASE&lt;\/version> &lt;\/dependency> &lt;dependency>     &lt;groupId>org.springframework.security&lt;\/groupId>     &lt;artifactId>spring-security-test&lt;\/artifactId>     &lt;scope>test&lt;\/scope> &lt;\/dependency><\/code><\/pre>\n<h3>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e OpenID Connect<\/h3>\n<p>\u0412\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 Okta. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 <a href=\"https:\/\/cli.okta.com\/\"><u>Okta CLI<\/u><\/a> \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 <code>okta register<\/code> \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c, \u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <code>okta login<\/code>. \u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 <code>okta apps create<\/code>.\u00a0<\/p>\n<p>\u0418\u043c\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (Application name) \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u043e \u0432\u0430\u0448\u0435\u043c\u0443 \u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u044e. \u0422\u0438\u043f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (Type of Application) \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 <strong>Web<\/strong>. Framework of Application \u2014 <strong>Okta Spring Boot Starter<\/strong>. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 Redirect URI \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0445\u043e\u0434\u0430 (Login Redirect) \u043d\u0430 <code>http:\/\/localhost:8080\/login\/oauth2\/code\/okta<\/code> \u0438 \u0432\u044b\u0445\u043e\u0434\u0430 (Logout Redirect) \u043d\u0430 <code>http:\/\/localhost:8080<\/code>.<\/p>\n<p>Okta CLI \u0441\u043e\u0437\u0434\u0430\u0441\u0442 OIDC Web App \u0432 \u0432\u0430\u0448\u0435\u0439 Okta Org, \u0434\u043e\u0431\u0430\u0432\u0438\u0442 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0432\u0430\u043c\u0438 URI \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u0433\u0440\u0443\u043f\u043f\u0435 Everyone. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043d\u0430 \u044d\u0442\u043e:<\/p>\n<pre><code>Okta application configuration has been written to:    \/path\/to\/app\/src\/main\/resources\/application.properties<\/code><\/pre>\n<p>\u0420\u0435\u043a\u0432\u0438\u0437\u0438\u0442\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>src\/main\/resources\/application.properties<\/code>.<\/p>\n<pre><code>okta.oauth2.issuer=https:\/\/dev-133337.okta.com\/oauth2\/default okta.oauth2.client-id=0oab8eb55Kb9jdMIr5d6 okta.oauth2.client-secret=NEVER-SHOW-SECRETS<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Okta Admin Console. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u0441\u043c. \u0440\u0430\u0437\u0434\u0435\u043b <a href=\"https:\/\/developer.okta.com\/docs\/guides\/sign-into-web-app\/springboot\/create-okta-application\/\"><u>Create a Spring Boot App<\/u><\/a> \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0435\u043c <code>application.properties<\/code> \u0432 <code>application.yml<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:<\/p>\n<pre><code>spring:   thymeleaf:     prefix: file:src\/main\/resources\/templates\/     security:     oauth2:       client:         provider:           okta:             user-name-attribute: email  okta:   oauth2:     issuer: https:\/\/{yourOktaDomain}\/oauth2\/default     client-id: {clientId}     client-secret: {clientSecret}     scopes:       - email       - openid<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043f\u043e\u043a\u0430 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d scope <code>profile<\/code>. \u0414\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 OpenID Connect <a href=\"https:\/\/openid.net\/specs\/openid-connect-basic-1_0.html#Scopes\"><u>\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e openid<\/u><\/a>. \u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>thymeleaf.prefix<\/code> \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0433\u043e\u0440\u044f\u0447\u0443\u044e \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432, \u0435\u0441\u043b\u0438 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c <code>spring-boot-devtools<\/code>.<\/p>\n<h3>\u0428\u0430\u0431\u043b\u043e\u043d\u044b Thymeleaf<\/h3>\n<p>\u0414\u043b\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 <code>src\/main\/resources\/templates<\/code> \u0438 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <code>home.html<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head>     &lt;title>User Details&lt;\/title>     &lt;!--\/*\/ &lt;th:block th:include=\"head :: head\"\/> \/*\/--> &lt;\/head> &lt;body id=\"samples\"> &lt;div th:replace=\"menu :: menu\">&lt;\/div>  &lt;div id=\"content\" class=\"container\">     &lt;h2>Okta Hosted Login + Spring Boot Example&lt;\/h2>      &lt;div th:unless=\"${#authorization.expression('isAuthenticated()')}\" class=\"text fw-light fs-6 lh-1\">         &lt;p>Hello!&lt;\/p>         &lt;p>If you're viewing this page then you have successfully configured and started this example server.&lt;\/p>         &lt;p>This example shows you how to use the &lt;a href=\"https:\/\/github.com\/okta\/okta-spring-boot\">Okta Spring Boot             Starter&lt;\/a> to add the &lt;a             href=\"https:\/\/developer.okta.com\/docs\/guides\/implement-grant-type\/authcode\/main\/\">Authorization             Code Flow&lt;\/a> to your application.&lt;\/p>         &lt;p>When you click the login button below, you will be redirected to the login page on your Okta org. After you             authenticate, you will be returned to this application.&lt;\/p>     &lt;\/div>      &lt;div th:if=\"${#authorization.expression('isAuthenticated()')}\" class=\"text fw-light fs-6 lh-1\">         &lt;p>Welcome home, &lt;span th:text=\"${#authentication.principal.name}\">Joe Coder&lt;\/span>!&lt;\/p>         &lt;p>You have successfully authenticated against your Okta org, and have been redirected back to this            application.&lt;\/p>     &lt;\/div>      &lt;form th:unless=\"${#authorization.expression('isAuthenticated()')}\" method=\"get\"            th:action=\"@{\/oauth2\/authorization\/okta}\">         &lt;button id=\"login-button\" class=\"btn btn-primary\" type=\"submit\">Sign In&lt;\/button>     &lt;\/form>  &lt;\/div> &lt;\/body> &lt;!--\/*\/ &lt;th:block th:include=\"footer :: footer\"\/> \/*\/--> &lt;\/html><\/code><\/pre>\n<p>\u0412 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u043e\u043c \u0432\u044b\u0448\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u0435 \u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0435\u0433 <code>&lt;th:block\/><\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0438 \u043d\u0438\u0436\u043d\u0435\u0433\u043e \u043a\u043e\u043b\u043e\u043d\u0442\u0438\u0442\u0443\u043b\u043e\u0432, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0432 <code>header.html<\/code> \u0438 <code>footer.html<\/code>. \u041e\u043d\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 Bootstrap \u0434\u043b\u044f \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432. \u0422\u0430\u043a\u0436\u0435 \u0432\u043c\u0435\u0441\u0442\u043e <code>&lt;div th:replace ...><\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u043c\u0435\u043d\u044e.<\/p>\n<p>\u0423\u0441\u043b\u043e\u0432\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f <code>th:if<\/code> \u0438 <code>th:unless<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d, \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 &#171;<strong>Sign In<\/strong>&#171;. \u0418\u043d\u0430\u0447\u0435 \u2014 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0448\u0430\u0431\u043b\u043e\u043d head.html:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head th:fragment=\"head\">     &lt;meta charset=\"utf-8\"\/>     &lt;meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"\/>     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\/>     &lt;link href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.1.3\/dist\/css\/bootstrap.min.css\" rel=\"stylesheet\"            integrity=\"sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3\" crossorigin=\"anonymous\">     &lt;link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap-icons@1.8.1\/font\/bootstrap-icons.css\"> &lt;\/head> &lt;body> &lt;p>Nothing to see here, move along.&lt;\/p> &lt;\/body> &lt;\/html><\/code><\/pre>\n<p>\u0418 <code>footer.html<\/code>:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"> &lt;head> &lt;\/head> &lt;body> &lt;p>Nothing to see here, move along.&lt;\/p> &lt;\/body> &lt;footer th:fragment=\"footer\">     &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.1.3\/dist\/js\/bootstrap.bundle.min.js\"               integrity=\"sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p\"              crossorigin=\"anonymous\">&lt;\/script> &lt;\/footer> &lt;\/html><\/code><\/pre>\n<p>\u0410 \u0442\u0430\u043a\u0436\u0435 \u0448\u0430\u0431\u043b\u043e\u043d <code>menu.html<\/code> \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430 \u043c\u0435\u043d\u044e:<\/p>\n<pre><code class=\"xml\">&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\">  &lt;body id=\"samples\"> &lt;nav class=\"navbar border mb-4 navbar-expand-lg navbar-light bg-light\" th:fragment=\"menu\">     &lt;div class=\"container-fluid\">         &lt;button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\"                 data-bs-target=\"#navbarSupportedContent\" aria-controls=\"navbarSupportedContent\"                 aria-expanded=\"false\" aria-label=\"Toggle navigation\">             &lt;span class=\"navbar-toggler-icon\">&lt;\/span>         &lt;\/button>         &lt;div class=\"collapse navbar-collapse\" id=\"navbarSupportedContent\">             &lt;ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">                 &lt;li class=\"nav-item\">&lt;a class=\"nav-link\" th:href=\"@{\/}\">Home&lt;\/a>&lt;\/li>             &lt;\/ul>             &lt;form class=\"d-flex\" method=\"post\" th:action=\"@{\/logout}\"                   th:if=\"${#authorization.expression('isAuthenticated()')}\">                 &lt;input class=\"form-control me-2\" type=\"hidden\" th:name=\"${_csrf.parameterName}\"                        th:value=\"${_csrf.token}\"\/>                 &lt;button id=\"logout-button\" type=\"submit\" class=\"btn btn-danger\">Logout&lt;\/button>             &lt;\/form>         &lt;\/div>     &lt;\/div> &lt;\/nav> &lt;\/body> &lt;\/html><\/code><\/pre>\n<h3>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440<\/h3>\n<p>\u0414\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 <code>home<\/code> \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435 <code>com.okta.developer.demo<\/code> \u043a\u043b\u0430\u0441\u0441 <code>HomeController<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c:<\/p>\n<pre><code class=\"java\">package com.okta.developer.demo;  import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.reactive.result.view.Rendering; import reactor.core.publisher.Mono;  import java.util.List; import java.util.stream.Collectors;  @Controller public class HomeController {      private static Logger logger = LoggerFactory.getLogger(HomeController.class);      @GetMapping(\"\/\")     public Mono&lt;Rendering><\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-333178","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/333178","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=333178"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/333178\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=333178"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=333178"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=333178"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}