写点什么

详细介绍 OAuth2

  • 2021 年 11 月 12 日
  • 本文字数:10338 字

    阅读完需:约 34 分钟

| 字段名 | 字段说明 |


| --- | :-- |


| client_id | 主键,必须唯一,不能为空. 用于唯一标识每一个客户端(client); 在注册时必须填写(也可由服务 端自动生成). 对于不同的 grant_type,该字段都是必须的. 在实际应用中的另一个名称叫 appKey,与 client_id 是同一个概念. |


| resource_ids | 客户端所能访问的资源 id 集合,多个资源时用逗号(,)分隔,如: “unity-resource,mobile- resource”. 该字段的值必须来源于与 security.xml 中标签?oauth2:resource-server 的属性 resource-id 值一致. 在 security.xml 配置有几个?oauth2:resource-server 标签, 则该字段可以 使用几个该值. 在实际应用中, 我们一般将资源进行分类,并分别配置对应 的?oauth2:resource-server,如订单资源配置一个?oauth2:resource-server, 用户资源又配置 一个?oauth2:resource-server. 当注册客户端时,根据实际需要可选择资源 id,也可根据不同的 注册流程,赋予对应的资源 id. |


| client_secret | 用于指定客户端(client)的访问密匙; 在注册时必须填写(也可由服务端自动生成). 对于不同的 grant_type,该字段都是必须的. 在实际应用中的另一个名称叫 appSecret,与 client_secret 是 同一个概念. |


| scope | 指定客户端申请的权限范围,可选值包括 read,write,trust;若有多个权限范围用逗号(,)分隔,如: “read,write”. scope 的值与 security.xml 中配置的?intercept-url 的 access 属性有关系. 如?intercept-url 的配置为?intercept-url pattern="/m/**" access=“ROLE_MOBILE,SCOPE_READ”/>则说明访问该 URL 时的客户端必须有 read 权限范 围. write 的配置值为 SCOPE_WRITE, trust 的配置值为 SCOPE_TRUST. 在实际应该中, 该值一 般由服务端指定, 常用的值为 read,write. |


| authorized_grant_types | 指定客户端支持的 grant_type,可选值包括 authorization_code,password,refresh_token,implicit,client_credentials, 若支持多个 grant_type 用逗号(,)分隔,如: “authorization_code,password”. 在实际应用中,当注册时,该字 段是一般由服务器端指定的,而不是由申请者去选择的,最常用的 grant_type 组合有: “authorization_code,refresh_token”(针对通过浏览器访问的客户端); “password,refresh_token”(针对移动设备的客户端). implicit 与 client_credentials 在实际中 很少使用. |


| web_server_redirect_uri | 客户端的重定向 URI,可为空, 当 grant_type 为 authorization_code 或 implicit 时, 在 Oauth 的流 程中会使用并检查与注册时填写的 redirect_uri 是否一致. 下面分别说明:当 grant_type=authorization_code 时, 第一步 从 spring-oauth-server 获取 'code’时客户端发 起请求时必须有 redirect_uri 参数, 该参数的值必须与 web_server_redirect_uri 的值一致. 第 二步 用 ‘code’ 换取 ‘access_token’ 时客户也必须传递相同的 redirect_uri. 在实际应用中, web_server_redirect_uri 在注册时是必须填写的, 一般用来处理服务器返回的 code, 验证 state 是否合法与通过 code 去换取 access_token 值.在 spring-oauth-client 项目中, 可具体参考 AuthorizationCodeController.java 中的 authorizationCodeCallback 方法.当 grant_type=implicit 时通过 redirect_uri 的 hash 值来传递 access_token 值. 如:http://localhost:7777/spring-oauth-client/implicit#access_token=dc891f4a-ac88- 4ba6-8224-a2497e013865&token_type=bearer&expires_in=43199 然后客户端通过 JS 等从 hash 值中取到 access_token 值. |


| authorities | 指定客户端所拥有的 Spring Security 的权限值,可选, 若有多个权限值,用逗号(,)分隔, 如: "ROLE_ |


| access_token_validity | 设定客户端的 access_token 的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间 值(60 * 60 * 12, 12 小时). 在服务端获取的 access_token JSON 数据中的 expires_in 字段的值 即为当前 access_token 的有效时间值. 在项目中, 可具体参考 DefaultTokenServices.java 中属 性 accessTokenValiditySeconds. 在实际应用中, 该值一般是由服务端处理的, 不需要客户端 自定义.refresh_token_validity 设定客户端的 refresh_token 的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间值(60 * 60 * 24 * 30, 30 天). 若客户端的 grant_type 不包 括 refresh_token,则不用关心该字段 在项目中, 可具体参考 DefaultTokenServices.java 中属 性 refreshTokenValiditySeconds. 在实际应用中, 该值一般是由服务端处理的, 不需要客户端 自定义. |


| additional_information | 这是一个预留的字段,在 Oauth 的流程中没有实际的使用,可选,但若设置值,必须是 JSON 格式的 数据,如:{“country”:“CN”,“country_code”:“086”}按照 spring-security-oauth 项目中对该字段 的描述 Additional information for this client, not need by the vanilla OAuth protocol but might be useful, for example,for storing descriptive information. (详见 ClientDetails.java 的 getAdditionalInformation()方法的注释)在实际应用中, 可以用该字段来 存储关于客户端的一些其他信息,如客户端的国家,地区,注册时的 IP 地址等等.create_time 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |


| archived | 用于标识客户端是否已存档(即实现逻辑删除),默认值为’0’(即未存档). 对该字段的具体使用请 参考 CustomJdbcClientDetailsService.java,在该类中,扩展了在查询 client_details 的 SQL 加上 archived = 0 条件 (扩展字段) |


| trusted | 设置客户端是否为受信任的,默认为’0’(即不受信任的,1 为受信任的). 该字段只适用于 grant_type="authorization_code"的情况,当用户登录成功后,若该值为 0,则会跳转到让用户 Approve 的页面让用户同意授权, 若该字段为 1,则在登录后不需要再让用户 Approve 同意授权 (因为是受信任的). 对该字段的具体使用请参考 OauthUserApprovalHandler.java. (扩展字 段) |


| autoapprove | 设置用户是否自动 Approval 操作, 默认值为 ‘false’, 可选值包括 ‘true’,‘false’, ‘read’,‘write’. 该 字段只适用于 grant_type="authorization_code"的情况,当用户登录成功后,若该值为’true’或 支持的 scope 值,则会跳过用户 Approve 的页面, 直接授权. 该字段与 trusted 有类似的功能, 是 spring-security-oauth2 的 2.0 版本后添加的新属性. 在项目中,主要操作 oauth_client_details 表的类是 JdbcClientDetailsService.java, 更多的细节请参考该类. 也可 以根据实际的需要,去扩展或修改该类的实现. |

5.2oauth_client_token

| 字段名 | 字段说明 |


| --- | :-- |


| create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |


| token_id | 从服务器端获取到的 access_token 的值. |


| token | 这是一个二进制的字段, 存储的数据是 OAuth2AccessToken.java 对象序列化后的二进制数据. |


| authentication_id | 该字段具有唯一性, 是根据当前的 username(如果有),client_id 与 scope 通过 MD5 加密生成的. 具体实现请参考 DefaultClientKeyGenerator.java 类. |


| user_name | 登录时的用户名 |


| client_id | |


该表用于在客户端系统中存储从服务端获取的 token 数据, 在 spring-oauth-server 项目中未使用到. 对 oauth_client_token 表的主要操作在 JdbcClientTokenServices.java 类中, 更多的细节请参考该类.

5.3oauth_access_token

| 字段名 | 字段说明 |


| --- | :-- |


| create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |


| token_id | 从服务器端获取到的 access_token 的值. |


| token | 这是一个二进制的字段, 存储的数据是 OAuth2AccessToken.java 对象序列化后的二进制数据. |


| authentication_id | 该字段具有唯一性, 是根据当前的 username(如果有),client_id 与 scope 通过 MD5 加密生成的. 具体实现请参考 DefaultClientKeyGenerator.java 类. |


| user_name | 登录时的用户名 |


| client_id | |


| authentication | 存储将 OAuth2Authentication.java 对象序列化后的二进制数据. |


| refresh_token | 该字段的值是将 refresh_token 的值通过 MD5 加密后存储的. 在项目中,主要操作 oauth_access_token 表的对象是 JdbcTokenStore.java. 更多的细节请参考该类 |

5.4oauth_refresh_token

| 字段名 | 字段说明 |


| --- | :-- |


| create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |


| token_id | 该字段的值是将 refresh_token 的值通过 MD5 加密后存储的. |


| token | 存储将 OAuth2RefreshToken.java 对象序列化后的二进制数据 |


| authentication | 存储将 OAuth2RefreshToken.java 对象序列化后的二进制数据 |

5.5oauth_code

| 字段名 | 字段说明 |


| --- | :-- |


| create_time | 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段) |


| code | 存储服务端系统生成的 code 的值(未加密). |


| authentication | 存储将 AuthorizationRequestHolder.java 对象序列化后的二进制数据. |


二、OAuth2.0 实战案例


=============================================================================


本案例同样通过 maven 的聚合工程来实现。


1.创建父工程





设置 pom 文件


<parent>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-parent</artifactId>


<version>2.1.3.RELEASE</version>


<relativePath/>


</parent>


<properties>


<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>


</properties>


<dependencyManagement>


<dependencies>


<dependency>


<groupId>org.springframework.cloud</groupId>


<artifactId>spring-cloud-dependencies</artifactId>


<version>${spring-cloud.version}</version>


<type>pom</type>


<scope>import</scope>


</dependency>


</dependencies>


</dependencyManagement>


<repositories>


<repository>


<id>spring-snapshots</id>


<name>Spring Snapshots</name>


<url>https://repo.spring.io/snapshot</url>


<snapshots>


<enabled>true</enabled>


</snapshots>


</repository>


<repository>


<id>spring-milestones</id>


<name>Spring Milestones</name>


<url>https://repo.spring.io/milestone</url>


<snapshots>


<enabled>false</enabled>


</snapshots>


</repository>


</repositories>


2.创建资源项目




接下来创建我们的资源项目

2.1 创建项目

2.2 导入依赖

<dependencies>


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-web</artifactId>


</dependency>


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-security</artifactId>


</dependency>


<dependency>


<groupId>org.springframework.security.oauth.boot</groupId>


<artifactId>spring-security-oauth2-autoconfigure</artifactId>


<version>2.1.0.RELEASE</version>


</dependency>


<dependency>


<groupId>org.springframework.cloud</groupId>


<artifactId>spring-cloud-starter-oauth2</artifactId>


<version>2.1.0.RELEASE</version>


<exclusions>


<exclusion>


<artifactId>org.springframework.security.oauth.boot</artifactId>


<groupId>spring-security-oauth2-autoconfigure</groupId>


</exclusion>


</exclusions>


</dependency>


<dependency>


<groupId>mysql</groupId>


<artifactId>mysql-connector-java</artifactId>


<version>5.1.47</version>


</dependency>


<dependency>


<groupId>org.mybatis.spring.boot</groupId>


<artifactId>mybatis-spring-boot-starter</artifactId>


<version>2.1.0</version>


</dependency>


<dependency>


<groupId>com.alibaba</groupId>


<artifactId>druid</artifactId>


<version>1.1.10</version>


</dependency>


</dependencies>

2.3 配置文件

server:


port: 9002


spring:


datasource:


driver-class-name: com.mysql.jdbc.Driver


url: jdbc:mysql://localhost:3306/srm


username: root


password: 123456


type: com.alibaba.druid.pool.DruidDataSource


main:


allow-bean-definition-overriding: true #允许我们自己覆盖 spring 放入到 IOC 容器的对象


mybatis:


type-aliases-package: com.dpb.domain


mapper-locations: classpath:mapper/*.xml


logging:


level:


com.dpb: debug

2.4 启动类

package com.dpb;


import org.mybatis.spring.annotation.MapperScan;


import org.springframework.boot.SpringApplication;


import org.springframework.boot.autoconfigure.SpringBootApplication;


/**


  • @program: springboot-security-oauth2-demo

  • @description:

  • @author: 波波烤鸭

  • @create: 2019-12-04 22:33


*/


@SpringBootApplication


@MapperScan("com.dpb.mapper")


public class OAuthSourceApp {


public static void main(String[] args) {


SpringApplication.run(OAuthSourceApp.class,args);


}


}

2.5 控制器

/**


  • @program: springboot-security-oauth2-demo

  • @description:

  • @author: 波波烤鸭

  • @create: 2019-12-04 22:34


*/


@RestController


public class ProductController {


@RequestMapping("/findAll")


public String findAll(){


return "产品列表信息...";


}


}


因为我们引入了 SpringSecurity,所以我们此时没法直接方法 findAll 方法,启动服务后访问如下:



那么如何解决呢?前面我们是采用单点登录的方式解决了这个问题,那么今天我们把这个资源交给OAuth2来管理,使用通行的 token 来访问资源

2.6 将访问资源作为 OAuth2 的资源来管理

复制前面介绍的 JWT 中的相关代码(GitHub 地址会提供)


即便是用 OAuth2 管理资源,也一样需要认证,这两个对象还是需要的。


2.7 编写资源管理配置类

package com.dpb.config;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.context.annotation.Bean;


import org.springframework.context.annotation.Configuration;


import org.springframework.http.HttpMethod;


import org.springframework.security.config.annotation.web.builders.HttpSecurity;


import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;


import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;


import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;


import org.springframework.security.oauth2.provider.token.TokenStore;


import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;


import javax.sql.DataSource;


/**


  • @program: springboot-security-oauth2-demo

  • @description:

  • @author: 波波烤鸭

  • @create: 2019-12-04 22:47


*/


@Configuration


@EnableResourceServer


public class OAuthSourceConfig extends ResourceServerConfigurerAdapter {


@Autowired


private DataSource dataSource;


/**


  • 指定 token 的持久化策略

  • InMemoryTokenStore 表示将 token 存储在内存

  • Redis 表示将 token 存储在 redis 中

  • JdbcTokenStore 存储在数据库中

  • @return


*/


@Bean


public TokenStore jdbcTokenStore(){


return new JdbcTokenStore(dataSource);


}


/**


  • 指定当前资源的 id 和存储方案

  • @param resources

  • @throws Exception


*/


@Override


public void configure(ResourceServerSecurityConfigurer resources) throws Exception {


resources.resourceId("product_api").tokenStore(jdbcTokenStore());


}


@Override


public void configure(HttpSecurity http) throws Exception {


http.authorizeRequests()


//指定不同请求方式访问资源所需要的权限,一般查询是 read,其余是 write。


.antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")


.antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")


.antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")


.antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")


.antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')")


.and()


.headers().addHeaderWriter((request, response) -> {


response.addHeader("Access-Control-Allow-Origin", "*");//允许跨域


if (request.getMethod().equals("OPTIONS")) {//如果是跨域的预检请求,则原封不动向下传达请求头信息


response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));


response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));


}


});


}


}


3.创建认证项目




接下来我们创建认证相关的项目

3.1 创建项目

3.2 导入依赖

和 source 项目的一样


<dependencies>


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-web</artifactId>


</dependency>


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-security</artifactId>


</dependency>


<dependency>


<groupId>org.springframework.security.oauth.boot</groupId>


<artifactId>spring-security-oauth2-autoconfigure</artifactId>


<version>2.1.0.RELEASE</version>


</dependency>


<dependency>


<groupId>org.springframework.cloud</groupId>


<artifactId>spring-cloud-starter-oauth2</artifactId>


<version>2.1.0.RELEASE</version>


<exclusions>


<exclusion>


<artifactId>org.springframework.security.oauth.boot</artifactId>


<groupId>spring-security-oauth2-autoconfigure</groupId>


</exclusion>


</exclusions>


</dependency>


<dependency>


<groupId>mysql</groupId>


<artifactId>mysql-connector-java</artifactId>


<version>5.1.47</version>


</dependency>


<dependency>


<groupId>org.mybatis.spring.boot</groupId>


<artifactId>mybatis-spring-boot-starter</artifactId>


<version>2.1.0</version>


</dependency>


<dependency>


<groupId>com.alibaba</groupId>


<artifactId>druid</artifactId>


<version>1.1.10</version>


</dependency>


</dependencies>

3.3 配置文件

server:


port: 9001


spring:


datasource:


driver-class-name: com.mysql.jdbc.Driver


url: jdbc:mysql://localhost:3306/srm


username: root


password: 123456


type: com.alibaba.druid.pool.DruidDataSource


main:


allow-bean-definition-overriding: true #允许我们自己覆盖 spring


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


放入到 IOC 容器的对象


mybatis:


type-aliases-package: com.dpb.domain


mapper-locations: classpath:mapper/*.xml


logging:


level:


com.dpb: debug

3.4 启动类

/**


  • @program: springboot-security-oauth2-demo

  • @description:

  • @author: 波波烤鸭

  • @create: 2019-12-04 23:06


*/


@SpringBootApplication


@MapperScan("com.dpb.mapper")


public class OAuthServerApp {


public static void main(String[] args) {


SpringApplication.run(OAuthServerApp.class,args);


}


}

3.5 复制之前认证的代码

3.6 提供 SpringSecurity 的配置类

package com.dpb.config;


import com.dpb.service.UserService;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.context.annotation.Bean;


import org.springframework.context.annotation.Configuration;


import org.springframework.security.authentication.AuthenticationManager;


import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;


import org.springframework.security.config.annotation.web.builders.HttpSecurity;


import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;


import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;


/**


  • @program: springboot-security-oauth2-demo

  • @description:

  • @author: 波波烤鸭

  • @create: 2019-12-04 23:09


*/


@Configuration


@EnableWebSecurity


public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


@Autowired


private UserService userService;


@Bean


public BCryptPasswordEncoder passwordEncoder(){


return new BCryptPasswordEncoder();


}


@Override


public void configure(AuthenticationManagerBuilder auth) throws Exception {


auth.userDetailsService(userService).passwordEncoder(passwordEncoder());


}


@Override


protected void configure(HttpSecurity http) throws Exception {


http.authorizeRequests()


.anyRequest().authenticated()


.and()


.formLogin()


.loginProcessingUrl("/login")


.permitAll()


.and()


.csrf()


.disable();


}


//AuthenticationManager 对象在 OAuth2 认证服务中要使用,提前放入 IOC 容器中


@Override


@Bean


public AuthenticationManager authenticationManagerBean() throws Exception {


return super.authenticationManagerBean();


}


}

3.7 提供 OAuth2 的配置类

package com.dpb.config;


import com.dpb.service.UserService;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.context.annotation.Bean;


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;


import org.springframework.security.oauth2.provider.approval.ApprovalStore;


import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;


import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;


import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;


import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;


import org.springframework.security.oauth2.provider.token.TokenStore;


import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;


import javax.sql.DataSource;


/**


  • @program: springboot-security-oauth2-demo

  • @description:

  • @author: 波波烤鸭

  • @create: 2019-12-04 23:12


*/


@Configuration


@EnableAuthorizationServer


public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {


//数据库连接池对象


@Autowired


private DataSource dataSource;


//认证业务对象


@Autowired


private UserService userService;


//授权模式专用对象


@Autowired


private AuthenticationManager authenticationManager;


//客户端信息来源


@Bean


public JdbcClientDetailsService jdbcClientDetailsService(){


return new JdbcClientDetailsService(dataSource);


}


//token 保存策略


@Bean


public TokenStore tokenStore(){


return new JdbcTokenStore(dataSource);


}


//授权信息保存策略


@Bean


public ApprovalStore approvalStore(){


return new JdbcApprovalStore(dataSource);


}


//授权码模式数据来源


@Bean


public AuthorizationCodeServices authorizationCodeServices(){


return new JdbcAuthorizationCodeServices(dataSource);


}


//指定客户端信息的数据库来源


@Override


public void configure(ClientDetailsServiceConfigurer clients) throws Exception {


clients.withClientDetails(jdbcClientDetailsService());


}


//检查 token 的策略


@Override


public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {


security.allowFormAuthenticationForClients();


security.checkTokenAccess("isAuthenticated()");


}


//OAuth2 的主配置信息


@Override


public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {


endpoints


.approvalStore(approvalStore())


.authenticationManager(authenticationManager)


.authorizationCodeServices(authorizationCodeServices())


.tokenStore(tokenStore());


}


}


4.测试



4.1 在数据库中手动添加客户端信息

所有要使用当前项目资源的项目,都是我们的客户端。比如我们之前举的例子,A 服务打印照片,B 服务存储照片。A 服务要使用 B 服务的资源,那么 A 服务就是 B 服务的客户端。这里要区分用户的信息和客户端信息,用户信息是用户在 B 服务上注册的用户信息,在 sys_user 表中。客户端信息是 A 服务在 B 服务中注册的账号,在 OAuth2 的 oauth_client_details 表中。


测试数据 sql 语句如下:


INSERT INTO oauth_client_details (


client_id,


resource_ids,


client_secret,


scope,


authorized_grant_types,


web_server_redirect_uri,


authorities,


access_token_validity,

评论

发布
暂无评论
详细介绍OAuth2