qiuxuhui

导航

OAuth2 授权码模式

3. 授权码模式

  示例代码对应仓库:

本小节,我们来学习授权码模式(Authorization Code)

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

  一般情况下,在有客户端的情况下,我们与第三方平台常常采用这种方式。

 

 

 

  • (A)用户访问客户端,后者将前者跳转到到授权服务器。
  • (B)用户选择是否给予客户端授权。
  • (C)假设用户给予授权,授权服务器将跳转到客户端事先指定的"重定向 URI"(Redirection URI),同时附上一个授权码
  • (D)客户端收到授权码,附上早先的"重定向 URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
  • (E)认证服务器核对了授权码重定向 URI,确认无误后,向客户端发送访问令牌

 

下面,我们来新建两个项目,搭建一个授权码模式的使用示例。如下图所示:

 

 

 

 

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 用户认证 Manager
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    //配置使用的 AuthenticationManager 实现用户认证的功能
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }

    //设置 /oauth/check_token 端点,通过认证后可访问。
    //这里的认证,指的是使用 client-id + client-secret 进行的客户端认证,不要和用户认证混淆。
    //其中,/oauth/check_token 端点对应 CheckTokenEndpoint 类,用于校验访问令牌的有效性。
    //在客户端访问资源服务器时,会在请求中带上访问令牌。
    //在资源服务器收到客户端的请求时,会使用请求中的访问令牌,找授权服务器确认该访问令牌的有效性。
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.checkTokenAccess("isAuthenticated()");
    }

    //进行 Client 客户端的配置。
    //设置使用基于内存的 Client 存储器。实际情况下,最好放入数据库中,方便管理。
/*
*
* 创建一个 Client 配置。如果要继续添加另外的 Client 配置,可以在 <4.3> 处使用 #and() 方法继续拼接。
* 注意,这里的 .withClient("clientapp").secret("112233") 代码段,就是 client-id 和 client-secret。
*补充知识:可能会有胖友会问,为什么要创建 Client 的 client-id 和 client-secret 呢?
*通过 client-id 编号和 client-secret,授权服务器可以知道调用的来源以及正确性。这样,
*即使“坏人”拿到 Access Token ,但是没有 client-id 编号和 client-secret,也不能和授权服务器发生有效的交互。
*/
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() // <4.1>
                .withClient("clientapp").secret("112233") // <4.2> Client 账号、密码。
                .authorizedGrantTypes("authorization_code") // <4.2> 授权码模式
                .redirectUris("http://127.0.0.1:9090/callback")
                .scopes("read_userinfo", "read_contacts") // <4.2> 可授权的 Scope
//                .and().withClient() // <4.3> 可以继续配置新的 Client
        ;
    }

}

  

 

仅仅需要修改 OAuth2AuthorizationServerConfig 类,设置使用 "authorization_code" 授权码模式,并设置回调地址。

  注意,这里设置的回调地址,稍后我们会在「3.2 搭建资源服务器」中实现。

 

3.1.1 简单测试

  执行 AuthorizationServerApplication 启动授权服务器。

① 使用浏览器,访问 http://127.0.0.1:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://127.0.0.1:9090/callback&response_type=code&scope=read_userinfo 地址,获取授权。请求参数说明如下:

  • client_id 参数,必传,为我们在 OAuth2AuthorizationServer 中配置的 Client 的编号。
  • redirect_uri 参数,可选,回调地址。当然,如果 client_id 对应的 Client 未配置 redirectUris 属性,会报错。
  • response_type 参数,必传,返回结果为 code 授权码
  • scope 参数,可选,申请授权的 Scope 。如果多个,使用逗号分隔。
  • state 参数,可选,表示客户端的当前状态,可以指定任意值,授权服务器会原封不动地返回这个值。

  友情提示:state 参数,未在上述 URL 中体现出来。

 

因为我们并未登录授权服务器,所以被拦截跳转到登录界面。如下图所示:

 

 

 

② 输入用户的账号密码「yunai/1024」进行登录。登录完成后,进入授权界面。如下图所示:

    和我们日常使用的腾讯 QQ、微信、微博等等三方登录,是一模一样的,除了丑了点,嘿嘿~

 

 

③ 选择 scope.read_userinfo 为 Approve 允许,点击「Authorize」按钮,完成授权操作。浏览器自动重定向到 Redirection URI 地址,并且在 URI 上可以看到 code 授权码。如下图所示:

 

 

  友情提示:/oauth/authorize 对应 AuthorizationEndpoint 端点。

 

④ 因为我们暂时没有启动资源服务器,所以显示无法访问。这里,我们先使用 Postman 模拟请求 http://localhost:8080/oauth/token 地址,使用授权码获取到访问令牌。如下图所示:

 

 

 

 

 

 

 请求说明:

  • 通过 Basic Auth 的方式,填写 client-id + client-secret 作为用户名与密码,实现 Client 客户端有效性的认证。
  • 请求参数 grant_type 为 "authorization_code",表示使用授权码模式
  • 请求参数 code,从授权服务器获取到的授权码
  • 请求参数 redirect_uri,Client 客户端的 Redirection URI 地址。

注意,授权码仅能使用一次,重复请求会报 Invalid authorization code: 错误。如下图所示:

 

 

 

3.2 搭建资源服务器

复制 lab-68-demo02-resource-server 项目,主要是提供回调地址。如下图所示:

 

 

① 新建 CallbackController 类,提供 /callback 回调地址。

② 在 OAuth2ResourceServerConfig 配置类中,设置 /callback 回调地址无需权限验证,不然回调都跳转不过来哈。

 

3.2.1 CallbackController

创建 CallbackController 类,提供 /callback 回调地址,在获取到授权码时,请求授权服务器,通过授权码获取访问令牌。代码如下:

@RestController
@RequestMapping("/")
public class CallbackController {

    @Autowired
    private OAuth2ClientProperties oauth2ClientProperties;

    @Value("${security.oauth2.access-token-uri}")
    private String accessTokenUri;

    @GetMapping("/callback")
    public OAuth2AccessToken login(@RequestParam("code") String code) {
        // 创建 AuthorizationCodeResourceDetails 对象
        AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails();
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientId(oauth2ClientProperties.getClientId());
        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());
        // 创建 OAuth2RestTemplate 对象
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
        restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAuthorizationCode(code); // <1> 设置 code
        restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setPreservedState("http://127.0.0.1:9090/callback"); // <2> 通过这个方式,设置 redirect_uri 参数
        restTemplate.setAccessTokenProvider(new AuthorizationCodeAccessTokenProvider());
        // 获取访问令牌
        return restTemplate.getAccessToken();
    }

}

  

代码比较简单,还是使用 OAuth2RestTemplate 进行请求授权服务器,胖友自己瞅瞅哈。

需要注意的是 <1> 和 <2> 处,设置请求授权服务器需要的 code 和 redirect_uri 参数。

 

3.2.2 简单测试

执行 ResourceServerApplication 启动资源服务器。

重复「3.2.1 简单测试」的过程,成功获取到访问令牌。如下图所示:

 

posted on 2021-01-15 15:26  qiuxuhui  阅读(3125)  评论(0编辑  收藏  举报