{"id":81573,"date":"2026-07-25T02:59:13","date_gmt":"2026-07-24T18:59:13","guid":{"rendered":"https:\/\/www.wsisp.com\/helps\/81573.html"},"modified":"2026-07-25T02:59:13","modified_gmt":"2026-07-24T18:59:13","slug":"springboot-%e9%9b%86%e6%88%90-oauth2-0-%e8%b5%84%e6%ba%90%e6%9c%8d%e5%8a%a1%e5%99%a8%e4%b8%8e%e6%8e%88%e6%9d%83%e6%9c%8d%e5%8a%a1%e5%99%a8","status":"publish","type":"post","link":"https:\/\/www.wsisp.com\/helps\/81573.html","title":{"rendered":"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668"},"content":{"rendered":"<p style=\"text-align: justify\">\u4e0a\u5468\u6709\u4f4d\u8bfb\u8005\u5728\u540e\u53f0\u63d0\u95ee&#xff1a;\u201c\u6211\u4eec\u516c\u53f8\u7684\u7cfb\u7edf\u8981\u5bf9\u5916\u63d0\u4f9b API \u63a5\u53e3&#xff0c;\u5ba2\u6237\u8981\u6c42\u7528 OAuth2.0 \u505a\u5b89\u5168\u8ba4\u8bc1&#xff0c;\u8fd9\u8be5\u600e\u4e48\u5b9e\u73b0\u5462&#xff1f;\u201d<\/p>\n<p style=\"text-align: justify\">\u8fd9\u786e\u5b9e\u662f\u4e2a\u5f88\u5b9e\u9645\u7684\u9700\u6c42\u3002\u968f\u7740\u5fae\u670d\u52a1\u548c\u5f00\u653e\u5e73\u53f0\u7684\u5174\u8d77&#xff0c;OAuth2.0 \u5df2\u7ecf\u6210\u4e3a API \u5b89\u5168\u7684\u4e8b\u5b9e\u6807\u51c6\u3002\u4eca\u5929&#xff0c;\u6211\u5c31\u4ece\u96f6\u5f00\u59cb&#xff0c;\u624b\u628a\u624b\u5e26\u4f60\u5b9e\u73b0 SpringBoot \u4e2d\u7684 OAuth2.0 \u5b8c\u6574\u65b9\u6848\u3002<\/p>\n<h3>\u4e00\u3001OAuth2.0\u4e3a\u4ec0\u4e48\u662f\u201c\u884c\u4e1a\u6807\u51c6\u201d&#xff1f;<\/h3>\n<p style=\"text-align: justify\">\u5728\u5f00\u59cb\u4ee3\u7801\u4e4b\u524d&#xff0c;\u6211\u4eec\u5148\u5feb\u901f\u56de\u987e\u4e00\u4e0b OAuth2.0 \u7684\u6838\u5fc3\u6982\u5ff5\u3002<\/p>\n<p style=\"text-align: justify\">OAuth2.0 \u8981\u89e3\u51b3\u4ec0\u4e48\u95ee\u9898&#xff1f;\u60f3\u8c61\u4e00\u4e0b&#xff0c;\u4f60\u60f3\u7528\u5fae\u4fe1\u767b\u5f55\u67d0\u6b3e App&#xff0c;\u4f46\u53c8\u4e0d\u613f\u628a\u5fae\u4fe1\u5bc6\u7801\u544a\u8bc9\u8fd9\u4e2a App\u3002OAuth2.0 \u5c31\u662f\u89e3\u51b3\u8fd9\u4e2a\u201c\u6388\u6743\u4ee3\u66ff\u8ba4\u8bc1\u201d\u95ee\u9898\u7684\u6807\u51c6\u65b9\u6848\u3002<\/p>\n<p style=\"text-align: justify\">\u56db\u79cd\u6388\u6743\u6a21\u5f0f\u5bf9\u6bd4&#xff1a;<\/p>\n<table>\n<tr>\n<td>\n<p>\u6a21\u5f0f<\/p>\n<\/td>\n<td>\n<p>\u9002\u7528\u573a\u666f<\/p>\n<\/td>\n<td>\n<p>\u5b89\u5168\u7ea7\u522b<\/p>\n<\/td>\n<td>\n<p>\u4f7f\u7528\u9891\u7387<\/p>\n<\/td>\n<\/tr>\n<tbody>\n<tr>\n<td>\n<p>\u6388\u6743\u7801\u6a21\u5f0f<\/p>\n<\/td>\n<td>\n<p>Web \u5e94\u7528\u3001\u79fb\u52a8\u7aef\u5e94\u7528<\/p>\n<\/td>\n<td>\n<p>\u9ad8<\/p>\n<\/td>\n<td>\n<p>\u2605\u2605\u2605\u2605\u2605<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u5bc6\u7801\u6a21\u5f0f<\/p>\n<\/td>\n<td>\n<p>\u81ea\u5bb6\u5e94\u7528&#xff08;\u4fe1\u4efb\u73af\u5883&#xff09;<\/p>\n<\/td>\n<td>\n<p>\u4e2d<\/p>\n<\/td>\n<td>\n<p>\u2605\u2605\u2606\u2606\u2606<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u5ba2\u6237\u7aef\u6a21\u5f0f<\/p>\n<\/td>\n<td>\n<p>\u670d\u52a1\u7aef\u4e4b\u95f4\u8c03\u7528<\/p>\n<\/td>\n<td>\n<p>\u4e2d<\/p>\n<\/td>\n<td>\n<p>\u2605\u2605\u2605\u2606\u2606<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u7b80\u5316\u6a21\u5f0f<\/p>\n<\/td>\n<td>\n<p>\u7eaf\u524d\u7aef\u5e94\u7528&#xff08;\u65e0\u540e\u7aef&#xff09;<\/p>\n<\/td>\n<td>\n<p>\u4f4e<\/p>\n<\/td>\n<td>\n<p>\u2605\u2606\u2606\u2606\u2606<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p style=\"text-align: justify\">\u4eca\u5929\u7684\u5b9e\u6218\u76ee\u6807&#xff1a;\u6211\u4eec\u8981\u642d\u5efa\u4e00\u4e2a\u5b8c\u6574\u7684 OAuth2.0 \u7cfb\u7edf&#xff0c;\u5305\u62ec&#xff1a;<\/p>\n<li>\n<p>1. \u6388\u6743\u670d\u52a1\u5668&#xff08;Authorization Server&#xff09;- \u8d1f\u8d23\u9881\u53d1 Token<\/p>\n<\/li>\n<li>\n<p>2. \u8d44\u6e90\u670d\u52a1\u5668&#xff08;Resource Server&#xff09;- \u8d1f\u8d23\u9a8c\u8bc1 Token \u5e76\u8fd4\u56de\u8d44\u6e90<\/p>\n<\/li>\n<li>\n<p>3. \u5ba2\u6237\u7aef\u5e94\u7528&#xff08;Client Application&#xff09;- \u83b7\u53d6 Token \u5e76\u8bbf\u95ee\u8d44\u6e90<\/p>\n<\/li>\n<hr \/>\n<h3>\u4e8c\u3001\u9879\u76ee\u521d\u59cb\u5316\u4e0e\u73af\u5883\u51c6\u5907<\/h3>\n<h4>2.1 \u521b\u5efa SpringBoot \u9879\u76ee<\/h4>\n<p style=\"text-align: justify\">\u4f7f\u7528 Spring Initializr \u521b\u5efa\u9879\u76ee&#xff0c;\u9009\u62e9\u4ee5\u4e0b\u4f9d\u8d56&#xff1a;<\/p>\n<p>&lt;dependencies&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;!&#8211; SpringBoot Web &#8211;&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;dependency&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/dependency&gt;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&lt;!&#8211; OAuth2 \u6388\u6743\u670d\u52a1\u5668 &#8211;&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;dependency&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;artifactId&gt;spring-boot-starter-oauth2-authorization-server&lt;\/artifactId&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/dependency&gt;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&lt;!&#8211; OAuth2 \u8d44\u6e90\u670d\u52a1\u5668 &#8211;&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;dependency&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;artifactId&gt;spring-boot-starter-oauth2-resource-server&lt;\/artifactId&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/dependency&gt;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&lt;!&#8211; \u6570\u636e\u5e93\u76f8\u5173&#xff08;\u5b58\u50a8\u5ba2\u6237\u7aef\u4fe1\u606f\u548c\u7528\u6237\u4fe1\u606f&#xff09; &#8211;&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;dependency&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;artifactId&gt;spring-boot-starter-data-jpa&lt;\/artifactId&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/dependency&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;dependency&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;groupId&gt;com.mysql&lt;\/groupId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;artifactId&gt;mysql-connector-j&lt;\/artifactId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;scope&gt;runtime&lt;\/scope&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/dependency&gt;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&lt;!&#8211; \u5de5\u5177\u7c7b &#8211;&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;dependency&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;artifactId&gt;lombok&lt;\/artifactId&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;optional&gt;true&lt;\/optional&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/dependency&gt;<br \/>\n&lt;\/dependencies&gt;<\/p>\n<h4>2.2 \u6570\u636e\u5e93\u914d\u7f6e<\/h4>\n<p># application.yml<br \/>\nspring:<br \/>\n\u00a0\u00a0datasource:<br \/>\n\u00a0 \u00a0\u00a0url:\u00a0jdbc:mysql:\/\/localhost:3306\/oauth2_demo?useSSL&#061;false&amp;serverTimezone&#061;UTC<br \/>\n\u00a0 \u00a0\u00a0username:\u00a0root<br \/>\n\u00a0 \u00a0\u00a0password:\u00a0123456<br \/>\n\u00a0 \u00a0\u00a0driver-class-name:\u00a0com.mysql.cj.jdbc.Driver<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0\u00a0jpa:<br \/>\n\u00a0 \u00a0\u00a0hibernate:<br \/>\n\u00a0 \u00a0 \u00a0\u00a0ddl-auto:\u00a0update<br \/>\n\u00a0 \u00a0\u00a0show-sql:\u00a0true<br \/>\n\u00a0 \u00a0\u00a0properties:<br \/>\n\u00a0 \u00a0 \u00a0\u00a0hibernate:<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0dialect:\u00a0org.hibernate.dialect.MySQL8Dialect<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\nserver:<br \/>\n\u00a0\u00a0port:\u00a08080<\/p>\n<hr \/>\n<h3>\u4e09\u3001\u6388\u6743\u670d\u52a1\u5668\u5b9e\u73b0<\/h3>\n<h4>3.1 \u6388\u6743\u670d\u52a1\u5668\u914d\u7f6e\u7c7b<\/h4>\n<p style=\"text-align: justify\">\u6388\u6743\u670d\u52a1\u5668\u662f\u6574\u4e2a OAuth2.0 \u4f53\u7cfb\u7684\u6838\u5fc3&#xff0c;\u8d1f\u8d23\u7ba1\u7406\u5ba2\u6237\u7aef\u3001\u7528\u6237\u8ba4\u8bc1\u548c Token \u9881\u53d1\u3002<\/p>\n<p>&#064;Configuration<br \/>\n&#064;EnableWebSecurity<br \/>\npublic\u00a0class\u00a0AuthorizationServerConfig\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u914d\u7f6e Spring Security<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0&#064;Order(1)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0SecurityFilterChain\u00a0authorizationServerSecurityFilterChain(HttpSecurity http)\u00a0throws\u00a0Exception {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0http<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u5141\u8bb8\u8ba4\u8bc1\u7aef\u70b9\u8868\u5355\u63d0\u4ea4<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .formLogin(Customizer.withDefaults())<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .build();<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u914d\u7f6e\u6388\u6743\u670d\u52a1\u5668<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0RegisteredClientRepository\u00a0registeredClientRepository()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u521b\u5efa\u4e00\u4e2a\u5ba2\u6237\u7aef\u6ce8\u518c\u4fe1\u606f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0RegisteredClient\u00a0client\u00a0&#061;\u00a0RegisteredClient.withId(UUID.randomUUID().toString())<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .clientId(&#034;my-client&#034;) \u00a0\/\/ \u5ba2\u6237\u7aefID<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .clientSecret(&#034;{noop}my-secret&#034;) \u00a0\/\/ \u5ba2\u6237\u7aef\u5bc6\u94a5&#xff0c;{noop}\u8868\u793a\u4e0d\u52a0\u5bc6<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) \u00a0\/\/ \u8ba4\u8bc1\u65b9\u5f0f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) \u00a0\/\/ \u6388\u6743\u7801\u6a21\u5f0f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) \u00a0\/\/ \u652f\u6301\u5237\u65b0Token<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) \u00a0\/\/ \u5ba2\u6237\u7aef\u51ed\u8bc1\u6a21\u5f0f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .redirectUri(&#034;http:\/\/127.0.0.1:8080\/login\/oauth2\/code\/my-client&#034;) \u00a0\/\/ \u56de\u8c03\u5730\u5740<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .redirectUri(&#034;http:\/\/127.0.0.1:8080\/authorized&#034;) \u00a0\/\/ \u6388\u6743\u540e\u7684\u56de\u8c03<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .scope(OidcScopes.OPENID) \u00a0\/\/ OpenID Connect \u652f\u6301<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .scope(&#034;read&#034;) \u00a0\/\/ \u81ea\u5b9a\u4e49scope<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .scope(&#034;write&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .clientSettings(ClientSettings.builder()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .requireAuthorizationConsent(true) \u00a0\/\/ \u9700\u8981\u7528\u6237\u6388\u6743\u786e\u8ba4<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .build())<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .build();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u53ef\u4ee5\u6ce8\u518c\u591a\u4e2a\u5ba2\u6237\u7aef<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0new\u00a0InMemoryRegisteredClientRepository(client);<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u914d\u7f6e JWK Source&#xff08;\u7528\u4e8eJWT\u7b7e\u540d&#xff09;<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0JWKSource&lt;SecurityContext&gt;\u00a0jwkSource()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0RSAKey\u00a0rsaKey\u00a0&#061;\u00a0generateRsa();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0JWKSet\u00a0jwkSet\u00a0&#061;\u00a0new\u00a0JWKSet(rsaKey);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0(jwkSelector, securityContext) -&gt; jwkSelector.select(jwkSet);<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0static\u00a0RSAKey\u00a0generateRsa()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0KeyPair\u00a0keyPair\u00a0&#061;\u00a0generateRsaKey();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0RSAPublicKey\u00a0publicKey\u00a0&#061;\u00a0(RSAPublicKey) keyPair.getPublic();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0RSAPrivateKey\u00a0privateKey\u00a0&#061;\u00a0(RSAPrivateKey) keyPair.getPrivate();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0new\u00a0RSAKey.Builder(publicKey)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .privateKey(privateKey)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .keyID(UUID.randomUUID().toString())<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .build();<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0static\u00a0KeyPair\u00a0generateRsaKey()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 KeyPair keyPair;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0try\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0KeyPairGenerator\u00a0keyPairGenerator\u00a0&#061;\u00a0KeyPairGenerator.getInstance(&#034;RSA&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 keyPairGenerator.initialize(2048);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 keyPair &#061; keyPairGenerator.generateKeyPair();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 }\u00a0catch\u00a0(Exception ex) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0throw\u00a0new\u00a0IllegalStateException(ex);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0keyPair;<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u914d\u7f6e JWT \u89e3\u7801\u5668<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0JwtDecoder\u00a0jwtDecoder(JWKSource&lt;SecurityContext&gt; jwkSource)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>3.2 \u7528\u6237\u4fe1\u606f\u670d\u52a1<\/h4>\n<p style=\"text-align: justify\">\u6388\u6743\u670d\u52a1\u5668\u9700\u8981\u77e5\u9053\u7528\u6237\u4fe1\u606f\u624d\u80fd\u8fdb\u884c\u8ba4\u8bc1&#xff1a;<\/p>\n<p>&#064;Service<br \/>\npublic\u00a0class\u00a0CustomUserDetailsService\u00a0implements\u00a0UserDetailsService\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u5b9e\u9645\u9879\u76ee\u4e2d\u5e94\u8be5\u4ece\u6570\u636e\u5e93\u67e5\u8be2\u7528\u6237\u4fe1\u606f<br \/>\n\u00a0 \u00a0 \u00a0* \u8fd9\u91cc\u4e3a\u4e86\u6f14\u793a&#xff0c;\u4f7f\u7528\u5185\u5b58\u6570\u636e<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;Override<br \/>\n\u00a0 \u00a0\u00a0public\u00a0UserDetails\u00a0loadUserByUsername(String username)\u00a0throws\u00a0UsernameNotFoundException {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u6a21\u62df\u6570\u636e\u5e93\u67e5\u8be2<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 Map&lt;String, UserDetails&gt; users &#061;\u00a0new\u00a0HashMap&lt;&gt;();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u7ba1\u7406\u5458\u7528\u6237<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 users.put(&#034;admin&#034;, User.withUsername(&#034;admin&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .password(&#034;{noop}admin123&#034;) \u00a0\/\/ {noop}\u8868\u793a\u4e0d\u52a0\u5bc6<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .roles(&#034;ADMIN&#034;,\u00a0&#034;USER&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorities(&#034;read&#034;,\u00a0&#034;write&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .build());<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u666e\u901a\u7528\u6237<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 users.put(&#034;user&#034;, User.withUsername(&#034;user&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .password(&#034;{noop}user123&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .roles(&#034;USER&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorities(&#034;read&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .build());<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0UserDetails\u00a0user\u00a0&#061;\u00a0users.get(username);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0if\u00a0(user &#061;&#061;\u00a0null) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0throw\u00a0new\u00a0UsernameNotFoundException(&#034;\u7528\u6237\u4e0d\u5b58\u5728: &#034;\u00a0&#043; username);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0user;<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>3.3 \u914d\u7f6e\u7528\u6237\u8ba4\u8bc1<\/h4>\n<p>&#064;Configuration<br \/>\n&#064;EnableWebSecurity<br \/>\npublic\u00a0class\u00a0DefaultSecurityConfig\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0SecurityFilterChain\u00a0defaultSecurityFilterChain(HttpSecurity http)\u00a0throws\u00a0Exception {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 http<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorizeHttpRequests(authorize -&gt; authorize<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .requestMatchers(&#034;\/oauth2\/**&#034;,\u00a0&#034;\/login\/**&#034;).permitAll() \u00a0\/\/ \u516c\u5f00\u7aef\u70b9<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .anyRequest().authenticated() \u00a0\/\/ \u5176\u4ed6\u9700\u8981\u8ba4\u8bc1<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .formLogin(form -&gt; form<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .loginPage(&#034;\/login&#034;) \u00a0\/\/ \u81ea\u5b9a\u4e49\u767b\u5f55\u9875<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .permitAll()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .csrf(csrf -&gt; csrf.ignoringRequestMatchers(&#034;\/oauth2\/**&#034;)); \u00a0\/\/ \u5ffd\u7565OAuth2\u7aef\u70b9\u7684CSRF<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0http.build();<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0UserDetailsService\u00a0userDetailsService()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0new\u00a0CustomUserDetailsService();<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0PasswordEncoder\u00a0passwordEncoder()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u5b9e\u9645\u9879\u76ee\u4e2d\u5e94\u8be5\u4f7f\u7528 BCryptPasswordEncoder<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0NoOpPasswordEncoder.getInstance(); \u00a0\/\/ \u4ec5\u7528\u4e8e\u6f14\u793a&#xff0c;\u751f\u4ea7\u73af\u5883\u4e0d\u5b89\u5168&#xff01;<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>3.4 \u81ea\u5b9a\u4e49\u767b\u5f55\u9875\u9762<\/h4>\n<p>&lt;!&#8211; src\/main\/resources\/templates\/login.html &#8211;&gt;<br \/>\n&lt;!DOCTYPE\u00a0html&gt;<br \/>\n&lt;html\u00a0xmlns:th&#061;&#034;http:\/\/www.thymeleaf.org&#034;&gt;<br \/>\n&lt;head&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;meta\u00a0charset&#061;&#034;UTF-8&#034;&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;title&gt;OAuth2 \u6388\u6743\u767b\u5f55&lt;\/title&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;style&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0body\u00a0{\u00a0font-family: Arial, sans-serif;\u00a0max-width:\u00a0400px;\u00a0margin:\u00a050px\u00a0auto; }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0.form-group\u00a0{\u00a0margin-bottom:\u00a015px; }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0label\u00a0{\u00a0display: block;\u00a0margin-bottom:\u00a05px; }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0input\u00a0{\u00a0width:\u00a0100%;\u00a0padding:\u00a08px;\u00a0box-sizing: border-box; }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0button\u00a0{\u00a0background:\u00a0#007bff;\u00a0color: white;\u00a0border: none;\u00a0padding:\u00a010px\u00a020px;\u00a0cursor: pointer; }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0.error\u00a0{\u00a0color: red;\u00a0margin-top:\u00a010px; }<br \/>\n\u00a0 \u00a0\u00a0&lt;\/style&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;h2&gt;\u6388\u6743\u767b\u5f55&lt;\/h2&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;div\u00a0th:if&#061;&#034;${param.error}&#034;\u00a0class&#061;&#034;error&#034;&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u7528\u6237\u540d\u6216\u5bc6\u7801\u9519\u8bef&#xff01;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/div&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;form\u00a0th:action&#061;&#034;&#064;{\/login}&#034;\u00a0method&#061;&#034;post&#034;&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;div\u00a0class&#061;&#034;form-group&#034;&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;label&gt;\u7528\u6237\u540d&#xff1a;&lt;\/label&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;input\u00a0type&#061;&#034;text&#034;\u00a0name&#061;&#034;username&#034;\u00a0required&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;\/div&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;div\u00a0class&#061;&#034;form-group&#034;&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;label&gt;\u5bc6\u7801&#xff1a;&lt;\/label&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;input\u00a0type&#061;&#034;password&#034;\u00a0name&#061;&#034;password&#034;\u00a0required&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;\/div&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;button\u00a0type&#061;&#034;submit&#034;&gt;\u767b\u5f55\u5e76\u6388\u6743&lt;\/button&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/form&gt;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&lt;!&#8211; \u663e\u793a\u53ef\u7528\u7684\u6388\u6743\u65b9\u5f0f &#8211;&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;div\u00a0style&#061;&#034;margin-transform: translateY( 30px; border-top: 1px solid\u00a0#eee; padding-top: 20px;&#034;&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;h4&gt;\u652f\u6301\u7684\u6388\u6743\u65b9\u5f0f&#xff1a;&lt;\/h4&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;ul&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;li&gt;\u6388\u6743\u7801\u6a21\u5f0f&#xff1a;\/oauth2\/authorize?response_type&#061;code&amp;client_id&#061;my-client&amp;redirect_uri&#061;http:\/\/127.0.0.1:8080\/authorized&amp;scope&#061;read&lt;\/li&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;li&gt;\u5ba2\u6237\u7aef\u6a21\u5f0f&#xff1a;\/oauth2\/token (\u4f7f\u7528 client_credentials)&lt;\/li&gt;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0&lt;\/ul&gt;<br \/>\n\u00a0 \u00a0\u00a0&lt;\/div&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt;<\/p>\n<p style=\"text-align: justify\">\u521b\u5efa\u767b\u5f55\u63a7\u5236\u5668&#xff1a;<\/p>\n<p>&#064;Controller<br \/>\npublic\u00a0class\u00a0LoginController\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;GetMapping(&#034;\/login&#034;)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0String\u00a0login()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0&#034;login&#034;;<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;GetMapping(&#034;\/authorized&#034;)<br \/>\n\u00a0 \u00a0\u00a0&#064;ResponseBody<br \/>\n\u00a0 \u00a0\u00a0public\u00a0String\u00a0authorized(&#064;RequestParam(required &#061; false)\u00a0String code,\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#064;RequestParam(required &#061; false)\u00a0String error)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0if\u00a0(error !&#061;\u00a0null) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0&#034;\u6388\u6743\u5931\u8d25: &#034;\u00a0&#043; error;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0&#034;\u6388\u6743\u6210\u529f&#xff01;\u6388\u6743\u7801: &#034;\u00a0&#043; code &#043;\u00a0&#034;&lt;br&gt;&#034;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 &#043;\u00a0&#034;\u8bf7\u4f7f\u7528\u6b64\u6388\u6743\u7801\u83b7\u53d6\u8bbf\u95ee\u4ee4\u724c\u3002&#034;;<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<hr \/>\n<h3>\u56db\u3001\u8d44\u6e90\u670d\u52a1\u5668\u5b9e\u73b0<\/h3>\n<p style=\"text-align: justify\">\u8d44\u6e90\u670d\u52a1\u5668\u8d1f\u8d23\u9a8c\u8bc1 Token \u5e76\u4fdd\u62a4 API \u8d44\u6e90\u3002<\/p>\n<h4>4.1 \u8d44\u6e90\u670d\u52a1\u5668\u914d\u7f6e<\/h4>\n<p>&#064;Configuration<br \/>\n&#064;EnableWebSecurity<br \/>\n&#064;EnableMethodSecurity(prePostEnabled &#061; true)\u00a0\u00a0\/\/ \u542f\u7528\u65b9\u6cd5\u7ea7\u5b89\u5168\u63a7\u5236<br \/>\npublic\u00a0class\u00a0ResourceServerConfig\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u8d44\u6e90\u670d\u52a1\u5668\u7684\u5b89\u5168\u914d\u7f6e<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0&#064;Order(2)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0SecurityFilterChain\u00a0resourceServerFilterChain(HttpSecurity http)\u00a0throws\u00a0Exception {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 http<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .securityMatcher(&#034;\/api\/**&#034;) \u00a0\/\/ \u53ea\u4fdd\u62a4 \/api \u5f00\u5934\u7684\u8def\u5f84<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorizeHttpRequests(authorize -&gt; authorize<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .anyRequest().authenticated() \u00a0\/\/ \u9700\u8981\u8ba4\u8bc1<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .oauth2ResourceServer(oauth2 -&gt; oauth2<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .jwt(Customizer.withDefaults()) \u00a0\/\/ \u4f7f\u7528JWT\u4ee4\u724c<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .csrf(csrf -&gt; csrf.disable()); \u00a0\/\/ API\u901a\u5e38\u7981\u7528CSRF<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0http.build();<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u914d\u7f6e JWT \u89e3\u7801\u5668<br \/>\n\u00a0 \u00a0 \u00a0* \u8fd9\u91cc\u9700\u8981\u548c\u6388\u6743\u670d\u52a1\u5668\u7684\u914d\u7f6e\u4e00\u81f4<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0JwtDecoder\u00a0jwtDecoder()\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u4ece\u6388\u6743\u670d\u52a1\u5668\u83b7\u53d6\u516c\u94a5\u6765\u9a8c\u8bc1JWT<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u5b9e\u9645\u9879\u76ee\u4e2d&#xff0c;\u6388\u6743\u670d\u52a1\u5668\u5e94\u8be5\u63d0\u4f9b jwks_uri<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0String\u00a0jwkSetUri\u00a0&#061;\u00a0&#034;http:\/\/localhost:8080\/oauth2\/jwks&#034;;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>4.2 \u53d7\u4fdd\u62a4\u7684 API \u8d44\u6e90<\/h4>\n<p>&#064;RestController<br \/>\n&#064;RequestMapping(&#034;\/api&#034;)<br \/>\npublic\u00a0class\u00a0ResourceController\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u83b7\u53d6\u7528\u6237\u4fe1\u606f&#xff08;\u9700\u8981\u8ba4\u8bc1&#xff09;<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;GetMapping(&#034;\/user\/info&#034;)<br \/>\n\u00a0 \u00a0\u00a0&#064;PreAuthorize(&#034;hasAuthority(&#039;SCOPE_read&#039;)&#034;)\u00a0\u00a0\/\/ \u9700\u8981 read \u6743\u9650<br \/>\n\u00a0 \u00a0\u00a0public\u00a0ResponseEntity&lt;?&gt; getUserInfo(&#064;AuthenticationPrincipal\u00a0Jwt jwt) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u4eceJWT\u4e2d\u63d0\u53d6\u7528\u6237\u4fe1\u606f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0String\u00a0username\u00a0&#061;\u00a0jwt.getClaim(&#034;sub&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0String\u00a0email\u00a0&#061;\u00a0jwt.getClaim(&#034;email&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 Map&lt;String, Object&gt; userInfo &#061;\u00a0new\u00a0HashMap&lt;&gt;();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 userInfo.put(&#034;username&#034;, username);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 userInfo.put(&#034;email&#034;, email !&#061;\u00a0null\u00a0? email : username &#043;\u00a0&#034;&#064;example.com&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 userInfo.put(&#034;userId&#034;, jwt.getClaim(&#034;user_id&#034;));<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 userInfo.put(&#034;authorities&#034;, jwt.getClaim(&#034;scope&#034;));<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 userInfo.put(&#034;issuedAt&#034;, jwt.getIssuedAt());<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 userInfo.put(&#034;expiresAt&#034;, jwt.getExpiresAt());<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0ResponseEntity.ok(Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;code&#034;,\u00a0200,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;message&#034;,\u00a0&#034;\u83b7\u53d6\u7528\u6237\u4fe1\u606f\u6210\u529f&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;data&#034;, userInfo<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 ));<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u521b\u5efa\u8d44\u6e90&#xff08;\u9700\u8981 write \u6743\u9650&#xff09;<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;PostMapping(&#034;\/resource&#034;)<br \/>\n\u00a0 \u00a0\u00a0&#064;PreAuthorize(&#034;hasAuthority(&#039;SCOPE_write&#039;)&#034;)\u00a0\u00a0\/\/ \u9700\u8981 write \u6743\u9650<br \/>\n\u00a0 \u00a0\u00a0public\u00a0ResponseEntity&lt;?&gt; createResource(&#064;RequestBody\u00a0Map&lt;String, Object&gt; resource,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#064;AuthenticationPrincipal\u00a0Jwt jwt) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0String\u00a0username\u00a0&#061;\u00a0jwt.getClaim(&#034;sub&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 Map&lt;String, Object&gt; response &#061;\u00a0new\u00a0HashMap&lt;&gt;();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 response.put(&#034;id&#034;, UUID.randomUUID().toString());<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 response.put(&#034;name&#034;, resource.get(&#034;name&#034;));<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 response.put(&#034;creator&#034;, username);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 response.put(&#034;createdAt&#034;,\u00a0new\u00a0Date());<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 response.put(&#034;message&#034;,\u00a0&#034;\u8d44\u6e90\u521b\u5efa\u6210\u529f&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0ResponseEntity.status(HttpStatus.CREATED).body(Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;code&#034;,\u00a0201,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;message&#034;,\u00a0&#034;\u8d44\u6e90\u521b\u5efa\u6210\u529f&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;data&#034;, response<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 ));<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u516c\u5f00\u63a5\u53e3&#xff08;\u4e0d\u9700\u8981\u8ba4\u8bc1&#xff09;<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;GetMapping(&#034;\/public\/hello&#034;)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0ResponseEntity&lt;?&gt; publicHello() {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0ResponseEntity.ok(Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;code&#034;,\u00a0200,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;message&#034;,\u00a0&#034;\u8fd9\u662f\u4e00\u4e2a\u516c\u5f00\u63a5\u53e3&#xff0c;\u65e0\u9700\u8ba4\u8bc1&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;timestamp&#034;,\u00a0new\u00a0Date()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 ));<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u7ba1\u7406\u63a5\u53e3&#xff08;\u9700\u8981 ADMIN \u89d2\u8272&#xff09;<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;GetMapping(&#034;\/admin\/dashboard&#034;)<br \/>\n\u00a0 \u00a0\u00a0&#064;PreAuthorize(&#034;hasRole(&#039;ADMIN&#039;)&#034;)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0ResponseEntity&lt;?&gt; adminDashboard(&#064;AuthenticationPrincipal\u00a0Jwt jwt) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0ResponseEntity.ok(Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;code&#034;,\u00a0200,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;message&#034;,\u00a0&#034;\u6b22\u8fce\u6765\u5230\u7ba1\u7406\u540e\u53f0&#xff0c;\u7ba1\u7406\u5458&#xff1a;&#034;\u00a0&#043; jwt.getClaim(&#034;sub&#034;),<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;adminData&#034;, Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;totalUsers&#034;,\u00a01500,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;activeSessions&#034;,\u00a042,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;systemStatus&#034;,\u00a0&#034;\u6b63\u5e38&#034;<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 ));<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>4.3 \u5168\u5c40\u5f02\u5e38\u5904\u7406<\/h4>\n<p>&#064;RestControllerAdvice<br \/>\npublic\u00a0class\u00a0GlobalExceptionHandler\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u5904\u7406\u8ba4\u8bc1\u5f02\u5e38<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;ExceptionHandler(AuthenticationException.class)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0ResponseEntity&lt;?&gt; handleAuthenticationException(AuthenticationException e) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;code&#034;,\u00a0401,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;message&#034;,\u00a0&#034;\u8ba4\u8bc1\u5931\u8d25&#xff1a;&#034;\u00a0&#043; e.getMessage(),<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;timestamp&#034;,\u00a0new\u00a0Date()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 ));<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u5904\u7406\u8bbf\u95ee\u88ab\u62d2\u7edd\u5f02\u5e38<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;ExceptionHandler(AccessDeniedException.class)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0ResponseEntity&lt;?&gt; handleAccessDeniedException(AccessDeniedException e) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;code&#034;,\u00a0403,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;message&#034;,\u00a0&#034;\u6743\u9650\u4e0d\u8db3&#xff0c;\u65e0\u6cd5\u8bbf\u95ee\u6b64\u8d44\u6e90&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;timestamp&#034;,\u00a0new\u00a0Date()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 ));<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0\/**<br \/>\n\u00a0 \u00a0 \u00a0* \u5904\u7406 Token \u8fc7\u671f\u6216\u65e0\u6548<br \/>\n\u00a0 \u00a0 \u00a0*\/<br \/>\n\u00a0 \u00a0\u00a0&#064;ExceptionHandler(JwtException.class)<br \/>\n\u00a0 \u00a0\u00a0public\u00a0ResponseEntity&lt;?&gt; handleJwtException(JwtException e) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;code&#034;,\u00a0401,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;message&#034;,\u00a0&#034;Token\u65e0\u6548\u6216\u5df2\u8fc7\u671f&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;error&#034;, e.getMessage(),<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0&#034;timestamp&#034;,\u00a0new\u00a0Date()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 ));<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<hr \/>\n<h3>\u4e94\u3001\u6d4b\u8bd5\u5b8c\u6574\u7684 OAuth2.0 \u6d41\u7a0b<\/h3>\n<h4>5.1 \u542f\u52a8\u5e94\u7528<\/h4>\n<p style=\"text-align: justify\">\u542f\u52a8\u5e94\u7528\u540e&#xff0c;\u8bbf\u95ee&#xff1a;http:\/\/localhost:8080<\/p>\n<h4>5.2 \u6388\u6743\u7801\u6a21\u5f0f\u6d4b\u8bd5<\/h4>\n<p style=\"text-align: justify\">\u6b65\u9aa4 1&#xff1a;\u83b7\u53d6\u6388\u6743\u7801<\/p>\n<p style=\"text-align: justify\">\u6d4f\u89c8\u5668\u8bbf\u95ee\u6388\u6743\u7aef\u70b9&#xff1a;<\/p>\n<p>http:\/\/localhost:8080\/oauth2\/authorize?response_type&#061;code&amp;client_id&#061;my-client&amp;redirect_uri&#061;http:\/\/127.0.0.1:8080\/authorized&amp;scope&#061;read<\/p>\n<p style=\"text-align: justify\">\u7cfb\u7edf\u4f1a\u8df3\u8f6c\u5230\u767b\u5f55\u9875\u9762&#xff0c;\u4f7f\u7528\u4ee5\u4e0b\u4efb\u4e00\u8d26\u53f7\u767b\u5f55&#xff1a;<\/p>\n<ul>\n<li>\n<p>\u2022 \u7528\u6237\u540d&#xff1a;admin&#xff0c;\u5bc6\u7801&#xff1a;admin123<\/p>\n<\/li>\n<li>\n<p>\u2022 \u7528\u6237\u540d&#xff1a;user&#xff0c;\u5bc6\u7801&#xff1a;user123<\/p>\n<\/li>\n<\/ul>\n<p style=\"text-align: justify\">\u767b\u5f55\u6210\u529f\u540e&#xff0c;\u4f1a\u770b\u5230\u6388\u6743\u786e\u8ba4\u9875\u9762&#xff0c;\u70b9\u51fb&#034;\u786e\u8ba4\u6388\u6743&#034;\u540e&#xff0c;\u4f1a\u8df3\u8f6c\u5230\u56de\u8c03\u5730\u5740\u5e76\u663e\u793a\u6388\u6743\u7801\u3002<\/p>\n<p style=\"text-align: justify\">\u6b65\u9aa4 2&#xff1a;\u7528\u6388\u6743\u7801\u83b7\u53d6\u8bbf\u95ee\u4ee4\u724c<\/p>\n<p style=\"text-align: justify\">\u4f7f\u7528 Postman \u6216 curl&#xff1a;<\/p>\n<p>curl &#8211;location\u00a0&#039;http:\/\/localhost:8080\/oauth2\/token&#039;\u00a0\\\\<br \/>\n&#8211;header\u00a0&#039;Content-Type: application\/x-www-form-urlencoded&#039;\u00a0\\\\<br \/>\n&#8211;header\u00a0&#039;Authorization: Basic bXktY2xpZW50Om15LXNlY3JldA&#061;&#061;&#039;\u00a0\\\\<br \/>\n&#8211;data-urlencode\u00a0&#039;grant_type&#061;authorization_code&#039;\u00a0\\\\<br \/>\n&#8211;data-urlencode\u00a0&#039;code&#061;YOUR_AUTHORIZATION_CODE&#039;\u00a0\\\\<br \/>\n&#8211;data-urlencode\u00a0&#039;redirect_uri&#061;http:\/\/127.0.0.1:8080\/authorized&#039;<\/p>\n<p style=\"text-align: justify\">\u6ce8\u610f&#xff1a;Authorization\u00a0\u5934\u662f\u00a0client_id:client_secret\u00a0\u7684 Base64 \u7f16\u7801\u3002<\/p>\n<p style=\"text-align: justify\">\u6b65\u9aa4 3&#xff1a;\u4f7f\u7528\u8bbf\u95ee\u4ee4\u724c\u8c03\u7528 API<\/p>\n<p>curl &#8211;location\u00a0&#039;http:\/\/localhost:8080\/api\/user\/info&#039;\u00a0\\\\<br \/>\n&#8211;header\u00a0&#039;Authorization: Bearer YOUR_ACCESS_TOKEN&#039;<\/p>\n<h4>5.3 \u5ba2\u6237\u7aef\u6a21\u5f0f\u6d4b\u8bd5<\/h4>\n<p>curl &#8211;location\u00a0&#039;http:\/\/localhost:8080\/oauth2\/token&#039;\u00a0\\\\<br \/>\n&#8211;header\u00a0&#039;Content-Type: application\/x-www-form-urlencoded&#039;\u00a0\\\\<br \/>\n&#8211;header\u00a0&#039;Authorization: Basic bXktY2xpZW50Om15LXNlY3JldA&#061;&#061;&#039;\u00a0\\\\<br \/>\n&#8211;data-urlencode\u00a0&#039;grant_type&#061;client_credentials&#039;\u00a0\\\\<br \/>\n&#8211;data-urlencode\u00a0&#039;scope&#061;read&#039;<\/p>\n<h4>5.4 \u5237\u65b0\u4ee4\u724c<\/h4>\n<p style=\"text-align: justify\">\u8bbf\u95ee\u4ee4\u724c\u8fc7\u671f\u540e&#xff0c;\u53ef\u4ee5\u4f7f\u7528\u5237\u65b0\u4ee4\u724c\u83b7\u53d6\u65b0\u7684\u8bbf\u95ee\u4ee4\u724c&#xff1a;<\/p>\n<p>curl &#8211;location\u00a0&#039;http:\/\/localhost:8080\/oauth2\/token&#039;\u00a0\\\\<br \/>\n&#8211;header\u00a0&#039;Content-Type: application\/x-www-form-urlencoded&#039;\u00a0\\\\<br \/>\n&#8211;header\u00a0&#039;Authorization: Basic bXktY2xpZW50Om15LXNlY3JldA&#061;&#061;&#039;\u00a0\\\\<br \/>\n&#8211;data-urlencode\u00a0&#039;grant_type&#061;refresh_token&#039;\u00a0\\\\<br \/>\n&#8211;data-urlencode\u00a0&#039;refresh_token&#061;YOUR_REFRESH_TOKEN&#039;<\/p>\n<hr \/>\n<h3>\u516d\u3001\u9ad8\u7ea7\u914d\u7f6e\u4e0e\u4f18\u5316<\/h3>\n<h4>6.1 \u4f7f\u7528\u6570\u636e\u5e93\u5b58\u50a8\u5ba2\u6237\u7aef\u4fe1\u606f<\/h4>\n<p style=\"text-align: justify\">\u4e0a\u9762\u7684\u4f8b\u5b50\u4f7f\u7528\u4e86\u5185\u5b58\u5b58\u50a8&#xff0c;\u5b9e\u9645\u9879\u76ee\u4e2d\u5e94\u8be5\u4f7f\u7528\u6570\u636e\u5e93&#xff1a;<\/p>\n<p>&#064;Entity<br \/>\n&#064;Table(name &#061; &#034;oauth2_clients&#034;)<br \/>\n&#064;Data<br \/>\npublic\u00a0class\u00a0OAuth2Client\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Id<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String id;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Column(nullable &#061; false, unique &#061; true)<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String clientId;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String clientSecret;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String authenticationMethods; \u00a0\/\/ \u9017\u53f7\u5206\u9694<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String authorizationGrantTypes; \u00a0\/\/ \u9017\u53f7\u5206\u9694<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String redirectUris; \u00a0\/\/ \u9017\u53f7\u5206\u9694<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String scopes; \u00a0\/\/ \u9017\u53f7\u5206\u9694<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0Boolean\u00a0requireProofKey\u00a0&#061;\u00a0false;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0Boolean\u00a0requireAuthorizationConsent\u00a0&#061;\u00a0false;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;CreationTimestamp<br \/>\n\u00a0 \u00a0\u00a0private\u00a0LocalDateTime createTime;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;UpdateTimestamp<br \/>\n\u00a0 \u00a0\u00a0private\u00a0LocalDateTime updateTime;<br \/>\n}&#064;Service<br \/>\n&#064;Transactional<br \/>\npublic\u00a0class\u00a0JpaRegisteredClientRepository\u00a0implements\u00a0RegisteredClientRepository\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Autowired<br \/>\n\u00a0 \u00a0\u00a0private\u00a0OAuth2ClientRepository clientRepository;<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Override<br \/>\n\u00a0 \u00a0\u00a0public\u00a0void\u00a0save(RegisteredClient registeredClient)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u4fdd\u5b58\u5230\u6570\u636e\u5e93<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Override<br \/>\n\u00a0 \u00a0\u00a0public\u00a0RegisteredClient\u00a0findById(String id)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u4ece\u6570\u636e\u5e93\u67e5\u8be2<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Override<br \/>\n\u00a0 \u00a0\u00a0public\u00a0RegisteredClient\u00a0findByClientId(String clientId)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u4ece\u6570\u636e\u5e93\u67e5\u8be2<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>6.2 \u81ea\u5b9a\u4e49 Token \u589e\u5f3a\u5668<\/h4>\n<p style=\"text-align: justify\">\u53ef\u4ee5\u5728 JWT Token \u4e2d\u6dfb\u52a0\u81ea\u5b9a\u4e49\u58f0\u660e&#xff1a;<\/p>\n<p>&#064;Component<br \/>\npublic\u00a0class\u00a0CustomTokenEnhancer\u00a0implements\u00a0OAuth2TokenCustomizer&lt;JwtEncodingContext&gt; {<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Override<br \/>\n\u00a0 \u00a0\u00a0public\u00a0void\u00a0customize(JwtEncodingContext context)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0if\u00a0(context.getPrincipal()\u00a0instanceof\u00a0UsernamePasswordAuthenticationToken) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u6dfb\u52a0\u7528\u6237ID\u548c\u89d2\u8272\u5230Token<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0Authentication\u00a0authentication\u00a0&#061;\u00a0context.getPrincipal();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0UserDetails\u00a0userDetails\u00a0&#061;\u00a0(UserDetails) authentication.getPrincipal();<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 context.getClaims().claims(claims -&gt; {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 claims.put(&#034;user_id&#034;, generateUserId(userDetails.getUsername()));<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 claims.put(&#034;email&#034;, userDetails.getUsername() &#043;\u00a0&#034;&#064;company.com&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u6dfb\u52a0\u81ea\u5b9a\u4e49\u58f0\u660e<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 claims.put(&#034;company&#034;,\u00a0&#034;MyCompany&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 claims.put(&#034;department&#034;,\u00a0&#034;IT&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 });<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0String\u00a0generateUserId(String username)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u751f\u6210\u7528\u6237ID\u7684\u903b\u8f91<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0&#034;user_&#034;\u00a0&#043; username.hashCode();<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<p style=\"text-align: justify\">\u5728\u914d\u7f6e\u4e2d\u6ce8\u518c&#xff1a;<\/p>\n<p>&#064;Bean<br \/>\npublic\u00a0OAuth2AuthorizationServerConfiguration.OAuth2AuthorizationServerConfigurer\u00a0authorizationServerConfigurer(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 RegisteredClientRepository registeredClientRepository,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 CustomTokenEnhancer customTokenEnhancer)\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0return\u00a0OAuth2AuthorizationServerConfiguration<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .authorizationServerConfigurer(registeredClientRepository)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .tokenEnhancer(customTokenEnhancer);<br \/>\n}<\/p>\n<h4>6.3 \u914d\u7f6e Token \u6709\u6548\u671f<\/h4>\n<p>&#064;Bean<br \/>\npublic\u00a0AuthorizationServerSettings\u00a0authorizationServerSettings()\u00a0{<br \/>\n\u00a0 \u00a0\u00a0return\u00a0AuthorizationServerSettings.builder()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .tokenEndpoint(&#034;\/oauth2\/token&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .authorizationEndpoint(&#034;\/oauth2\/authorize&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .tokenIntrospectionEndpoint(&#034;\/oauth2\/introspect&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .tokenRevocationEndpoint(&#034;\/oauth2\/revoke&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .jwkSetEndpoint(&#034;\/oauth2\/jwks&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .oidcUserInfoEndpoint(&#034;\/connect\/userinfo&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .build();<br \/>\n}<\/p>\n<p>&#064;Bean<br \/>\npublic\u00a0TokenSettings\u00a0tokenSettings()\u00a0{<br \/>\n\u00a0 \u00a0\u00a0return\u00a0TokenSettings.builder()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .accessTokenTimeToLive(Duration.ofHours(2)) \u00a0\/\/ \u8bbf\u95ee\u4ee4\u724c2\u5c0f\u65f6\u8fc7\u671f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .refreshTokenTimeToLive(Duration.ofDays(7)) \u00a0\/\/ \u5237\u65b0\u4ee4\u724c7\u5929\u8fc7\u671f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .authorizationCodeTimeToLive(Duration.ofMinutes(5)) \u00a0\/\/ \u6388\u6743\u78015\u5206\u949f\u8fc7\u671f<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .reuseRefreshTokens(false) \u00a0\/\/ \u4e0d\u91cd\u590d\u4f7f\u7528\u5237\u65b0\u4ee4\u724c<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 .build();<br \/>\n}<\/p>\n<hr \/>\n<h3>\u4e03\u3001\u6ce8\u610f\u4e8b\u9879<\/h3>\n<h4>7.1 \u5b89\u5168\u6700\u4f73\u5b9e\u8df5<\/h4>\n<li>\n<p>1.\u00a0\u4e0d\u8981\u4f7f\u7528 {noop} \u5bc6\u7801\u7f16\u7801\u5668<\/p>\n<p>&#064;Bean<br \/>\npublic\u00a0PasswordEncoder\u00a0passwordEncoder()\u00a0{<br \/>\n\u00a0 \u00a0\u00a0return\u00a0new\u00a0BCryptPasswordEncoder(); \u00a0\/\/ \u751f\u4ea7\u73af\u5883\u4f7f\u7528<br \/>\n}<\/li>\n<li>\n<p>2.\u00a0\u4f7f\u7528 HTTPS\u751f\u4ea7\u73af\u5883\u5fc5\u987b\u4f7f\u7528 HTTPS&#xff0c;\u9632\u6b62 Token \u88ab\u7a83\u53d6\u3002<\/p>\n<\/li>\n<li>\n<p>3.\u00a0\u9650\u5236\u91cd\u5b9a\u5411 URI<\/p>\n<p>RegisteredClient\u00a0client\u00a0&#061;\u00a0RegisteredClient.withId(&#034;1&#034;)<br \/>\n\u00a0 \u00a0 .clientId(&#034;web-app&#034;)<br \/>\n\u00a0 \u00a0 .redirectUris(uris -&gt; {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 uris.add(&#034;https:\/\/production.com\/callback&#034;);<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\/\/ \u4e0d\u8981\u4f7f\u7528\u901a\u914d\u7b26\u6216\u8fc7\u4e8e\u5bbd\u677e\u7684\u89c4\u5219<br \/>\n\u00a0 \u00a0 })<br \/>\n\u00a0 \u00a0 .build();<\/li>\n<li>\n<p>4.\u00a0\u5b9a\u671f\u8f6e\u6362\u5bc6\u94a5JWK \u7b7e\u540d\u5bc6\u94a5\u5e94\u5b9a\u671f\u8f6e\u6362\u3002<\/p>\n<\/li>\n<h4>7.2 \u76d1\u63a7\u4e0e\u65e5\u5fd7<\/h4>\n<p>&#064;Component<br \/>\npublic\u00a0class\u00a0OAuth2AuditLogger\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0private\u00a0static\u00a0final\u00a0Logger\u00a0log\u00a0&#061;\u00a0LoggerFactory.getLogger(&#034;OAUTH2_AUDIT&#034;);<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;EventListener<br \/>\n\u00a0 \u00a0\u00a0public\u00a0void\u00a0onAuthorizationSuccess(AuthorizationSuccessEvent event)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 log.info(&#034;\u6388\u6743\u6210\u529f &#8211; \u5ba2\u6237\u7aef: {}, \u7528\u6237: {}, \u8303\u56f4: {}&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 event.getAuthorization().getRegisteredClientId(),<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 event.getAuthorization().getPrincipalName(),<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 event.getAuthorization().getAuthorizedScopes());<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;EventListener<br \/>\n\u00a0 \u00a0\u00a0public\u00a0void\u00a0onAuthorizationFailure(AuthorizationErrorEvent event)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 log.warn(&#034;\u6388\u6743\u5931\u8d25 &#8211; \u9519\u8bef: {}, \u5ba2\u6237\u7aef: {}&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 event.getError(),<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 event.getAuthorization().getRegisteredClientId());<br \/>\n\u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;EventListener<br \/>\n\u00a0 \u00a0\u00a0public\u00a0void\u00a0onTokenIssued(OAuth2TokenIssuedEvent event)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 log.info(&#034;Token\u7b7e\u53d1 &#8211; \u7c7b\u578b: {}, \u5ba2\u6237\u7aef: {}&#034;,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 event.getToken().getClass().getSimpleName(),<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 event.getAuthorization().getRegisteredClientId());<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>7.3 \u6027\u80fd\u4f18\u5316<\/h4>\n<li>\n<p>1.\u00a0\u4f7f\u7528 Redis \u7f13\u5b58\u4ee4\u724c<\/p>\n<p>&#064;Bean<br \/>\npublic\u00a0OAuth2AuthorizationService\u00a0authorizationService(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 RegisteredClientRepository registeredClientRepository,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 RedisConnectionFactory redisConnectionFactory)\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0return\u00a0new\u00a0RedisOAuth2AuthorizationService(<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 redisConnectionFactory,<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 registeredClientRepository);<br \/>\n}<\/li>\n<li>\n<p>2.\u00a0\u542f\u7528\u54cd\u5e94\u5f0f\u652f\u6301<\/p>\n<p>&#064;Configuration<br \/>\n&#064;EnableWebFluxSecurity<br \/>\npublic\u00a0class\u00a0ReactiveResourceServerConfig\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0SecurityWebFilterChain\u00a0springSecurityFilterChain(ServerHttpSecurity http)\u00a0{<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 http<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorizeExchange(exchanges -&gt; exchanges<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .anyExchange().authenticated()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .oauth2ResourceServer(oauth2 -&gt; oauth2<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .jwt(Customizer.withDefaults())<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 );<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0http.build();<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/li>\n<hr \/>\n<h3>\u516b\u3001\u5e38\u89c1\u95ee\u9898\u4e0e\u89e3\u51b3\u65b9\u6848<\/h3>\n<h4>Q1: \u5982\u4f55\u652f\u6301\u591a\u79cd\u8ba4\u8bc1\u65b9\u5f0f&#xff1f;<\/h4>\n<p style=\"text-align: justify\">A: \u5728\u00a0RegisteredClient\u00a0\u4e2d\u914d\u7f6e\u591a\u79cd\u8ba4\u8bc1\u65b9\u6cd5&#xff1a;<\/p>\n<p>.clientAuthenticationMethods(methods -&gt; {<br \/>\n\u00a0 \u00a0 methods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);<br \/>\n\u00a0 \u00a0 methods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);<br \/>\n\u00a0 \u00a0 methods.add(ClientAuthenticationMethod.CLIENT_SECRET_JWT);<br \/>\n\u00a0 \u00a0 methods.add(ClientAuthenticationMethod.PRIVATE_KEY_JWT);<br \/>\n})<\/p>\n<h4>Q2: \u5982\u4f55\u5b9e\u73b0\u5355\u70b9\u767b\u5f55&#xff08;SSO&#xff09;&#xff1f;<\/h4>\n<p style=\"text-align: justify\">A: Spring Security \u652f\u6301 OAuth2 \u5355\u70b9\u767b\u5f55&#xff1a;<\/p>\n<p>&#064;Configuration<br \/>\n&#064;EnableWebSecurity<br \/>\npublic\u00a0class\u00a0OAuth2LoginConfig\u00a0{<br \/>\n\u00a0 \u00a0\u00a0<br \/>\n\u00a0 \u00a0\u00a0&#064;Bean<br \/>\n\u00a0 \u00a0\u00a0public\u00a0SecurityFilterChain\u00a0filterChain(HttpSecurity http)\u00a0throws\u00a0Exception {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 http<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .oauth2Login(oauth2 -&gt; oauth2<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .loginPage(&#034;\/login&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .defaultSuccessUrl(&#034;\/home&#034;)<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 )<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .authorizeHttpRequests(authz -&gt; authz<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .anyRequest().authenticated()<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 );<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0return\u00a0http.build();<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/p>\n<h4>Q3: \u5982\u4f55\u96c6\u6210\u7b2c\u4e09\u65b9\u767b\u5f55&#xff08;\u5fae\u4fe1\u3001GitHub\u7b49&#xff09;&#xff1f;<\/h4>\n<p style=\"text-align: justify\">A: \u6dfb\u52a0\u5bf9\u5e94\u7684\u4f9d\u8d56\u548c\u914d\u7f6e&#xff1a;<\/p>\n<p>spring:<br \/>\n\u00a0\u00a0security:<br \/>\n\u00a0 \u00a0\u00a0oauth2:<br \/>\n\u00a0 \u00a0 \u00a0\u00a0client:<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0registration:<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0github:<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0client-id:\u00a0YOUR_GITHUB_CLIENT_ID<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0client-secret:\u00a0YOUR_GITHUB_CLIENT_SECRET<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0wechat:<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0client-id:\u00a0YOUR_WECHAT_APPID<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0client-secret:\u00a0YOUR_WECHAT_SECRET<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0authorization-grant-type:\u00a0authorization_code<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0scope:\u00a0snsapi_userinfo<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0client-name:\u00a0\u5fae\u4fe1<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0provider:\u00a0wechat<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0provider:<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0wechat:<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0authorization-uri:\u00a0https:\/\/open.weixin.qq.com\/connect\/oauth2\/authorize<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0token-uri:\u00a0https:\/\/api.weixin.qq.com\/sns\/oauth2\/access_token<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0user-info-uri:\u00a0https:\/\/api.weixin.qq.com\/sns\/userinfo<\/p>\n<hr \/>\n<h3>\u5199\u5728\u6700\u540e<\/h3>\n<p style=\"text-align: justify\">\u901a\u8fc7\u4eca\u5929\u7684\u6587\u7ae0&#xff0c;\u6211\u4eec\u5b8c\u6574\u5b9e\u73b0\u4e86 SpringBoot \u96c6\u6210 OAuth2.0 \u7684\u6388\u6743\u670d\u52a1\u5668\u548c\u8d44\u6e90\u670d\u52a1\u5668\u3002\u603b\u7ed3\u4e00\u4e0b\u5173\u952e\u70b9&#xff1a;<\/p>\n<li>\n<p>1.\u00a0\u6388\u6743\u670d\u52a1\u5668\u8d1f\u8d23\u9881\u53d1 Token&#xff0c;\u652f\u6301\u591a\u79cd\u6388\u6743\u6a21\u5f0f<\/p>\n<\/li>\n<li>\n<p>2.\u00a0\u8d44\u6e90\u670d\u52a1\u5668\u8d1f\u8d23\u9a8c\u8bc1 Token \u5e76\u4fdd\u62a4 API<\/p>\n<\/li>\n<li>\n<p>3.\u00a0JWT Token\u00a0\u662f\u65e0\u72b6\u6001\u7684&#xff0c;\u9002\u5408\u5206\u5e03\u5f0f\u7cfb\u7edf<\/p>\n<\/li>\n<li>\n<p>4.\u00a0Spring Security OAuth2\u00a0\u63d0\u4f9b\u4e86\u5b8c\u6574\u7684\u89e3\u51b3\u65b9\u6848<\/p>\n<\/li>\n<p style=\"text-align: justify\">OAuth2.0 \u770b\u4f3c\u590d\u6742&#xff0c;\u4f46\u7406\u89e3\u4e86\u6838\u5fc3\u6982\u5ff5\u540e&#xff0c;\u7ed3\u5408 Spring Security \u7684\u81ea\u52a8\u5316\u914d\u7f6e&#xff0c;\u5b9e\u73b0\u8d77\u6765\u5e76\u4e0d\u56f0\u96be\u3002\u5efa\u8bae\u4f60\u5148\u5728\u672c\u5730\u8fd0\u884c\u8fd9\u4e2a\u793a\u4f8b&#xff0c;\u7136\u540e\u6839\u636e\u5b9e\u9645\u4e1a\u52a1\u9700\u6c42\u8fdb\u884c\u8c03\u6574\u3002<\/p>\n<hr \/>\n<p style=\"text-align: justify\">\u539f\u521b\u4e0d\u6613&#xff0c;\u5982\u679c\u89c9\u5f97\u6709\u5e2e\u52a9&#xff0c;\u6b22\u8fce\u70b9\u8d5e\u3001\u5728\u770b\u6216\u5206\u4eab\u7ed9\u4f60\u7684\u5c0f\u4f19\u4f34~<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u4e0a\u5468\u6709\u4f4d\u8bfb\u8005\u5728\u540e\u53f0\u63d0\u95ee&#xff1a;\u201c\u6211\u4eec\u516c\u53f8\u7684\u7cfb\u7edf\u8981\u5bf9\u5916\u63d0\u4f9b API \u63a5\u53e3&#xff0c;\u5ba2\u6237\u8981\u6c42\u7528 OAuth2.0 \u505a\u5b89\u5168\u8ba4\u8bc1&#xff0c;\u8fd9\u8be5\u600e\u4e48\u5b9e\u73b0\u5462&#xff1f;\u201d\u8fd9\u786e\u5b9e\u662f\u4e2a\u5f88\u5b9e\u9645\u7684\u9700\u6c42\u3002\u968f\u7740\u5fae\u670d\u52a1\u548c\u5f00\u653e\u5e73\u53f0\u7684\u5174\u8d77&#xff0c;OAuth2.0 \u5df2\u7ecf\u6210\u4e3a API \u5b89\u5168\u7684\u4e8b\u5b9e\u6807\u51c6\u3002\u4eca\u5929&#xff0c;\u6211\u5c31\u4ece\u96f6\u5f00\u59cb&#xff0c;\u624b\u628a\u624b\u5e26\u4f60\u5b9e\u73b0 SpringBoot \u4e2d\u7684 OAuth2.0 \u5b8c\u6574\u65b9\u6848\u3002\u4e00\u3001OAuth2.0\u4e3a\u4ec0\u4e48\u662f\u201c\u884c\u4e1a\u6807\u51c6\u201d&#xff1f;\u5728\u5f00<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[99,101,98,292,43],"topic":[],"class_list":["post-81573","post","type-post","status-publish","format-standard","hentry","category-server","tag-java","tag-spring","tag-spring-boot","tag-292","tag-43"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.wsisp.com\/helps\/81573.html\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"og:description\" content=\"\u4e0a\u5468\u6709\u4f4d\u8bfb\u8005\u5728\u540e\u53f0\u63d0\u95ee&#xff1a;\u201c\u6211\u4eec\u516c\u53f8\u7684\u7cfb\u7edf\u8981\u5bf9\u5916\u63d0\u4f9b API \u63a5\u53e3&#xff0c;\u5ba2\u6237\u8981\u6c42\u7528 OAuth2.0 \u505a\u5b89\u5168\u8ba4\u8bc1&#xff0c;\u8fd9\u8be5\u600e\u4e48\u5b9e\u73b0\u5462&#xff1f;\u201d\u8fd9\u786e\u5b9e\u662f\u4e2a\u5f88\u5b9e\u9645\u7684\u9700\u6c42\u3002\u968f\u7740\u5fae\u670d\u52a1\u548c\u5f00\u653e\u5e73\u53f0\u7684\u5174\u8d77&#xff0c;OAuth2.0 \u5df2\u7ecf\u6210\u4e3a API \u5b89\u5168\u7684\u4e8b\u5b9e\u6807\u51c6\u3002\u4eca\u5929&#xff0c;\u6211\u5c31\u4ece\u96f6\u5f00\u59cb&#xff0c;\u624b\u628a\u624b\u5e26\u4f60\u5b9e\u73b0 SpringBoot \u4e2d\u7684 OAuth2.0 \u5b8c\u6574\u65b9\u6848\u3002\u4e00\u3001OAuth2.0\u4e3a\u4ec0\u4e48\u662f\u201c\u884c\u4e1a\u6807\u51c6\u201d&#xff1f;\u5728\u5f00\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.wsisp.com\/helps\/81573.html\" \/>\n<meta property=\"og:site_name\" content=\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-24T18:59:13+00:00\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/81573.html\",\"url\":\"https:\/\/www.wsisp.com\/helps\/81573.html\",\"name\":\"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"isPartOf\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\"},\"datePublished\":\"2026-07-24T18:59:13+00:00\",\"dateModified\":\"2026-07-24T18:59:13+00:00\",\"author\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/81573.html#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.wsisp.com\/helps\/81573.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/81573.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.wsisp.com\/helps\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\",\"url\":\"https:\/\/www.wsisp.com\/helps\/\",\"name\":\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"description\":\"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"contentUrl\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"caption\":\"admin\"},\"sameAs\":[\"http:\/\/wp.wsisp.com\"],\"url\":\"https:\/\/www.wsisp.com\/helps\/author\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.wsisp.com\/helps\/81573.html","og_locale":"zh_CN","og_type":"article","og_title":"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","og_description":"\u4e0a\u5468\u6709\u4f4d\u8bfb\u8005\u5728\u540e\u53f0\u63d0\u95ee&#xff1a;\u201c\u6211\u4eec\u516c\u53f8\u7684\u7cfb\u7edf\u8981\u5bf9\u5916\u63d0\u4f9b API \u63a5\u53e3&#xff0c;\u5ba2\u6237\u8981\u6c42\u7528 OAuth2.0 \u505a\u5b89\u5168\u8ba4\u8bc1&#xff0c;\u8fd9\u8be5\u600e\u4e48\u5b9e\u73b0\u5462&#xff1f;\u201d\u8fd9\u786e\u5b9e\u662f\u4e2a\u5f88\u5b9e\u9645\u7684\u9700\u6c42\u3002\u968f\u7740\u5fae\u670d\u52a1\u548c\u5f00\u653e\u5e73\u53f0\u7684\u5174\u8d77&#xff0c;OAuth2.0 \u5df2\u7ecf\u6210\u4e3a API \u5b89\u5168\u7684\u4e8b\u5b9e\u6807\u51c6\u3002\u4eca\u5929&#xff0c;\u6211\u5c31\u4ece\u96f6\u5f00\u59cb&#xff0c;\u624b\u628a\u624b\u5e26\u4f60\u5b9e\u73b0 SpringBoot \u4e2d\u7684 OAuth2.0 \u5b8c\u6574\u65b9\u6848\u3002\u4e00\u3001OAuth2.0\u4e3a\u4ec0\u4e48\u662f\u201c\u884c\u4e1a\u6807\u51c6\u201d&#xff1f;\u5728\u5f00","og_url":"https:\/\/www.wsisp.com\/helps\/81573.html","og_site_name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","article_published_time":"2026-07-24T18:59:13+00:00","author":"admin","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"admin","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"10 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.wsisp.com\/helps\/81573.html","url":"https:\/\/www.wsisp.com\/helps\/81573.html","name":"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","isPartOf":{"@id":"https:\/\/www.wsisp.com\/helps\/#website"},"datePublished":"2026-07-24T18:59:13+00:00","dateModified":"2026-07-24T18:59:13+00:00","author":{"@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41"},"breadcrumb":{"@id":"https:\/\/www.wsisp.com\/helps\/81573.html#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.wsisp.com\/helps\/81573.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.wsisp.com\/helps\/81573.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.wsisp.com\/helps"},{"@type":"ListItem","position":2,"name":"SpringBoot \u96c6\u6210 OAuth2.0 \u8d44\u6e90\u670d\u52a1\u5668\u4e0e\u6388\u6743\u670d\u52a1\u5668"}]},{"@type":"WebSite","@id":"https:\/\/www.wsisp.com\/helps\/#website","url":"https:\/\/www.wsisp.com\/helps\/","name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","description":"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41","name":"admin","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","contentUrl":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","caption":"admin"},"sameAs":["http:\/\/wp.wsisp.com"],"url":"https:\/\/www.wsisp.com\/helps\/author\/admin"}]}},"_links":{"self":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/81573","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/comments?post=81573"}],"version-history":[{"count":0,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/81573\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media?parent=81573"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/categories?post=81573"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/tags?post=81573"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/topic?post=81573"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}