当前位置: 首页 > news >正文

Oauth2是个什么东西?

本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看

(一)前言

在之前的项目中只用到了SpringSecurity,后来发现公司的架构中有Oauth2,虽然自己在业务中没有过接触,但是觉得还是需要去了解一下,因此就有了这篇博客。本博客会从概念和简单的应用出发,讲解Oauth2的理论及实践,不涉及数据库等操作。

(二)什么是OAuth2

OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。

OAuth2协议包含几种角色:Client(客户端)、Resource Owner(资源拥有者)、Authorization Server(认证服务器)、Resource Server(资源服务器)。

上面这副图很详细地描述了OAuth2协议的运行原理:

A:用户打开一个客户端,客户端发送请求给资源的拥有者

B:资源拥有者返回一个需要授权的结果

C:用户在客户端上进行授权,授权信息发送到认证服务器

D:认证服务器认证完成后返回令牌

E:客户端通过令牌去访问资源服务器

F:资源服务器返回相应的资源、

以某个网站使用微信第三方登陆为例:

A:用户通过客户端打开某网站

B:某网站返回需要第三方登陆的信息

C:用户通过扫码在客户端同意登陆

D:认证服务器返回登陆成功后的token给客户端

E:客户端拿着token访问资源服务器

F:资源服务器接收token返回相应的资源

(三)OAuth2的应用场景

微信第三方就是OAuth2的应用场景之一。

我们在使用微信第三方登陆时,都需要先向微信申请到AppID和AppKey,然后拿着id和key去调用授权接口。

一般情况下大部分公司会将Oauth2应用在统一的认证中心,供第三方服务去访问他们的某些资源。

(四)OAuth2协议的四种模式

4.1 授权码模式 authorization_code

最常用的模式,也是上面所讲的模式,根据请求获取token,用token来访问资源。因为不涉及用户名和密码的泄漏,因此更加安全。

4.2 密码形式 password

传递用户名和密码的方式,不安全。

4.3 简化模式 implicit

不通过授权码,直接向认证系统要token。用到的场景很少。

4.4 客户端模式 client_credentials

客户端通过自己的名义向认证服务器获取授权,这种场景也很少见,比如一台只有一台硬件可以访问某个系统,可以理解为客户端模式。

(五)OAuth2认证服务器的实现

OAuth2只是一种协议,spring-security-oauth2是该协议的一种实现方式。接下来就通过spring-security-oauth2来实现认证授权。

5.1 引入依赖

首先引入项目中必要的依赖包

<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</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>

5.2 SpringSecurity配置

如果使用spring-security-oauth2,在获取oauth2的授权之前,必须要先获取SpringSecurity的授权,因此我们先配置SpringSecurity:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("root")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .authorities("/*");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic()
                .and()
                .csrf().disable();
    }
}

以上是最基本的SpringSecurity配置类,通过内存中设定的用户名root和密码123456进行登陆。

5.3 OAuth授权配置

AuthorizationServer配置的是一个认证服务器,当传入的条件满足配置时,就会在回调接口中带上对应的code值。下面的代码中主要配置了几个重要的参数,已经写在注释中。

@Component
@EnableAuthorizationServer
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter{
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
          //开启允许表单认证
         security.allowFormAuthenticationForClients()
                .checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //client_id
                .withClient("javayz")
                //secret
                .secret(bCryptPasswordEncoder.encode("my_secret"))
                //授权模式
                .authorizedGrantTypes("authorization_code")
                //作用域,也就是权限范围
                .scopes("all")
                //资源的ID
                .resourceIds("my_resource")
                //回调地址
                .redirectUris("http://localhost:8080/callback");
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

5.4 获取token方式

首先第一步,访问认证接口:

http://localhost:8080/oauth/authorize?client_id=javayz&response_type=code

第一次访问时,由于SpringSecurity的拦截,需要输入用户名和密码,我这里由于都是写在内存中的,因此输入root和123456即可。

输入用户名和密码之后,oauth会让你选择是否授权,这一步可以理解为微信第三方登陆时,微信扫码后在手机上点击授权。

授权成功之后,会跳转到回调接口中,并且带上一个code值。

http://localhost:8080/callback?code=KGNujV

接着我们要拿上这些code值去获取token:

当所有的参数都填写正确的情况下,就可以拿到access_token

由于在上面的代码中,我们开启了允许表单认证,因此可以通过接口判断token是否有效:

(六)Oauth2资源服务器的实现

写了认证后就需要写一个资源服务器进行验证,新建一个资源项目,启动的端口要和认证服务不同,编写资源服务器配置类:

@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {

    private static String ClientId="javayz";
    private static String Security="my_secret";

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    //token校验
    @Bean
    public RemoteTokenServices remoteTokenServices(){
        RemoteTokenServices tokenServices=new RemoteTokenServices();
        tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
        tokenServices.setClientId(ClientId);
        tokenServices.setClientSecret(Security);
        return tokenServices;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        http.authorizeRequests().anyRequest().authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("my_resource").stateless(true);
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

这段代码的核心是remoteTokenServices,这里需要定义token校验的地址,以及对应的client和secret。

接着写一个接口用来测试:

@RestController
public class TestController {
    @GetMapping("/test")
    public String test(){
        return "测试接口";
    }
}

由于在配置类中设置了所有请求都需要认证,所以当我们直接访问时,会提示无权限:

这个时候需要将上面生成的token放入认证信息中,有两种方式,第一种是Postman在Authorization中设置Bearer token

第二种方式是在请求头header中设置认证,注意token的前缀

(七)总结

通过这篇博客,你应该知道了Oauth2是个什么东西,也可以在项目自己的项目中尝试着用一下。如果是公司层面的使用,还需要考虑其他的一些问题,比如用户信息的存储方式,token的生成方式等等。我是鱼仔,我们下期再见!

相关文章:

  • [javaSE] 看知乎学习工厂模式
  • 数据库分库分表扫盲,不会用也得知道概念
  • 写了那么久的String字符串,你可能根本不懂它!
  • 2012 借教室
  • 怎样才能写出规范的好代码?
  • 让Android Studio支持系统签名
  • 详解Java中的BIO、NIO、AIO
  • 取经阿里十年技术大佬,得到Java线上问题排查攻略!
  • Understanding memory usage on Linux
  • Java中的拦截器和过滤器有什么区别
  • 为什么要引入分布式任务调度系统?
  • 为什么说datax是目前最好的异构数据源数据交换工具
  • Java常用关键字:this、super、final、static、访问修饰符
  • JAVA UUID 生成唯一标识
  • 网络开发的最强大框架:Netty快速入门
  • @jsonView过滤属性
  • CSS 专业技巧
  • Docker入门(二) - Dockerfile
  • js 实现textarea输入字数提示
  • Js基础——数据类型之Null和Undefined
  • linux学习笔记
  • rabbitmq延迟消息示例
  • Vue小说阅读器(仿追书神器)
  • Vultr 教程目录
  • Wamp集成环境 添加PHP的新版本
  • 机器学习 vs. 深度学习
  • 坑!为什么View.startAnimation不起作用?
  • 批量截取pdf文件
  • 设计模式(12)迭代器模式(讲解+应用)
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 微服务框架lagom
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • Semaphore
  • ​渐进式Web应用PWA的未来
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • ![CDATA[ ]] 是什么东东
  • #pragma once
  • #绘制圆心_R语言——绘制一个诚意满满的圆 祝你2021圆圆满满
  • (1)(1.13) SiK无线电高级配置(五)
  • (10)STL算法之搜索(二) 二分查找
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (Forward) Music Player: From UI Proposal to Code
  • (二)Eureka服务搭建,服务注册,服务发现
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (十三)Maven插件解析运行机制
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET Project Open Day(2011.11.13)
  • .NET设计模式(7):创建型模式专题总结(Creational Pattern)
  • .NET中统一的存储过程调用方法(收藏)
  • /*在DataTable中更新、删除数据*/