{"id":399978,"date":"2024-06-29T14:59:27","date_gmt":"2024-06-29T14:59:27","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=399978"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=399978","title":{"rendered":"<span>KeyCloak \u0438 Spring Boot<\/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<h2>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0425\u043e\u0447\u0443 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u043a\u0430\u043a \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 Keycloak \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u044f token \u0438 refreshToken , \u0430 \u0442\u0430\u043a-\u0436\u0435 \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0442\u044c refreshToken \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 token.<\/p>\n<p>\u041c\u044b \u0442\u0430\u043a\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0444\u0440\u043e\u043d\u0442\u043e\u043c. \u0412\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u0438 \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f token 15 \u043c\u0438\u043d\u0443\u0442 \u0438 \u043a\u043e\u0433\u0434\u0430 \u043e\u043d \u0431\u044b\u043b \u043f\u0440\u043e\u0441\u0440\u043e\u0447\u0435\u043d, \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 refreshToken.<\/p>\n<h2>\u0417\u0430\u043f\u0443\u0441\u043a \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 keycloak<\/h2>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 keycloak \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0443\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c docker-compose. \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435 \u0433\u0434\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043b\u0438\u0448\u044c docker.    \u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 docker-compose \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 postgres. \u0411\u0430\u0437\u0430 \u0443 \u043d\u0430\u0441 \u043e\u0434\u043d\u0430, \u0434\u043b\u044f \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f keycloak \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0441\u0445\u0435\u043c\u0443 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0430\u0445.<\/p>\n<pre><code class=\"yaml\">  postgres:     container_name: postgres     image: library\/postgres:12     environment:       POSTGRES_USER: postgres       POSTGRES_PASSWORD: postgres       POSTGRES_DB: crm     ports:       - \"5432:5432\"     volumes:       - ${WORK_DATA}\/database:\/var\/lib\/postgresql\/data       - .\/initdb:\/docker-entrypoint-initdb.d     restart: unless-stopped    keycloak:     image: jboss\/keycloak     container_name: keycloak     environment:       DB_VENDOR: POSTGRES       DB_ADDR: postgres       DB_DATABASE: crm       DB_SCHEMA: keycloak       DB_USER: postgres       DB_PASSWORD: postgres       KEYCLOAK_USER: admin       KEYCLOAK_PASSWORD: admin     ports:       - \"8484:8080\"     depends_on:       - postgres<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c realm, \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0440\u043e\u043b\u0438 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c realm &#171;first_realm&#187;.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/35a\/f53\/baa\/35af53baab6a7d1bd5da7ff0bee9841d.png\" width=\"1364\" height=\"423\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/35a\/f53\/baa\/35af53baab6a7d1bd5da7ff0bee9841d.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442 &#171;my_app&#187;, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c Token \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c:<br \/> Access Type = &#171;confidential&#187;<br \/> Authorization Enable &#171;ON&#187;<br \/> \u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \u0442\u0443\u0442 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 Secret, \u043d\u043e \u044d\u0442\u043e \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c valid redirect Urls \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u0435\u043d: <a href=\"http:\/\/localhost:8080\/*\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:8080\/*<\/a><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5df\/233\/1cc\/5df2331cc763c1a778af4c0958dddf33.png\" width=\"1896\" height=\"896\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5df\/233\/1cc\/5df2331cc763c1a778af4c0958dddf33.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0440\u043e\u043b\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043d\u0430\u0448\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b &#8212; &#171;ADMIN&#187;, &#171;USER&#187;<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5de\/33d\/d04\/5de33dd0433d396ea73ca964297d7c34.png\" width=\"1366\" height=\"411\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5de\/33d\/d04\/5de33dd0433d396ea73ca964297d7c34.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 &#171;admin&#187; \u0441 \u0440\u043e\u043b\u044c\u044e &#171;ADMIN&#187;:<br \/> \u0418 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f &#171;user&#187; \u0441 \u0440\u043e\u043b\u044c\u044e &#171;USER&#187;. \u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u0438 \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 &#171;Credentials&#187;. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043f\u043e\u0442\u043e\u043c \u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/61b\/6bb\/946\/61b6bb946d27559565449660cc756319.png\" width=\"1205\" height=\"521\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/61b\/6bb\/946\/61b6bb946d27559565449660cc756319.png\"\/><figcaption><\/figcaption><\/figure>\n<h2>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c Keycloak \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Spring-Boot<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 spring-boot \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043a \u043d\u0435\u043c\u0443 Keycloak Spring Boot \u0430\u0434\u0430\u043f\u0442\u0435\u0440.  \u0424\u0430\u0439\u043b maven \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"xml\">    &lt;dependencyManagement>         &lt;dependencies>             &lt;dependency>                 &lt;groupId>org.keycloak.bom&lt;\/groupId>                 &lt;artifactId>keycloak-adapter-bom&lt;\/artifactId>                 &lt;version>12.0.3&lt;\/version>                 &lt;type>pom&lt;\/type>                 &lt;scope>import&lt;\/scope>             &lt;\/dependency>         &lt;\/dependencies>     &lt;\/dependencyManagement>  &lt;dependencys>         &lt;dependency>             &lt;groupId>org.springframework.boot&lt;\/groupId>             &lt;artifactId>spring-boot-starter-security&lt;\/artifactId>         &lt;\/dependency>         &lt;dependency>             &lt;groupId>org.springframework.boot&lt;\/groupId>             &lt;artifactId>spring-boot-starter-web&lt;\/artifactId>         &lt;\/dependency>         &lt;dependency>             &lt;groupId>org.keycloak&lt;\/groupId>             &lt;artifactId>keycloak-spring-boot-starter&lt;\/artifactId>         &lt;\/dependency>         &lt;dependency>             &lt;groupId>org.keycloak&lt;\/groupId>             &lt;artifactId>keycloak-admin-client&lt;\/artifactId>             &lt;version>${org.keycloak.admin-client.version}&lt;\/version>         &lt;\/dependency> &lt;\/dependencys><\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f Token<\/p>\n<pre><code class=\"java\">@Controller @RequiredArgsConstructor public class AuthController {      private final AuthService authService;      @PostMapping(\"\/auth\/login\")      @PreAuthorize(\"permitAll()\")     public ResponseEntity&lt;LoginResponseMessage> login(String email, String pass) {)         val responseMessage = authService.login(loginRequestMessage);         return ResponseEntity.status(HttpStatus.OK)                 .body(responseMessage);    } }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439<\/p>\n<pre><code class=\"java\">@Slf4j @Service @RequiredArgsConstructor public class AuthService {      private final AuthzClient authzClient;      public LoginResponseMessage login(String email, String pass) {         log.info(\"START login for user {}\", email);         try {             val response = authzClient.authorization(email, pass)                     .authorize();             val result = new LoginResponseMessage()                     .tokenType(response.getTokenType())                     .token(response.getToken());             log.info(\"FINISH login for user {} successfully\", email)             return result;         } catch (AuthorizationDeniedException | HttpResponseException ex) {             log.debug(\"Exception when login {}\", email, ex);             log.info(\"FINISH login for user {} is bad\", email);             throw new BadAuthorizeException();         } catch (Exception ex) {             log.error(\"Some error occurred during login\");             throw new BadAuthorizeException();         }     } }<\/code><\/pre>\n<pre><code class=\"java\">public class LoginResponseMessage   {   @JsonProperty(\"token\")   private String token;    @JsonProperty(\"refreshToken\")   private String refreshToken;    @JsonProperty(\"tokenType\")   private String tokenType;   }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f<br \/> \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0440\u043e\u043b\u0435\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435.<\/p>\n<pre><code class=\"java\">@Slf4j @Controller public class ClientController {      @PostMapping(\"\/client\/add\")     @PreAuthorize(\"hasRole('ADMIN')\")     public ResponseEntity&lt;ClientInfo> addNewClient(@Valid ClientInfo clientInfo) {         log.info(\"Call method addNewClient\");         return ResponseEntity.status(HttpStatus.OK)                 .body(\"Client add\");     }      @GetMapping(\"\/client\/{clientId}\")     @PreAuthorize(\"hasRole('USER')\")     public ResponseEntity&lt;ClientInfo> getClientInfoById(Integer clientId) {         val result = clientService.getClientById(clientId);          return ResponseEntity.status(HttpStatus.OK)                 .body(\"CLIENT\");     } }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a keycloak,<br \/> \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.<br \/> \u0412 application.yml \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438,<br \/> \u0432\u043e\u0442 \u0442\u0443\u0442 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u043f\u043e\u043b\u0435 secret \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 Credentials<\/p>\n<pre><code class=\"yaml\">keycloak:   authServerUrl: http:\/\/localhost:8484\/auth   realm: first_realm   resource: my_app   credentials:     secret: S63XNDWRT8i4DlsKhBgTJdO94fasd<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e spring-security, \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c KeycloakWebSecurityConfigurerAdapter<\/p>\n<pre><code class=\"java\">@KeycloakConfiguration @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {       @Override     protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {         return new NullAuthenticatedSessionStrategy();     }      @Autowired     public void configureGlobal(AuthenticationManagerBuilder authManagerBuilder) {         KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();         keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());         authManagerBuilder.authenticationProvider(keycloakAuthenticationProvider);     }      @Bean     public KeycloakConfigResolver keycloakConfigResolver() {         return new KeycloakSpringBootConfigResolver();     }      @Override     protected void configure(HttpSecurity http) throws Exception {         super.configure(http);         http.cors().and().csrf().disable();         http                 .authorizeRequests()                 .antMatchers(\"\/auth\/**\").permitAll()                 .anyRequest().fullyAuthenticated()                 .and()                 .exceptionHandling()                 .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));     } }<\/code><\/pre>\n<p>\u0414\u0435\u043b\u0430\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Keycloak<\/p>\n<pre><code class=\"java\">@Configuration public class KeycloakConfiguration {      @Bean     public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {         return new KeycloakSpringBootConfigResolver();     }      @Bean     public AuthzClient keycloakAuthzClient(KeycloakSpringBootProperties props) {         val config = new org.keycloak.authorization.client.Configuration(                 props.getAuthServerUrl(), props.getRealm(),                 props.getResource(), props.getCredentials(), null);          return AuthzClient.create(config);     }      @Bean     public Keycloak keycloak(KeycloakSpringBootProperties props) {         return KeycloakBuilder.builder()                 .serverUrl(props.getAuthServerUrl())                 .realm(props.getRealm())                 .grantType(OAuth2Constants.CLIENT_CREDENTIALS)                 .clientId(props.getResource())                 .clientSecret((String) props.getCredentials().get(\"secret\"))                 .build();     } }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0443 \/auth\/login \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c,<br \/> \u0430 \u0432 \u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c token \u0438 refreshToken<\/p>\n<p>\u0412\u0440\u0435\u043c\u044f \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 Token \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 realm \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 tokens<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043d\u0430\u0448 Token \u0443\u0441\u0442\u0430\u0440\u0435\u043b, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u0438 \u043d\u0443\u0436\u0435\u043d<br \/> refreshToken. \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u043c\u0435\u0442\u043e\u0434. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0435\u043b\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u041d\u043e\u0432\u044b\u0439 \u044d\u0434\u043f\u043e\u0438\u043d\u0442 \/auth\/tokenRefresh \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0442\u0443\u0434\u0430 \u043d\u0430\u0448 refreshToken.<\/p>\n<pre><code class=\"json\">    @PostMapping(\"\/auth\/tokenRefresh\")     @PreAuthorize(\"permitAll()\")     public ResponseEntity&lt;LoginResponseMessage> tokenRefresh(String refreshToken) {         val responseMessage = authService.tokenRefresh(refreshToken);         return ResponseEntity.status(HttpStatus.OK)                 .body(responseMessage);     }<\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f token \u0432 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<pre><code class=\"java\">   @Transactional     public LoginResponseMessage tokenRefresh(String refresh) {         log.info(\"START tokenRefresh\");         try {             String url = authzClient.getConfiguration().getAuthServerUrl() + \"\/realms\/\" + authzClient.getConfiguration().getRealm() + \"\/protocol\/openid-connect\/token\";             String clientId = authzClient.getConfiguration().getResource();             String secret = (String) authzClient.getConfiguration().getCredentials().get(\"secret\");             val http = new Http(authzClient.getConfiguration(), (params, headers) -> {             });              val response = http.&lt;AccessTokenResponse>post(url)                     .authentication()                     .client()                     .form()                     .param(\"grant_type\", \"refresh_token\")                     .param(\"refresh_token\", refresh)                     .param(\"client_id\", clientId)                     .param(\"client_secret\", secret)                     .response()                     .json(AccessTokenResponse.class)                     .execute();              val result = new LoginResponseMessage()                     .tokenType(response.getTokenType())                     .token(response.getToken())                     .refreshToken(response.getRefreshToken());             log.info(\"FINISH tokenRefresh\");             return result;         } catch (AuthorizationDeniedException | HttpResponseException ex) {             log.debug(\"Exception when tokenRefresh\", ex);             log.info(\"FINISH tokenRefresh is bad\");             throw new BadAuthorizeException();         }     }<\/code><\/pre>\n<p>\u0418 \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043c\u044b \u043e\u043f\u044f\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 token \u0438 refreshToken.<\/p>\n<h2>\u0412\u0441\u0435\u043c \u043a\u0442\u043e \u0434\u043e\u0447\u0438\u0442\u0430\u043b \u0441\u043f\u0430\u0441\u0438\u0431\u043e!<\/h2>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u043a\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f \u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0433\u043e\u0442\u043e\u0432 \u0432\u044b\u0441\u043b\u0443\u0448\u0430\u0442\u044c.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/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\/articles\/661541\/\"> https:\/\/habr.com\/ru\/articles\/661541\/<\/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<h2>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0425\u043e\u0447\u0443 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u043a\u0430\u043a \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 Keycloak \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u044f token \u0438 refreshToken , \u0430 \u0442\u0430\u043a-\u0436\u0435 \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0442\u044c refreshToken \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 token.<\/p>\n<p>\u041c\u044b \u0442\u0430\u043a\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0444\u0440\u043e\u043d\u0442\u043e\u043c. \u0412\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u0438 \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f token 15 \u043c\u0438\u043d\u0443\u0442 \u0438 \u043a\u043e\u0433\u0434\u0430 \u043e\u043d \u0431\u044b\u043b \u043f\u0440\u043e\u0441\u0440\u043e\u0447\u0435\u043d, \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 refreshToken.<\/p>\n<h2>\u0417\u0430\u043f\u0443\u0441\u043a \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 keycloak<\/h2>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 keycloak \u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0443\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c docker-compose. \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435 \u0433\u0434\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043b\u0438\u0448\u044c docker.    \u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 docker-compose \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 postgres. \u0411\u0430\u0437\u0430 \u0443 \u043d\u0430\u0441 \u043e\u0434\u043d\u0430, \u0434\u043b\u044f \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f keycloak \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0441\u0445\u0435\u043c\u0443 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0430\u0445.<\/p>\n<pre><code class=\"yaml\">  postgres:     container_name: postgres     image: library\/postgres:12     environment:       POSTGRES_USER: postgres       POSTGRES_PASSWORD: postgres       POSTGRES_DB: crm     ports:       - \"5432:5432\"     volumes:       - ${WORK_DATA}\/database:\/var\/lib\/postgresql\/data       - .\/initdb:\/docker-entrypoint-initdb.d     restart: unless-stopped    keycloak:     image: jboss\/keycloak     container_name: keycloak     environment:       DB_VENDOR: POSTGRES       DB_ADDR: postgres       DB_DATABASE: crm       DB_SCHEMA: keycloak       DB_USER: postgres       DB_PASSWORD: postgres       KEYCLOAK_USER: admin       KEYCLOAK_PASSWORD: admin     ports:       - \"8484:8080\"     depends_on:       - postgres<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c realm, \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0440\u043e\u043b\u0438 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c realm &#171;first_realm&#187;.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442 &#171;my_app&#187;, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c Token \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c:<br \/> Access Type = &#171;confidential&#187;<br \/> Authorization Enable &#171;ON&#187;<br \/> \u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \u0442\u0443\u0442 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 Secret, \u043d\u043e \u044d\u0442\u043e \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c valid redirect Urls \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u0435\u043d: <a href=\"http:\/\/localhost:8080\/*\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost:8080\/*<\/a><\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0440\u043e\u043b\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043d\u0430\u0448\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b &#8212; &#171;ADMIN&#187;, &#171;USER&#187;<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 &#171;admin&#187; \u0441 \u0440\u043e\u043b\u044c\u044e &#171;ADMIN&#187;:<br \/> \u0418 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f &#171;user&#187; \u0441 \u0440\u043e\u043b\u044c\u044e &#171;USER&#187;. \u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u0438 \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 &#171;Credentials&#187;. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043f\u043e\u0442\u043e\u043c \u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h2>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c Keycloak \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Spring-Boot<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 spring-boot \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043a \u043d\u0435\u043c\u0443 Keycloak Spring Boot \u0430\u0434\u0430\u043f\u0442\u0435\u0440.  \u0424\u0430\u0439\u043b maven \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"xml\">    &lt;dependencyManagement>         &lt;dependencies>             &lt;dependency>                 &lt;groupId>org.keycloak.bom&lt;\/groupId>                 &lt;artifactId>keycloak-adapter-bom&lt;\/artifactId>                 &lt;version>12.0.3&lt;\/version>                 &lt;type>pom&lt;\/type>                 &lt;scope>import&lt;\/scope>             &lt;\/dependency>         &lt;\/dependencies>     &lt;\/dependencyManagement>  &lt;dependencys>         &lt;dependency>             &lt;groupId>org.springframework.boot&lt;\/groupId>             &lt;artifactId>spring-boot-starter-security&lt;\/artifactId>         &lt;\/dependency>         &lt;dependency>             &lt;groupId>org.springframework.boot&lt;\/groupId>             &lt;artifactId>spring-boot-starter-web&lt;\/artifactId>         &lt;\/dependency>         &lt;dependency>             &lt;groupId>org.keycloak&lt;\/groupId>             &lt;artifactId>keycloak-spring-boot-starter&lt;\/artifactId>         &lt;\/dependency>         &lt;dependency>             &lt;groupId>org.keycloak&lt;\/groupId>             &lt;artifactId>keycloak-admin-client&lt;\/artifactId>             &lt;version>${org.keycloak.admin-client.version}&lt;\/version>         &lt;\/dependency> &lt;\/dependencys><\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f Token<\/p>\n<pre><code class=\"java\">@Controller @RequiredArgsConstructor public class AuthController {      private final AuthService authService;      @PostMapping(\"\/auth\/login\")      @PreAuthorize(\"permitAll()\")     public ResponseEntity&lt;LoginResponseMessage> login(String email, String pass) {)         val responseMessage = authService.login(loginRequestMessage);         return ResponseEntity.status(HttpStatus.OK)                 .body(responseMessage);    } }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439<\/p>\n<pre><code class=\"java\">@Slf4j @Service @RequiredArgsConstructor public class AuthService {      private final AuthzClient authzClient;      public LoginResponseMessage login(String email, String pass) {         log.info(\"START login for user {}\", email);         try {             val response = authzClient.authorization(email, pass)                     .authorize();             val result = new LoginResponseMessage()                     .tokenType(response.getTokenType())                     .token(response.getToken());             log.info(\"FINISH login for user {} successfully\", email)             return result;         } catch (AuthorizationDeniedException | HttpResponseException ex) {             log.debug(\"Exception when login {}\", email, ex);             log.info(\"FINISH login for user {} is bad\", email);             throw new BadAuthorizeException();         } catch (Exception ex) {             log.error(\"Some error occurred during login\");             throw new BadAuthorizeException();         }     } }<\/code><\/pre>\n<pre><code class=\"java\">public class LoginResponseMessage   {   @JsonProperty(\"token\")   private String token;    @JsonProperty(\"refreshToken\")   private String refreshToken;    @JsonProperty(\"tokenType\")   private String tokenType;   }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f<br \/> \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0440\u043e\u043b\u0435\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435.<\/p>\n<pre><code class=\"java\">@Slf4j @Controller public class ClientController {      @PostMapping(\"\/client\/add\")     @PreAuthorize(\"hasRole('ADMIN')\")     public ResponseEntity&lt;ClientInfo> addNewClient(@Valid ClientInfo clientInfo) {         log.info(\"Call method addNewClient\");         return ResponseEntity.status(HttpStatus.OK)                 .body(\"Client add\");     }      @GetMapping(\"\/client\/{clientId}\")     @PreAuthorize(\"hasRole('USER')\")     public ResponseEntity&lt;ClientInfo> getClientInfoById(Integer clientId) {         val result = clientService.getClientById(clientId);          return ResponseEntity.status(HttpStatus.OK)                 .body(\"CLIENT\");     } }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a keycloak,<br \/> \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.<br \/> \u0412 application.yml \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438,<br \/> \u0432\u043e\u0442 \u0442\u0443\u0442 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u043f\u043e\u043b\u0435 secret \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 Credentials<\/p>\n<pre><code class=\"yaml\">keycloak:   authServerUrl: http:\/\/localhost:8484\/auth   realm: first_realm   resource: my_app   credentials:     secret: S63XNDWRT8i4DlsKhBgTJdO94fasd<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e spring-security, \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c KeycloakWebSecurityConfigurerAdapter<\/p>\n<pre><code class=\"java\">@KeycloakConfiguration @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {       @Override     protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {         return new NullAuthenticatedSessionStrategy();     }      @Autowired     public void configureGlobal(AuthenticationManagerBuilder authManagerBuilder) {         KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();         keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());         authManagerBuilder.authenticationProvider(keycloakAuthenticationProvider);     }      @Bean     public KeycloakConfigResolver keycloakConfigResolver() {         return new KeycloakSpringBootConfigResolver();     }      @Override     protected void configure(HttpSecurity http) throws Exception {         super.configure(http);         http.cors().and().csrf().disable();         http                 .authorizeRequests()                 .antMatchers(\"\/auth\/**\").permitAll()                 .anyRequest().fullyAuthenticated()                 .and()                 .exceptionHandling()                 .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));     } }<\/code><\/pre>\n<p>\u0414\u0435\u043b\u0430\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Keycloak<\/p>\n<pre><code class=\"java\">@Configuration public class KeycloakConfiguration {      @Bean     public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {         return new KeycloakSpringBootConfigResolver();     }      @Bean     public AuthzClient keycloakAuthzClient(KeycloakSpringBootProperties props) {         val config = new org.keycloak.authorization.client.Configuration(                 props.getAuthServerUrl(), props.getRealm(),                 props.getResource(), props.getCredentials(), null);          return AuthzClient.create(config);     }      @Bean     public Keycloak keycloak(KeycloakSpringBootProperties props) {         return KeycloakBuilder.builder()                 .serverUrl(props.getAuthServerUrl())                 .realm(props.getRealm())                 .grantType(OAuth2Constants.CLIENT_CREDENTIALS)                 .clientId(props.getResource())                 .clientSecret((String) props.getCredentials().get(\"secret\"))                 .build();     } }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u043a \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0443 \/auth\/login \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c,<br \/> \u0430 \u0432 \u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c token \u0438 refreshToken<\/p>\n<p>\u0412\u0440\u0435\u043c\u044f \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 Token \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 realm \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 tokens<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043d\u0430\u0448 Token \u0443\u0441\u0442\u0430\u0440\u0435\u043b, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u0438 \u043d\u0443\u0436\u0435\u043d<br \/> refreshToken. \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u043c\u0435\u0442\u043e\u0434. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0435\u043b\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u041d\u043e\u0432\u044b\u0439 \u044d\u0434\u043f\u043e\u0438\u043d\u0442 \/auth\/tokenRefresh \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0442\u0443\u0434\u0430 \u043d\u0430\u0448 refreshToken.<\/p>\n<pre><code class=\"json\">    @PostMapping(\"\/auth\/tokenRefresh\")     @PreAuthorize(\"permitAll()\")     public ResponseEntity&lt;LoginResponseMessage> tokenRefresh(String refreshToken) {         val responseMessage = authService.tokenRefresh(refreshToken);         return ResponseEntity.status(HttpStatus.OK)                 .body(responseMessage);     }<\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f token \u0432 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<pre><code class=\"java\">   @Transactional     public LoginResponseMessage tokenRefresh(String refresh) {         log.info(\"START tokenRefresh\");         try {             String url = authzClient.getConfiguration().getAuthServerUrl() + \"\/realms\/\" + authzClient.getConfiguration().getRealm() + \"\/protocol\/openid-connect\/token\";             String clientId = authzClient.getConfiguration().getResource();             String secret = (String)<\/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-399978","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/399978","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=399978"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/399978\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=399978"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=399978"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=399978"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}