OAuth2.0

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: OAuth2.0

OAuth2授权总体流程

  • 角色梳理:第三方应用 <------> 存储用户私密信息应用 -----> 授权服务器 ----》资源服务器
  • 整体授权流程:(图片来自RF6749文档)

四种授权模式

授权码模式

授权码模式(Authorization Code)是功能最完整、流程最严密、最安全并且使用最广泛的一种OAuth2授权模式,同事也是最复杂的一种授权模式,他的特点是通过客户端的后台服务器,于服务提供商的认证服务器进行互动,器具体的授权流程如图

  • Third-party application:第三方应用程序,简称“客户端”(client)
  • Resource Owner:资源所有者 简称用户(user)
  • User Agent:用户代理,是指浏览器;
  • Authorization Server:认证服务器,机服务端专门用来处理认证的服务器
  • Resource Server:资源服务器,即服务端存放用户生成的资源服务器,他与认证服务器,可以是同一个服务器,也可以是不同的服务器。

简单模式


密码模式

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

具体步骤如下:

  • (A)用户向客户端提供用户名和密码
  • (B)客户端将用户名和密码发送给认证服务器,向后者请求令牌
  • (C)认证服务器确认无误后,向客户端提供访问令牌

核心参数:

https://wx.com/token?grant_type=password&username=USERNAME&password=PASSWORD&CLient_id=CLIENT_ID

客户端模式

OAuth2 标准接口

GitHub授权登录

创建Oauth应用

访问github并登录,在https://github.com/settings/profile中找到Developer Settings选项

  • 创建OAuth app并输入基本信息

项目开发

创建controller

/**
 * @Description:
 * @Author: Guo.Yang
 * @Date: 2022/11/12/23:49
 */
@RestController
public class HelloController {
    /**
     * DefaultOAuth2User说明
     * 只要是OAuth授权认证之后返回的都是DefaultOAuth2User 对象 里边存贮的是用户的信息
     *
     * @return
     */
    @RequestMapping("/hello")
    public DefaultOAuth2User hello(){
        System.out.println("hello");
        // springsecurity认证后就会吧用户信息存在 SecurityContextHolder 中 (springsecurity中提到)
        // 可以直接在 SecurityContextHolder 中拿到用户的基本信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return (DefaultOAuth2User) authentication.getPrincipal();
    }
}
  • 配置 security
// 配置SpringSecurity 自定义配置
@Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .oauth2Login(); // 使用 oauth2 认证 , 配置文件中的配置认证服务
        }
    }
  • 配置文件
server:
  port: 8080
spring:
  application:
    name: springsecurity-oauth2-client-github
#    配置oauth2
  security:
    oauth2:
      client:
        registration:
          github:
#            连接id
            client-id: b0e485699b7fe9abe7e0
#            连接密钥
            client-secret: 22c5861091d17ce16416b35a976ebbbeb8ab4e9e
#            授权回调地址
            redirect-uri: http://localhost:8080/login/oauth2/code/github
  • 启动测试
  • 点击github 登录,点击授权就会跳转至我们刚才在github配置的应用首页,再次点击to hello 即可访问到我们应用的/hello资源

  • 测试通过

说明: 在整个过程中,颁发授权码,重定向授权回调地址、授权码获取令牌整个过程都是不可见的,过程可能更抽象一些。

OAuth源码相关认证过滤器

  • OAuth2AuthorizationCodeGrantFilter ---  处理OAuth2认证授权码
  • OAuth2LoginAuthenticationFilter --- 处理OAuth2认证




  • 真正的认证的过滤器:
  • OAuth2LoginAuthenticationFilter中的
  • attemptAuthentication()方法

Spring Security OAuth2

授权、资源服务器

授权服务器搭建

1. 基于内存客户端和令牌存储

创建 springboot 应用,并引入资源

注意:降低 springboot 版本为2.2.5.RELEASE

<!--引入授权服务器的依赖-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-oauth2</artifactId>
  <version>2.2.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 编写配置类,添加security 配置类以及oauth配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 由于授权服务器的密钥是不可以明文存贮的,只可以是加密以后,所以在此添加密码加密方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    /**
     * 配置security请求
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and().formLogin()
                .and().csrf().disable();
    }
    /**
     * 配置内存用户
     * 用户名:root
     * 密码:123
     * 权限:ADMIN
     * @return
     */
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN").build());
        return inMemoryUserDetailsManager;
    }
    /**
     * 配置用户名密码使用内存存储的
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }
}
/**
 * @Description: 授权服务器配置
 * @Author: Guo.Yang
 * @Date: 2022/11/13/21:28
 */
@Configuration
@EnableAuthorizationServer // 指定当前应用为授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    // 因为密钥不可以使用明文,所以后期修改时,将其注入进来
    @Autowired
    private PasswordEncoder passwordEncoder;
    /**
     * 用来配置授权服务器i可以为那些客户端授权
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client") // id
                // .secret("secret") // 密钥
                .secret(passwordEncoder.encode("secret")) // 加密方式加密密钥,因为密钥不可以使用明文
                .redirectUris("https://www.baidu,com") // 重定向的uri
                .authorizedGrantTypes("authorization_code") // 授权服务器支持的模式 当前配置为 仅支持授权码模式
                .scopes("read:user"); // 令牌允许获取资源的权限
    }
}

授权码这种模式

  • 请求用户是否授权 /oauth/authorize
  • 授权之后根据获取的授权码获取令牌 /oauth/token

2.授权码模式测试

  1. 1.启动服务,登录之后进行授权码获取
  2. 完整路径为
http://localhost:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=https://www.baidu.com
  1. 2.点击授权后,跳转至重定向的uri并会在地址的后边携带上授权码 code
  2. 3.授权之后根据获取的授权码获取令牌 /oauth/token  参数有:id、secret、redirectUri、code

完整路径为:

curl --location --request POST 'http://client:secret@localhost:8080/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: JSESSIONID=9B99A4E09092B73EF32FFC3FF7630DAA' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=ytDyI7' \
--data-urlencode 'redirect_uri=http://www.baidu.com'

注意:

  • 当前访问之后并不会返回accesstoken,会报错,
  • 由于我们在配置springsecurity账号密码的时候设置的是明文的方式
  • 而在我们的授权服务的配置中 密钥也同样是明文,这样是不可以的
  • 正确的做法添加加密方式
  • 在springsecurityconfig配置密码加密方式
@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}
  • 在AuthorizationServerConfig中将 PasswordEncoder 注入进来,并加密密钥
  • 正确的获取令牌的路径
curl --location --request POST 'http://client:secret@localhost:8080/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: JSESSIONID=9B99A4E09092B73EF32FFC3FF7630DAA' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=elPsoG' \
--data-urlencode 'redirect_uri=https://www.baidu.com'

3、密码模式测试

  • 需要在授权配置类中开启oauth对密码模式的支持
  • 需要在SpringSecurityConfig配置类中将 AuthenticationManager暴露出来 重写authenticationManager 并添加Bean
    /**
     * 密码模式所需
     * 暴露出 AuthenticationManager
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager()  ;
    }
  • 在 AuthorizationServerConfig(授权配置类)中添加认证authenticationManager
    /**
     * 配置授权服务器使用那个 userDetailService
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager); // 注入 认证authenticationManager
    }

  • 测试
curl --location --request POST 'http://client:secret@localhost:8080/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: JSESSIONID=9B99A4E09092B73EF32FFC3FF7630DAA' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=root' \
--data-urlencode 'password=123'

基于数据库的客户端、令牌存储

简表语句

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for clientdetails
-- ----------------------------
DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE `clientdetails` (
  `appId` varchar(256) NOT NULL,
  `resourceIds` varchar(256) DEFAULT NULL,
  `appSecret` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `grantTypes` varchar(256) DEFAULT NULL,
  `redirectUrl` varchar(256) DEFAULT NULL,
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additionalInformation` varchar(4096) DEFAULT NULL,
  `autoApproveScopes` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`appId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication_id` varchar(256) NOT NULL,
  `user_name` varchar(256) DEFAULT NULL,
  `client_id` varchar(256) DEFAULT NULL,
  `authentication` blob,
  `refresh_token` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for oauth_approvals
-- ----------------------------
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (
  `userId` varchar(256) DEFAULT NULL,
  `clientId` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `status` varchar(10) DEFAULT NULL,
  `expiresAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `lastModifiedAt` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
  `client_id` varchar(256) NOT NULL,
  `resource_ids` varchar(256) DEFAULT NULL,
  `client_secret` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `authorized_grant_types` varchar(256) DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) DEFAULT NULL,
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) DEFAULT NULL,
  `autoapprove` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for oauth_client_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication_id` varchar(256) NOT NULL,
  `user_name` varchar(256) DEFAULT NULL,
  `client_id` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
  `code` varchar(256) DEFAULT NULL,
  `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
SET FOREIGN_KEY_CHECKS = 1;

配置客户端

令牌颁发配置


相关文章
|
11天前
|
存储 安全 小程序
认识OAuth2.0
认识OAuth2.0
22 0
认识OAuth2.0
|
6月前
|
存储 安全 Java
|
11月前
|
JSON 安全 Cloud Native
什么是单点登录?什么又是 OAuth2.0?
什么是单点登录?什么又是 OAuth2.0?
164 0
|
存储 安全 数据安全/隐私保护
OAuth2.0与OAuth1.0你了解了吗?
OAuth2.0与OAuth1.0你了解了吗?
|
安全 JavaScript 前端开发
详解OAuth2.0
1.概述 OAUTH,Open Authorization,开放授权协议,为用户资源的授权提供了一个安全的、开放而又简易的标准。目的是让第三方对用户的数据只有有限访问权,而无法触及到用户的核心信息。 例如,在第三方网站上使用微信或者QQ作为账号进行登录,就是使用的oauth协议,只返回给第三方诸如用户名、头像等信息,而不会返回给第三方秘密等核心数据。 OAuth最初由Twitter的开发人员提出,后来成为了一个互联网标准,并得到了广泛应用。OAuth2.0是OAuth协议的第二个版本,是一种更加安全、可扩展、功能更加完备的授权协议。目前我们说OAuth一般指的就是OAuth 2.0。
1809 1
|
存储 JavaScript 前端开发
带你全面了解 OAuth2.0
最开始接触 OAuth2.0 的时候,经常将它和 SSO单点登录搞混。后来因为工作需要,在项目中实现了一套SSO,通过对SSO的逐渐了解,也把它和OAuth2.0区分开了。所以当时自己也整理了一篇文章《SSO单点登录原理及实现方式》 最近需要经常和各大电商平台进行对接,所以又和OAuth2.0重逢了。因此这里再次对OAuth2.0的原理及实现方式进行一个梳理,希望通过这套教程能够帮到大家。对于技术类的文章,这引言是有点过长了。好了下面我们进入正题。
653 0
|
安全 应用服务中间件 API
JWT和OAuth2.0
JWT是一种认证协议,提供了一种用于发布接入令牌(Access Token),并对发布的签名接入令牌进行验证的方法。SSO私钥加密token。应用端公钥解密token。 OAuth2.0是一种授权框架,提供了一套详细的授权机制(指导)。用户或应用可以通过公开的或私有的设置,授权第三方应用访问特定资源。
208 0
JWT和OAuth2.0
|
存储 安全 Java
OAuth2.0介绍|学习笔记
快速学习 OAuth2.0介绍
OAuth2.0介绍|学习笔记
|
存储 数据库 数据安全/隐私保护
OAuth2介绍 | 学习笔记
快速学习 OAuth2介绍
OAuth2介绍 | 学习笔记
|
安全 程序员 数据安全/隐私保护
浅谈OAuth2.0
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。OAuth不是一个认证协议,这点不要搞混。