{"id":2405,"date":"2021-07-24T19:54:03","date_gmt":"2021-07-24T10:54:03","guid":{"rendered":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=2405"},"modified":"2021-07-31T11:22:52","modified_gmt":"2021-07-31T02:22:52","slug":"spring-boot-oauth2-resource-server-%ea%b5%ac%ec%b6%95","status":"publish","type":"post","link":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=2405","title":{"rendered":"Spring Boot OAuth2 Resource Server \uad6c\ucd95"},"content":{"rendered":"<h1>Spring Boot OAuth2 Resource Server \uad6c\ucd95<\/h1>\n<p>\ucc38\uc870\uc0ac\uc774\ud2b8 : <a href=\"https:\/\/www.baeldung.com\/spring-security-oauth2-jws-jwk\">https:\/\/www.baeldung.com\/spring-security-oauth2-jws-jwk<\/a><\/p>\n<p>\ucc38\uc870\uc0ac\uc774\ud2b8 : <a href=\"https:\/\/daddyprogrammer.org\/post\/1890\/spring-boot-oauth2-resourceserver-asymmetric-keys-to-do-the-signing-process\/\">https:\/\/daddyprogrammer.org\/post\/1890\/spring-boot-oauth2-resourceserver-asymmetric-keys-to-do-the-signing-process\/<\/a><\/p>\n<h2>\ubaa9\ud45c<\/h2>\n<p>OAuth2 Resource Server \ub97c \uad6c\ucd95\ud569\ub2c8\ub2e4.<\/p>\n<h2>\uc18c\uc2a4\ucf54\ub4dc<\/h2>\n<p><a href=\"https:\/\/github.com\/skyer9\/SpringBootOauth2ResourceServer\">\uc5ec\uae30<\/a> \uc5d0 \uc804\uccb4 \uc18c\uc2a4\ucf54\ub4dc\uac00 \uc62c\ub77c\uac00 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n<h2>\ud504\ub85c\uc81d\ud2b8 \uc0dd\uc131<\/h2>\n<p>\uc2e0\uaddc \ud504\ub85c\uc81d\ud2b8\ub97c \uc0dd\uc131\ud569\ub2c8\ub2e4.<\/p>\n<p>\ud504\ub85c\uc81d\ud2b8\uba85\uc740 oauth2resourceserver \ub85c \ud569\ub2c8\ub2e4.<\/p>\n<p>\uc758\uc874\uc131\uc740 DevTools, Lombok, Spring Web \uc744 \uc120\ud0dd\ud569\ub2c8\ub2e4.<\/p>\n<h2>hosts \ud30c\uc77c \uc218\uc815<\/h2>\n<p>\ud074\ub77c\uc774\uc5b8\ud2b8\ub294 <code>localhost<\/code> \ub85c, \uc778\uc99d\uc11c\ubc84\ub294 <code>auth.localhost<\/code>,<br \/>\n\ub9ac\uc18c\uc2a4\uc11c\ubc84\ub294 <code>res.localhost<\/code> \ub85c \uac01\uac01 \ud560\ub2f9\ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-text\">127.0.0.1    auth.localhost\n127.0.0.1    res.localhost<\/code><\/pre>\n<h2>build.gradle \uc218\uc815<\/h2>\n<pre><code class=\"language-gradle\">dependencies {\n    implementation &#039;org.springframework.boot:spring-boot-starter-oauth2-resource-server&#039;\n    implementation &#039;org.springframework.boot:spring-boot-starter-web&#039;\n    implementation &#039;org.springframework.security:spring-security-jwt&#039;\n\n    \/\/ \ubc84\uc804\uc744 \uba85\uc2dc\uc801\uc73c\ub85c \uc9c0\uc815\ud574\uc57c \ud55c\ub2e4(?)\n    implementation &#039;org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.5.2&#039;\n\n    compileOnly &#039;org.projectlombok:lombok&#039;\n    developmentOnly &#039;org.springframework.boot:spring-boot-devtools&#039;\n    annotationProcessor &#039;org.projectlombok:lombok&#039;\n    testImplementation &#039;org.springframework.boot:spring-boot-starter-test&#039;\n}<\/code><\/pre>\n<h2>application.yml \uc218\uc815<\/h2>\n<p>\ud074\ub77c\uc774\uc5b8\ud2b8 \uc815\ubcf4\uc640 <code>token-info-uri<\/code> \ub9cc\uc73c\ub85c \ud1a0\ud070 \uccb4\ud06c\uac00 \uac00\ub2a5\ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-yaml\">security:\n  oauth2:\n#    jwt:\n#      signkey: 123@#$\n    resource:\n      token-info-uri: http:\/\/localhost:9000\/oauth\/check_token\n    client:\n      client-id: foo\n      client-secret: bar\n\nserver:\n  port: 9001<\/code><\/pre>\n<h2>Bearer \ud1a0\ud070\ubc29\uc2dd<\/h2>\n<p>Authorization Server \ub97c JDBC \ubc29\uc2dd\uc73c\ub85c \ubcc0\uacbd\ud569\ub2c8\ub2e4.<\/p>\n<p><code>.checkTokenAccess(&quot;isAuthenticated()&quot;)<\/code> \uc73c\ub85c<br \/>\n\ud1a0\ud070\uccb4\ud06c \uc5d4\ub4dc\ud3ec\uc778\ud2b8\ub97c \ud65c\uc131\ud654 \ud574\uc90d\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-java\">@RequiredArgsConstructor\n@Configuration\n@EnableAuthorizationServer\npublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    \/\/ ......\n\n    \/\/ JDBC \ubc29\uc2dd\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.tokenStore(new JdbcTokenStore(dataSource));\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {\n        security.tokenKeyAccess(&quot;permitAll()&quot;)\n                .checkTokenAccess(&quot;isAuthenticated()&quot;) \/\/allow check token\n                .allowFormAuthenticationForClients();\n    }\n\n\/\/    \/\/ JWT \ubc29\uc2dd\n\/\/    @Override\n\/\/    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n\/\/        super.configure(endpoints);\n\/\/        endpoints.accessTokenConverter(jwtAccessTokenConverter()).userDetailsService(userDetailService);\n\/\/    }\n\/\/\n\/\/    @Bean\n\/\/    public JwtAccessTokenConverter jwtAccessTokenConverter() {\n\/\/        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n\/\/        converter.setSigningKey(signKey);\n\/\/        return converter;\n\/\/    }\n}<\/code><\/pre>\n<p>ResourceServerConfig.java<\/p>\n<p><code>user-info-uri<\/code> \ub9cc \uc124\uc815\ud574 \uc8fc\uba74 \uc124\uc815\uc774 \ub05d\ub0a9\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-java\">@Configuration\n@EnableResourceServer\npublic class ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n\n        http.headers().frameOptions().disable();\n        http.authorizeRequests()\n                .antMatchers(&quot;\/api\/userinfo&quot;).access(&quot;#oauth2.hasScope(&#039;profile&#039;)&quot;)\n                .anyRequest().authenticated();\n    }\n}<\/code><\/pre>\n<p>UserInfoController.java<\/p>\n<pre><code class=\"language-java\">@RestController\n@RequestMapping(&quot;\/api&quot;)\npublic class UserInfoController {\n\n    @GetMapping(&quot;\/userinfo&quot;)\n    public ResponseEntity&lt;?&gt; userInfo(Principal principal,\n                                      HttpServletRequest request, @RequestParam HashMap&lt;String,String&gt; paramMap) {\n        return ResponseEntity.ok(principal);\n    }\n}<\/code><\/pre>\n<h2>JWT \ud1a0\ud070\ubc29\uc2dd(signKey \ubc29\uc2dd)<\/h2>\n<p>Authorization Server \ub97c JWT \ubc29\uc2dd\uc73c\ub85c \ubcc0\uacbd\ud569\ub2c8\ub2e4.<\/p>\n<p>\uc778\uc99d\uc11c\ubc84\ub97c \uac70\uce58\uc9c0 \uc54a\uace0, <code>signKey<\/code> \ub9cc\uc73c\ub85c \ud1a0\ud070\uccb4\ud06c\ub97c \ud569\ub2c8\ub2e4.<br \/>\n\uc778\uc99d\uc11c\ubc84\uc640 \ub9ac\uc18c\uc2a4\uc11c\ubc84\uc758 <code>signKey<\/code> \ub294 \ub3d9\uc77c\ud574\uc57c \ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-java\">@RequiredArgsConstructor\n@Configuration\n@EnableAuthorizationServer\npublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    @Value(&quot;${security.oauth2.jwt.signkey}&quot;)\n    private String signKey;\n\n    private final DataSource dataSource;\n\n    private final PasswordEncoder passwordEncoder;\n\n    private final CustomUserDetailService userDetailService;\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.jdbc(dataSource).passwordEncoder(passwordEncoder);\n    }\n\n\/\/    \/\/ JDBC \ubc29\uc2dd\n\/\/    @Override\n\/\/    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n\/\/        endpoints.tokenStore(new JdbcTokenStore(dataSource));\n\/\/    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {\n        security.tokenKeyAccess(&quot;permitAll()&quot;)\n                .checkTokenAccess(&quot;isAuthenticated()&quot;) \/\/allow check token\n                .allowFormAuthenticationForClients();\n    }\n\n    \/\/ JWT \ubc29\uc2dd\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        super.configure(endpoints);\n        endpoints.accessTokenConverter(jwtAccessTokenConverter()).userDetailsService(userDetailService);\n    }\n\n    @Bean\n    public JwtAccessTokenConverter jwtAccessTokenConverter() {\n        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n        converter.setSigningKey(signKey);\n        return converter;\n    }\n}<\/code><\/pre>\n<p>Resource Server \uc758 application.yml \uc744 \uc544\ub798\uc640 \uac19\uc774 \uc218\uc815\ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-yaml\">security:\n  oauth2:\n    jwt:\n      signkey: 123@#$\n#    resource:\n#      token-info-uri: http:\/\/localhost:9000\/oauth\/check_token\n#    client:\n#      client-id: foo\n#      client-secret: bar<\/code><\/pre>\n<p>ResourceServerConfig.java<\/p>\n<pre><code class=\"language-java\">@Configuration\n@EnableResourceServer\npublic class ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Value(&quot;${security.oauth2.jwt.signkey}&quot;)\n    private String signKey;\n\n    @Bean\n    public TokenStore tokenStore() {\n        return new JwtTokenStore(accessTokenConverter());\n    }\n\n    @Bean\n    public JwtAccessTokenConverter accessTokenConverter() {\n        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n        converter.setSigningKey(signKey);\n        return converter;\n    }\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n\n        http.headers().frameOptions().disable();\n        http.authorizeRequests()\n                .antMatchers(&quot;\/api\/userinfo&quot;).access(&quot;#oauth2.hasScope(&#039;profile&#039;)&quot;)\n                .anyRequest().authenticated();\n    }\n}<\/code><\/pre>\n<h2>JWT \ud1a0\ud070\ubc29\uc2dd(\ube44\ub300\uce6d\ud0a4 \ubc29\uc2dd)<\/h2>\n<p>\uacf5\uac1c\ud0a4\/\ube44\ubc00\ud0a4\ub97c \uc774\uc6a9\ud55c \ud1a0\ud070 \uc554\ud638\ud654\ub97c \ud569\ub2c8\ub2e4.<\/p>\n<h3>\ube44\ub300\uce6d \ud0a4 \uc0dd\uc131<\/h3>\n<p><code>keytool<\/code> \uc744 \uc774\uc6a9\ud574 \uc554\ud638\ud30c\uc77c\uc744 \uc0dd\uc131\ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-bash\">cd $JAVA_HOME\/bin\n\n.\/keytool -genkeypair \\\n  -alias my-oauth-jwt \\\n  -keyalg RSA \\\n  -keypass mypassword \\\n  -keystore my-oauth-jwt.jks \\\n  -storepass mypassword\n\n.\/keytool -list -rfc --keystore my-oauth-jwt.jks | openssl x509 -inform pem -pubkey\n\n-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyg0cDZE5BsUkVi6tcq7s\ne\/L8oZ\/deX1wEx8poKCw0L9psnJ94RFPE1TpBO+Y1XoCmI6Y+srtnhOkuen0xgWN\n......................\n-----END PUBLIC KEY-----\n-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgIEeur0ATANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJL\nUjEOMAwGA1UECBMFU2VvdWwxDjAMBgNVBAcTBVNlb3VsMQ0wCwYDVQQKEwRIb21l\nMQ0wCwYDVQQLEwRIb21lMRAwDgYDVQQDEwdTYW4gTGVlMB4XDTIxMDcyNTAwNTgw\n......................\n-----END CERTIFICATE-----<\/code><\/pre>\n<h3>Authorization Server \ud504\ub85c\uc81d\ud2b8\uc5d0 \ucd94\uac00<\/h3>\n<p>Authorization Server \uc758 resources \ud3f4\ub354\uc5d0 <code>my-oauth-jwt.jks<\/code> \ub97c \ucd94\uac00\ud569\ub2c8\ub2e4.<br \/>\nmypassword \ub97c <code>my-oauth-jwt.private<\/code> \ub85c \uc0dd\uc131 \ud6c4 resources \ud3f4\ub354\uc5d0 \ucd94\uac00\ud569\ub2c8\ub2e4.<br \/>\n\uacf5\uac1c\ud0a4\ub97c <code>my-oauth-jwt.pub<\/code> \ub85c \uc0dd\uc131 \ud6c4 resources \ud3f4\ub354\uc5d0 \ucd94\uac00\ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-text\">-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyg0cDZE5BsUkVi6tcq7s\ne\/L8oZ\/deX1wEx8poKCw0L9psnJ94RFPE1TpBO+Y1XoCmI6Y+srtnhOkuen0xgWN\n..........................\n-----END PUBLIC KEY-----<\/code><\/pre>\n<p><code>my-oauth-jwt.jks<\/code>, <code>my-oauth-jwt.private<\/code> \ub97c \uc774\uc6a9\ud574 \ud1a0\ud070\uc744 \uc554\ud638\ud654\ud569\ub2c8\ub2e4.<\/p>\n<p>AuthorizationServerConfig.java<\/p>\n<pre><code class=\"language-java\">public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    \/\/ ......\n\n\/\/    @Bean\n\/\/    public JwtAccessTokenConverter jwtAccessTokenConverter() {\n\/\/        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n\/\/        converter.setSigningKey(signKey);\n\/\/        return converter;\n\/\/    }\n\n    @Bean\n    public JwtAccessTokenConverter jwtAccessTokenConverter() throws Exception {\n\n        \/\/ \ud328\uc2a4\uc6cc\ub4dc \uc800\uc7a5 \ud30c\uc77c\n        String password = &quot;&quot;;\n        byte[] buffer = new byte[128];\n        ClassPathResource resource = new ClassPathResource(&quot;my-oauth-jwt.private&quot;);\n        InputStreamReader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.US_ASCII);\n        StringBuilder builder = new StringBuilder();\n        for(int c = reader.read(); c != -1; c = reader.read()){\n            builder.append((char) c);\n        }\n        password = builder.toString();\n\n        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new FileSystemResource(&quot;src\/main\/resources\/my-oauth-jwt.jks&quot;), password.toCharArray());\n        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n        converter.setKeyPair(keyStoreKeyFactory.getKeyPair(&quot;my-oauth-jwt&quot;));\n\n        return converter;\n    }\n}<\/code><\/pre>\n<h3>Resource Server \ud504\ub85c\uc81d\ud2b8\uc5d0 \ucd94\uac00<\/h3>\n<p>\uacf5\uac1c\ud0a4\ub97c <code>my-oauth-jwt.pub<\/code> \ub85c \uc0dd\uc131 \ud6c4 resources \ud3f4\ub354\uc5d0 \ucd94\uac00\ud569\ub2c8\ub2e4.<br \/>\n\uc778\uc99d\uc11c\ubc84\uc640 \ub9ac\uc18c\uc2a4\uc11c\ubc84\uc758 <code>my-oauth-jwt.pub<\/code> \ub294 \uac19\uc740 \ud30c\uc77c\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-java\">\/\/    @Bean\n\/\/    public JwtAccessTokenConverter accessTokenConverter() {\n\/\/        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n\/\/        converter.setSigningKey(signKey);\n\/\/        return converter;\n\/\/    }\n\n    @Bean\n    public JwtAccessTokenConverter accessTokenConverter() {\n        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n        Resource resource = new ClassPathResource(&quot;my-oauth-jwt.pub&quot;);\n        String publicKey = null;\n        try {\n            \/\/ implementation &#039;commons-io:commons-io:2.6&#039;\n            publicKey = IOUtils.toString(resource.getInputStream());\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n        converter.setVerifierKey(publicKey);\n        return converter;\n    }<\/code><\/pre>\n<h2>JWT \ud1a0\ud070\ubc29\uc2dd(\ube44\ub300\uce6d\ud0a4 \ubc29\uc2dd, \/oauth\/token_key \uc774\uc6a9)<\/h2>\n<h3>Authorization Server \ud504\ub85c\uc81d\ud2b8 \uc218\uc815<\/h3>\n<p><code>tokenKeyAccess(&quot;permitAll()&quot;)<\/code> \uc5d0 \uc758\ud574 <code>\/oauth\/token_key<\/code> \uc811\uadfc\uc744 \ud5c8\uc6a9\ud569\ub2c8\ub2e4.<\/p>\n<p>AuthorizationServerConfig.java<\/p>\n<pre><code class=\"language-java\">    @Override\n    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {\n        security.tokenKeyAccess(&quot;permitAll()&quot;)\n                .checkTokenAccess(&quot;isAuthenticated()&quot;) \/\/allow check token\n                .allowFormAuthenticationForClients();\n    }<\/code><\/pre>\n<h3>Resource Server \ud504\ub85c\uc81d\ud2b8 \uc218\uc815<\/h3>\n<p><code>key-uri<\/code> \ub97c \uc124\uc815\ud574 \uc90d\ub2c8\ub2e4.<\/p>\n<p><code>token_key<\/code> \uc5d0 \uc554\ud638\ud654 \ubc29\uc2dd\uacfc \uacf5\uac1c\ud0a4\ub97c \uc81c\uacf5\ubc1b\uc2b5\ub2c8\ub2e4.<\/p>\n<pre><code class=\"language-yaml\">security:\n  oauth2:\n    resource:\n      jwt:\n        key-uri: http:\/\/auth.localhost:9000\/oauth\/token_key\n#    jwt:\n#      signkey: 123@#$\n#    resource:\n#      token-info-uri: http:\/\/localhost:9000\/oauth\/check_token\n#    client:\n#      client-id: foo\n#      client-secret: bar<\/code><\/pre>\n<p><code>token_key<\/code> \uc5d0 \uc758\ud574 \ubaa8\ub4e0 \uc124\uc815\uc774 \uc801\uc6a9\ub418\ubbc0\ub85c \ubcc4\ub3c4\uc758 \ucf54\ub529\uc774 \ud544\uc694\uc5c6\uc2b5\ub2c8\ub2e4.<\/p>\n<p>ResourceServerConfig.java<\/p>\n<pre><code class=\"language-java\">public class ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n\n        http.headers().frameOptions().disable();\n        http.authorizeRequests()\n                .antMatchers(&quot;\/api\/userinfo&quot;).access(&quot;#oauth2.hasScope(&#039;profile&#039;)&quot;)\n                .anyRequest().authenticated();\n    }\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Spring Boot OAuth2 Resource Server \uad6c\ucd95 \ucc38\uc870\uc0ac\uc774\ud2b8 : https:\/\/www.baeldung.com\/spring-security-oauth2-jws-jwk \ucc38\uc870\uc0ac\uc774\ud2b8 : https:\/\/daddyprogrammer.org\/post\/1890\/spring-boot-oauth2-resourceserver-asymmetric-keys-to-do-the-signing-process\/ \ubaa9\ud45c OAuth2 Resource Server \ub97c \uad6c\ucd95\ud569\ub2c8\ub2e4. \uc18c\uc2a4\ucf54\ub4dc \uc5ec\uae30 \uc5d0 \uc804\uccb4 \uc18c\uc2a4\ucf54\ub4dc\uac00 \uc62c\ub77c\uac00 \uc788\uc2b5\ub2c8\ub2e4. \ud504\ub85c\uc81d\ud2b8 \uc0dd\uc131 \uc2e0\uaddc \ud504\ub85c\uc81d\ud2b8\ub97c \uc0dd\uc131\ud569\ub2c8\ub2e4. \ud504\ub85c\uc81d\ud2b8\uba85\uc740 oauth2resourceserver \ub85c \ud569\ub2c8\ub2e4. \uc758\uc874\uc131\uc740 DevTools, Lombok, Spring Web \uc744 \uc120\ud0dd\ud569\ub2c8\ub2e4. hosts \ud30c\uc77c \uc218\uc815 \ud074\ub77c\uc774\uc5b8\ud2b8\ub294 localhost \ub85c, \uc778\uc99d\uc11c\ubc84\ub294 auth.localhost, \ub9ac\uc18c\uc2a4\uc11c\ubc84\ub294 res.localhost \ub85c \uac01\uac01 \ud560\ub2f9\ud569\ub2c8\ub2e4. 127.0.0.1\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=2405\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29],"tags":[],"class_list":["post-2405","post","type-post","status-publish","format-standard","hentry","category-spring-boot-2-5"],"_links":{"self":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/2405","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2405"}],"version-history":[{"count":15,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/2405\/revisions"}],"predecessor-version":[{"id":2506,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/2405\/revisions\/2506"}],"wp:attachment":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2405"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2405"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2405"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}