Springboot 系列 (6) - Springboot+OAuth2(一) | 使用 Security 搭建基于内存验证的授权服务器

 

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。Spring Boot 给 Spring Security 提供了自动化配置方案 (spring-boot-starter-security),可以零配置使用 Spring Security。

OAuth 2.0 是 OAuth 协议的延续版本,但不向前兼容 OAuth 1.0 (即完全废止了 OAuth 1.0)。OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

比如:一个网站,允许用户不注册为该网站用户,而是使用微信授权后登录该网站。微信在这个流程中就处于 OAuth2 server(授权服务器)的位置,这个网站处于 OAuth client,这个网站(OAuth client)和微信(OAuth server)属于信任关系,带着微信授权令牌的用户,也被这个网站信任。

1. OAuth 简介

    1) OAuth 基本概念

        (1) Third-party Application:第三方应用程序,本文中又称 "客户端"(client);
        (2) HTTP Service:HTTP 服务提供商,本文中简称 "服务提供商";
        (3) Resource Owner:资源所有者,本文中又称 "用户"(user);
        (4) User Agent:用户代理,本文中就是指浏览器;
        (5) Authorization Server:认证服务器,即服务提供商专门用来处理认证的服务器。
        (6) Resource Server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

      OAuth 在 "客户端"与 "服务提供商" 之间,设置了一个授权层(Authorization Layer)。"客户端" 不能直接登录 "服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端" 登录授权层所用的令牌(Token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。

      "客户端" 登录授权层以后,"服务提供商" 根据令牌的权限范围和有效期,向 "客户端" 开放用户储存的资料。

    2) 授权模式

        授权码模式(Authorization Code)、简化模式(Implicit)、密码模式(Resource Owner Password Credentials)、客户端模式(Client Credentials)

        (1) 授权码模式(Authorization Code)

            a) 用户访问客户端,后者将前者导向认证服务器;
            b) 用户选择是否给予客户端授权;
            c) 假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(Redirection URI),同时附上一个授权码;
            d) 客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见;
            e) 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(Access Token)和更新令牌(Refresh Token);

            注:授权码模式是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与 "服务提供商" 的认证服务器进行互动。

      (2) 简化模式(Implicit)

            a) 用户决定是否给于客户端授权;
            b) 假设用户给予授权,认证服务器将用户导向客户端指定的 "重定向 URI",并在 URI 的 Hash 部分包含了访问令牌;
            c) 浏览器向资源服务器发出请求,其中不包括上一步收到的 Hash 值;
            d) 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌;
            e) 浏览器执行上一步获得的脚本,提取出令牌;
            f) 浏览器将令牌发给客户端;

            注: 简化模式 (或称隐式模式),不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了 "授权码" 这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

       (3) 密码模式(Resource Owner Password Credentials)

            a) 用户向客户端提供用户名和密码;
            b) 客户端将用户名和密码发给认证服务器,向后者请求令牌;
            c) 认证服务器确认无误后,向客户端提供访问令牌;

            注:密码模式,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向 "服务商提供商" 索要授权。

        (4) 客户端模式(Client Credentials)

            a) 客户端向认证服务器进行身份认证,并要求一个访问令牌;
            b) 认证服务器确认无误后,向客户端提供访问令牌;

            注:客户端模式指客户端以自己的名义,而不是以用户的名义,向 "服务提供商" 进行认证。严格地说,客户端模式并不属于 OAuth 框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求 "服务提供商" 提供服务,其实不存在授权问题。

    OAuth 的客户端凭证校验是通过 ClientDetailsService 来实现的,OAuth 默认为我们提供了InMemoryClientDetailsService (内存验证) 和 JdbcClientDetailsService(JDBC验证),也可以实现自定义的 ClientDetailsService (自定义验证)。
    
    本文将搭建一个基于内存验证的授权服务器。


2. 开发环境

    Windows版本:Windows 10 Home (20H2)   
    IntelliJ IDEA (https://www.jetbrains.com/idea/download/):Community Edition for Windows 2020.1.4
    Apache Maven (https://maven.apache.org/):3.8.1

    注:Spring 开发环境的搭建,可以参考 “ Spring基础知识(1)- Spring简介、Spring体系结构和开发环境配置 ”。


3. 创建 Spring Boot 基础项目

    项目实例名称:SpringbootExample06
    Spring Boot 版本:2.6.6

    创建步骤:

        (1) 创建 Maven 项目实例 SpringbootExample06;
        (2) Spring Boot Web 配置;
        (3) 导入 Thymeleaf 依赖包;
        (4) 配置 jQuery;
        
    具体操作请参考 “Springboot 系列 (2) - 在 Spring Boot 项目里使用 Thymeleaf、JQuery+Bootstrap 和国际化” 里的项目实例 SpringbootExample02,文末包含如何使用 spring-boot-maven-plugin 插件运行打包的内容。

    SpringbootExample06 和 SpringbootExample02 相比,SpringbootExample06 不配置 Bootstrap、模版文件(templates/*.html)和国际化。


4. 导入 Security、 OAuth2 依赖包

    修改 pom.xml

 1         <project ... >
 2             ...
 3             <dependencies>
 4                 ...
 5 
 6                 <!-- Spring security -->
 7                 <dependency>
 8                     <groupId>org.springframework.boot</groupId>
 9                     <artifactId>spring-boot-starter-security</artifactId>
10                 </dependency>
11                 <!-- OAuth2 -->
12                 <dependency>
13                     <groupId>org.springframework.security.oauth</groupId>
14                     <artifactId>spring-security-oauth2</artifactId>
15                     <version>2.3.4.RELEASE</version>
16                 </dependency>
17 
18                 ...
19             </dependencies>
20 
21             ...
22         </project>


    在IDE中项目列表 -> SpringbootExample06 -> 点击鼠标右键 -> Maven -> Reload Project


5. 配置 Security & OAuth2 (基于内存验证)

    1) 创建 src/main/java/com/example/config/WebSecurityConfig.java 文件

 1         package com.example.config;
 2 
 3         import org.springframework.context.annotation.Bean;
 4         import org.springframework.context.annotation.Configuration;
 5         import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 6         import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 7         import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 8         import org.springframework.security.authentication.AuthenticationManager;
 9         import org.springframework.security.oauth2.provider.token.TokenStore;
10         import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
11 
12         @Configuration
13         public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
14 
15             @Override
16             protected void configure(AuthenticationManagerBuilder auth) throws Exception {
17                 // 内存模式
18                 auth.inMemoryAuthentication()
19                         .withUser("admin").password("{noop}123456").roles("ADMIN")
20                         .and()
21                         .withUser("user").password("{noop}123456").roles("USER");
22             }
23 
24             @Override
25             protected void configure(HttpSecurity http) throws Exception {
26                 // 配置认证
27                 http.authorizeRequests()
28                         .antMatchers("/error", "/lib/**", "/oauth/**").permitAll()
29                         .anyRequest().authenticated()
30                         .and()
31                         .formLogin()
32                         .and()
33                         .csrf().disable(); // 关闭 csrf 保护功能,默认是开启的
34             }
35 
36             @Bean
37             public AuthenticationManager authenticationManagerBean() throws Exception {
38                 return super.authenticationManagerBean();
39             }
40 
41             @Bean
42             public TokenStore tokenStoreBean() {
43                 // token 保存在内存中(也可以保存在数据库、Redis 中)。
44                 // 如果保存在中间件(数据库、Redis),那么资源服务器与认证服务器可以不在同一个工程中。
45                 // 注意:如果不保存 access_token,则没法通过 access_token 获取被保护资源
46                 return new InMemoryTokenStore();
47             }
48         }


        注:{noop} 表示 NoOpPasswordEncoder,{noop} 配合没有加过密的密码使用;{bcrypt} 表示 BCryptPasswordEncoder,{bcrypt} 配合加密过的密码使用;其它加密方式,参考两者的命名方式用小写字母。

        这里定义的 AuthenticationManager、tokenStoreBean 是Spring Bean,在 AuthorizationServerConfig 里会装配使用。

    2) 创建 src/main/java/com/example/config/oauth2/AuthorizationServerConfig.java 文件

 1         package com.example.config.oauth2;
 2 
 3         import org.springframework.context.annotation.Configuration;
 4         import org.springframework.beans.factory.annotation.Autowired;
 5         import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
 6         import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
 7         import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
 8         import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
 9         import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
10         import org.springframework.security.authentication.AuthenticationManager;
11         import org.springframework.security.oauth2.provider.token.TokenStore;
12 
13         @Configuration
14         @EnableAuthorizationServer
15         public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter  {
16             @Autowired
17             private AuthenticationManager authenticationManager;
18             @Autowired
19             private TokenStore tokenStore;
20             
21             @Override
22             public void configure(AuthorizationServerSecurityConfigurer authServer) {
23                 // 访问权限控制
24                 authServer.tokenKeyAccess("permitAll()")
25                          .checkTokenAccess("isAuthenticated()")
26                         .allowFormAuthenticationForClients();
27             }
28 
29             @Override
30             public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
31 
32                 // 使内存模式
33                 clients.inMemory().withClient("1")
34                                     .secret("{noop}4eti4hAaux")
35                                     .authorizedGrantTypes("authorization_code", "refresh_token")
36                                     .scopes("All")
37                                     .autoApprove(true)
38                                     .redirectUris("/oauth/test/code/callback")
39                                     .and()
40                                     .withClient("2")
41                                     .secret("{noop}xGJoD2i2lj")
42                                     .authorizedGrantTypes("implicit")
43                                     .scopes("All")
44                                     .autoApprove(true)
45                                     .redirectUris("/oauth/test/implicit/callback")
46                                     .and()
47                                     .withClient("3")
48                                     .secret("{noop}2lo2ijxJ3e")
49                                     .authorizedGrantTypes("password", "client_credentials")
50                                     .scopes("All")
51                                     .autoApprove(true);
52             }
53 
54             @Override
55             public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
56 
57                 endpoints.authenticationManager(authenticationManager);
58                 endpoints.tokenStore(tokenStore);
59 
60             }
61 
62         }

 

  注:.checkTokenAccess("isAuthenticated()") 表示是访问 /oauth/check_token 时,需要使用 client_id 和 client_secret 验证,如无需验证,可设置为 "permitAll()" 。

 

6. 测试实例 (Web 模式)

    1) 授权码模式(Authorization Code)实例

        (1) 创建 src/main/resources/templates/authorization_code.html 文件

  1             <html lang="en" xmlns:th="http://www.thymeleaf.org">
  2             <head>
  3                 <meta charset="UTF-8">
  4                 <title th:text="${var}">Title</title>
  5                 <script language="javascript" th:src="@{/lib/jquery/jquery-3.6.0.min.js}"></script>
  6             </head>
  7             <body>
  8 
  9                 <h4>OAuth 2.0 - Authorization Code</h4>
 10 
 11                 <p>Token URL: <br><input type="text" name="url" id="url" style="width: 50%;" th:value="@{/oauth/token}" value="" /></p>
 12                 <p>Client Id: <br><input type="text" name="client_id" id="client_id" style="width: 50%;" value="1" /></p>
 13                 <p>Client Secret: <br><input type="text" name="client_secret" id="client_secret" style="width: 50%;" value="4eti4hAaux" /></p>
 14                 <p>Code: <br><textarea name="code" id="code" rows="5" style="width: 50%;" readonly th:text="${code}"></textarea></p>
 15 
 16                 <p>&nbsp;</p>
 17                 <p>
 18                     <button type="button" id="btn_get_token" th:unless="${code}==null">Get Token</button>
 19                     <button type="button" id="btn_refresh_code" th:if="${code}==null">Refresh Code</button>
 20                     <br><br><textarea name="result" id="result" style="width: 50%;" rows="8"></textarea>
 21                 </p>
 22 
 23                 <p>&nbsp;</p>
 24                 <script type="text/javascript">
 25 
 26                     var tokenObj = null;
 27 
 28                     $(document).ready(function(){
 29 
 30                         $('#btn_get_token').click(function() {
 31 
 32                             var url = $('#url').val();
 33                             if (url == '') {
 34                                 alert('Please enter url');
 35                                 $('#url').focus();
 36                                 return;
 37                             }
 38 
 39                             var client_id = $('#client_id').val();
 40                             if (client_id == '') {
 41                                 alert('Please enter client id');
 42                                 $('#client_id').focus();
 43                                 return;
 44                             }
 45 
 46                             var client_secret = $('#client_secret').val();
 47                             if (client_secret == '') {
 48                                 alert('Please enter client secret');
 49                                 $('#client_secret').focus();
 50                                 return;
 51                             }
 52 
 53                             var code = $('#code').val();
 54                             if (code == '') {
 55                                 alert('Please enter code');
 56                                 $('#code').focus();
 57                                 return;
 58                             }
 59 
 60                             $('#result').val('Sending post ...');
 61 
 62                             $.ajax({
 63 
 64                                 type: 'POST',
 65                                 url: url,
 66                                 data: {
 67                                     grant_type: 'authorization_code',
 68                                     client_id: client_id,
 69                                     client_secret: client_secret,
 70                                     redirect_uri: '/oauth/test/code/callback',
 71                                     code: code,
 72                                 },
 73                                 success: function(response) {
 74 
 75                                     console.log(response);
 76                                     $('#result').val(JSON.stringify(response));
 77 
 78                                     tokenObj = response;
 79 
 80                                 },
 81                                 error: function() {
 82 
 83                                     $('#result').val('Error: AJAX issue');
 84 
 85                                     tokenObj = null;
 86 
 87                                 }
 88                             });
 89 
 90                         });
 91 
 92                         $('#btn_refresh_code').click(function() {
 93                             window.location.href = "/oauth/test/code";
 94                         });
 95 
 96                     });
 97 
 98                 </script>
 99 
100             </body>
101             </html>


        (2) 创建 src/main/java/com/example/controller/OauthController.java 文件

 1             package com.example.controller;
 2 
 3             import org.springframework.stereotype.Controller;
 4             import org.springframework.web.bind.annotation.RequestMapping;
 5             import org.springframework.web.bind.annotation.RequestParam;
 6             import org.springframework.ui.Model;
 7 
 8             @Controller
 9             @RequestMapping("/oauth")
10             public class OauthController {
11 
12                 @RequestMapping("/test/code")
13                 public String testCode() {
14                     return "redirect:/oauth/authorize?client_id=1&redirect_uri=/oauth/test/code/callback&response_type=code";
15                 }
16 
17                 @RequestMapping("/test/code/callback")
18                 public String testCodeCallback(@RequestParam String code, Model model) {
19                     model.addAttribute("code", code);
20                     return "authorization_code";
21                 }
22             }


            运行并访问 http://localhost:9090/oauth/test/code,自动跳转到 http://localhost:9090/login (Spring security 的默认页面)。
            
            输入上文 WebSecurityConfig 里配置的用户名/密码 (admin/123456),登录后跳转到 http://localhost:9090/test/code/callback,点击 Get Token 按钮,会在按钮下方显示如下数据:

                {
                    "access_token": "0cea651f-4d01-43c0-9421-bc70d4eca081",
                    "token_type": "bearer",
                    "refresh_token": "a3418c82-5f80-4ee8-a1dd-9bdbfce52bdc",
                    "expires_in": 43199,
                    "scope": "All"
                }

    可以访问 http://localhost:9090/oauth/check_token?token=0cea651f-4d01-43c0-9421-bc70d4eca081 来检测 access_token 的状态。


    2) 简化模式(Implicit)实例

        (1) 创建 src/main/resources/templates/implicit.html 文件

 1             <html lang="en" xmlns:th="http://www.thymeleaf.org">
 2             <head>
 3                 <meta charset="UTF-8">
 4                 <title th:text="${var}">Title</title>
 5                 <script language="javascript" th:src="@{/lib/jquery/jquery-3.6.0.min.js}"></script>
 6             </head>
 7             <body>
 8 
 9                 <h4>OAuth 2.0 - Implicit</h4>
10 
11                 <p id="message"></p>
12 
13                 <script type="text/javascript">
14 
15                     function QueryStringToJSON(str) {
16                         var pairs = str.split('&');
17                         var result = {};
18                         pairs.forEach(function (pair) {
19                             pair = pair.split('=');
20                             var name = pair[0]
21                             var value = pair[1]
22                             if (name.length)
23                                 if (result[name] !== undefined) {
24                                     if (!result[name].push) {
25                                         result[name] = [result[name]];
26                                     }
27                                     result[name].push(value || '');
28                                 } else {
29                                     result[name] = value || '';
30                                 }
31                         });
32                         return (result);
33                     }
34 
35                     $(document).ready(function() {
36 
37                         var urlObj = window.location.href.split("#");
38                         if (urlObj.length > 1) {
39 
40                             var str = '';
41                             var obj = QueryStringToJSON(urlObj[1]);
42                             for (k in obj) {
43                                 str += k + ': ' + obj[k] + '<br>';
44                             }
45 
46                             $('#message').html(str);
47 
48                         }
49 
50                     });
51 
52                 </script>
53             </body>
54             </html>


        (2) 修改 src/main/java/com/example/controller/OauthController.java 文件

 1             package com.example.controller;
 2 
 3             import org.springframework.stereotype.Controller;
 4             import org.springframework.web.bind.annotation.RequestMapping;
 5             import org.springframework.web.bind.annotation.RequestParam;
 6             import org.springframework.ui.Model;
 7 
 8             @Controller
 9             @RequestMapping("/oauth")
10             public class OauthController {
11 
12                 ...
13 
14                 @RequestMapping("/test/implicit")
15                 public String testImplicit() {
16                     return "redirect:/oauth/authorize?client_id=2&client_secret=xGJoD2i2lj&grant_type=implicit&response_type=token";
17                 }
18 
19                 @RequestMapping("/test/implicit/callback")
20                 public String testImplicitCallback(HttpServletRequest request) {
21                     return "implicit";
22                 }
23 
24             }


            运行并访问 http://localhost:9090/oauth/test/implicit,自动跳转到 http://localhost:9090/login (Spring security 的默认页面)。
            
            输入上文 WebSecurityConfig 里配置的用户名和密码登录,登录后跳转到 http://localhost:9090/test/implicit/callback#access_token=52856cfb-29ab-46dc-87af-06a7d025bb77&token_type=bearer&expires_in=40739&scope=All,解析 “#” 符号之后的数据,显示如下:

                OAuth 2.0 - Implicit

                access_token: 52856cfb-29ab-46dc-87af-06a7d025bb77
                token_type: bearer
                expires_in: 40739
                scope: All


    3) 密码模式(Resource Owner Password Credentials)实例

        (1) 创建 src/main/resources/templates/password.html 文件

 1             <html lang="en" xmlns:th="http://www.thymeleaf.org">
 2             <head>
 3                 <meta charset="UTF-8">
 4                 <title th:text="${var}">Title</title>
 5                 <script language="javascript" th:src="@{/lib/jquery/jquery-3.6.0.min.js}"></script>
 6             </head>
 7             <body>
 8 
 9                 <h4>OAuth 2.0 - Password</h4>
10 
11                 <p>Token URL: <br><input type="text" name="url" id="url" style="width: 50%;" th:value="@{/oauth/token}" value="" /></p>
12                 <p>Client Id: <br><input type="text" name="client_id" id="client_id" style="width: 50%;" value="3" /></p>
13                 <p>Client Secret: <br><input type="text" name="client_secret" id="client_secret" style="width: 50%;" value="2lo2ijxJ3e" /></p>
14                 <p>Username: <br><input type="text" name="username" id="username" style="width: 50%;" value="admin" /></p>
15                 <p>Password: <br><input type="text" name="password" id="password" style="width: 50%;" value="123456" /></p>
16 
17                 <p>&nbsp;</p>
18                 <p>
19                     <button type="button" id="btn_get_token" >Get Token</button>
20                     <br><br><textarea name="result" id="result" style="width: 50%;" rows="8"></textarea>
21                 </p>
22 
23                 <script type="text/javascript">
24 
25                     $(document).ready(function() {
26 
27                     $('#btn_get_token').click(function() {
28 
29                             var url = $('#url').val();
30                             if (url == '') {
31                                 alert('Please enter url');
32                                 $('#url').focus();
33                                 return;
34                             }
35 
36                             var client_id = $('#client_id').val();
37                             if (client_id == '') {
38                                 alert('Please enter client id');
39                                 $('#client_id').focus();
40                                 return;
41                             }
42 
43                             var client_secret = $('#client_secret').val();
44                             if (client_secret == '') {
45                                 alert('Please enter client secret');
46                                 $('#client_secret').focus();
47                                 return;
48                             }
49 
50                             var username = $('#username').val();
51                             if (username == '') {
52                                 alert('Please enter username');
53                                 $('#username').focus();
54                                 return;
55                             }
56 
57                             var password = $('#password').val();
58                             if (password == '') {
59                                 alert('Please enter password');
60                                 $('#password').focus();
61                                 return;
62                             }
63 
64                             $('#result').val('Sending post ...');
65 
66                             $.ajax({
67 
68                                 type: 'POST',
69                                 url: url,
70                                 data: {
71                                     grant_type: 'password',
72                                     client_id: client_id,
73                                     client_secret: client_secret,
74                                     username: username,
75                                     password: password,
76                                 },
77                                 success: function(response) {
78 
79                                     console.log(response);
80                                     $('#result').val(JSON.stringify(response));
81 
82                                 },
83                                 error: function() {
84 
85                                     $('#result').val('Error: AJAX issue');
86 
87                                 }
88                             });
89 
90                         });
91 
92                     });
93 
94                 </script>
95             </body>
96             </html>


        (2) 修改 src/main/java/com/example/controller/OauthController.java 文件

 1             package com.example.controller;
 2 
 3             import org.springframework.stereotype.Controller;
 4             import org.springframework.web.bind.annotation.RequestMapping;
 5             import org.springframework.web.bind.annotation.RequestParam;
 6             import org.springframework.ui.Model;
 7 
 8             @Controller
 9             @RequestMapping("/oauth")
10             public class OauthController {
11 
12                 ...
13 
14                 @RequestMapping("/test/password")
15                 public String testPassword() {
16                     return "password";
17                 }
18 
19             }


            运行并访问 http://localhost:9090/oauth/test/password ,点击 Get Token 按钮,会在按钮下方显示如下JSON 格式数据:
            
                {"access_token":"00f15603-786e-46d1-a091-8169b3bb2e94","token_type":"bearer","expires_in":43200,"scope":"All"}


    4) 客户端模式(Client Credentials)实例

        (1) 创建 src/main/resources/templates/client.html 文件

 1             <html lang="en" xmlns:th="http://www.thymeleaf.org">
 2             <head>
 3                 <meta charset="UTF-8">
 4                 <title th:text="${var}">Title</title>
 5                 <script language="javascript" th:src="@{/lib/jquery/jquery-3.6.0.min.js}"></script>
 6             </head>
 7             <body>
 8 
 9                 <h4>OAuth 2.0 - Client</h4>
10 
11                 <p>Token URL: <br><input type="text" name="url" id="url" style="width: 50%;" th:value="@{/oauth/token}" value="" /></p>
12                 <p>Client Id: <br><input type="text" name="client_id" id="client_id" style="width: 50%;" value="3" /></p>
13                 <p>Client Secret: <br><input type="text" name="client_secret" id="client_secret" style="width: 50%;" value="2lo2ijxJ3e" /></p>
14 
15                 <p>&nbsp;</p>
16                 <p>
17                     <button type="button" id="btn_get_token" >Get Token</button>
18                     <br><br><textarea name="result" id="result" style="width: 50%;" rows="8"></textarea>
19                 </p>
20 
21                 <script type="text/javascript">
22 
23                     $(document).ready(function() {
24 
25                     $('#btn_get_token').click(function() {
26 
27                             var url = $('#url').val();
28                             if (url == '') {
29                                 alert('Please enter url');
30                                 $('#url').focus();
31                                 return;
32                             }
33 
34                             var client_id = $('#client_id').val();
35                             if (client_id == '') {
36                                 alert('Please enter client id');
37                                 $('#client_id').focus();
38                                 return;
39                             }
40 
41                             var client_secret = $('#client_secret').val();
42                             if (client_secret == '') {
43                                 alert('Please enter client secret');
44                                 $('#client_secret').focus();
45                                 return;
46                             }
47 
48                             $('#result').val('Sending post ...');
49 
50                             $.ajax({
51 
52                                 type: 'POST',
53                                 url: url,
54                                 data: {
55                                     grant_type: 'client_credentials',
56                                     client_id: client_id,
57                                     client_secret: client_secret,
58                                 },
59                                 success: function(response) {
60 
61                                     console.log(response);
62                                     $('#result').val(JSON.stringify(response));
63 
64                                 },
65                                 error: function() {
66 
67                                     $('#result').val('Error: AJAX issue');
68 
69                                 }
70                             });
71 
72                         });
73 
74                     });
75 
76                 </script>
77             </body>
78             </html>


        (2) 修改 src/main/java/com/example/controller/OauthController.java 文件

 1             package com.example.controller;
 2 
 3             import org.springframework.stereotype.Controller;
 4             import org.springframework.web.bind.annotation.RequestMapping;
 5             import org.springframework.web.bind.annotation.RequestParam;
 6             import org.springframework.ui.Model;
 7 
 8             @Controller
 9             @RequestMapping("/oauth")
10             public class OauthController {
11 
12                 ...
13 
14                 @RequestMapping("/test/client")
15                 public String testClient() {
16                     return "client";
17                 }
18 
19             }


            运行并访问 http://localhost:9090/oauth/test/client ,点击 Get Token 按钮,会在按钮下方显示如下JSON 格式数据:
            
                {"access_token":"91fae539-6d24-4075-8a39-4eb15d197645","token_type":"bearer","expires_in":43199,"scope":"All"}


--------------------------------------

示例代码:https://gitee.com/slksm/public-codes/tree/master/demos/springboot-series/SpringbootExample06

 

posted @ 2022-05-14 16:47  垄山小站  阅读(1288)  评论(0编辑  收藏  举报