Oauth2.1第三方授权前后端分离实现

news2025/3/16 4:12:22

前言

Spring Cloud 整合 Spring Security Oauth2 请看我上一篇文章

在当今的数字化时代,随着微服务架构的流行和前后端分离技术的广泛应用,如何实现安全、高效的用户认证与授权成为了开发者们面临的重要挑战。Spring Cloud与Spring Security OAuth2作为Java领域中的佼佼者,为我们提供了强大的解决方案。本文将详细探讨Spring Cloud与Spring Security OAuth2的整合过程,以及如何在前后端分离的架构中实现用户认证与授权,旨在帮助读者更好地理解并掌握这一关键技术。
Spring Cloud作为一套微服务解决方案,提供了丰富的功能组件,用于构建分布式系统。其中,安全认证与授权是微服务架构中不可或缺的一环。Spring Security OAuth2则是Spring Security的一个扩展模块,它支持OAuth 2.0协议,实现了客户端与服务器之间的安全认证与授权。通过整合Spring Cloud与Spring Security OAuth2,我们可以轻松构建出安全、可靠的分布式系统。
在前后端分离的架构中,前端负责与用户进行交互,展示数据和接收用户输入;后端则负责处理业务逻辑和数据存储。这种架构使得前后端可以独立开发、部署和扩展,提高了系统的灵活性和可维护性。然而,这也带来了新的问题:如何在前后端之间实现安全的用户认证与授权?
通过整合Spring Cloud与Spring Security OAuth2,我们可以实现以下目标:

  1. 提供一个统一的认证授权中心,用于管理用户的身份信息和权限;
  2. 实现前后端之间的安全通信,确保数据的完整性和机密性;
  3. 简化用户认证与授权的流程,提高用户体验;
  4. 支持多种认证方式,如用户名密码、社交登录等;
  5. 提供细粒度的权限控制,满足不同业务场景的需求。

本文将详细介绍如何整合Spring Cloud与Spring Security OAuth2,并在前后端分离的架构中实现用户认证与授权。我们将从环境搭建、配置认证授权中心、实现前后端通信等方面入手,逐步引导读者完成整个整合过程。希望通过本文的介绍,读者能够掌握这一关键技术,并在实际项目中加以应用。

步骤

下面我们按照第三方授权登录流程讲解

页面改造

  1. 登录页面改造

我们这里采用了两次登录方式,先登录我们自己的系统,成功后再次请求oauth2的登录请求。

const submitForm = async (formEl: FormInstance | undefined) => {
  if (!formEl) return
  await formEl.validate(async (valid) => {
    if (valid && !store.state.fullscreenLoading) {
      store.commit('fullscreenLoading', true)
      const res = <resData>await oauth2Token({}, data)
      if (res.ok) {
        setSession('user_info', data)
        setSession('access_token', res.data.token_type.value.concat(" ").concat(res.data.access_token))
        setSession('refresh_token', res.data.refresh_token)
        // 如果路由携带回调地址参数则认为是客户端授权,需要请求oauth2的登录请求,登录成功后重定向到路由上的回调地址
        if (route.query.redirect_url) {
          data.redirect_url = route.query.redirect_url
          const res = <resData>await login(data)
          if (res.ok) {
            window.location.href = decodeURI(route.query.redirect_url)
          }
        } else {
          await router.replace('tenant').catch()
        }
      }
    }
  })
}
  1. 创建授权页面

授权页面需要显示授权的客户和用户的基本细腻,所以需要提供一个获取客户信息和用户信息的接口。

效果如下
image.png

请求授权地址

http://127.0.0.1:8011/single-auth/oauth2/authorize?response_type=code&client_id=messaging-client&scope=message.read&redirect_uri=http://www.baidu.com

  1. 如果未登录、未授权的情况下会被AuthenticationEntryPoint拦截,我们需要在此监听中判断是否是授权地址请求,如果是则重定向到登录页面并携带授权地址。
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private static final String OAUTH2_AUTHORIZE = "/oauth2/authorize";

    @Value("${oauth2.login-uri}")
    private String loginUri;

    @Override
    public void commence(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse, AuthenticationException e) {
        // 设置响应内容类型为 JSON,编码为 UTF-8
        httpServletResponse.setContentType(Constant.APPLICATION_JSON_UTF8_VALUE);
        // 设置响应状态码为 401 Unauthorized
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        try (PrintWriter out = httpServletResponse.getWriter()) {
            // 判断请求的 URI 是否为 "/oauth2/authorize"
            if (OAUTH2_AUTHORIZE.equals(httpServletRequest.getRequestURI())) {
                // 如果是,则重定向到用户登录页面
                String requestURL = "http://127.0.0.1:8011/single-auth/oauth2/authorize" +
                        SymbolConstant.QUESTION_MARK +
                        httpServletRequest.getQueryString();
                httpServletResponse.sendRedirect(loginUri.concat("?redirect_url=").concat(URLEncoder.encode(requestURL, Constant.UTF8)));
            } else {
                // 如果不是,则将错误信息转换为 JSON 字符串并写入响应输出流
                out.write(JSONUtil.toJsonStr(RespJson.error(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, I18nUtils.getMessage(e.getMessage()))));
            }
        } catch (IOException ex) {
            throw new MyException(ErrorCode.ERROR);
        }
    }

}
  1. 如果已登录、未授权则会重定向到我们自定义的授权页面。

这里我们需要自定义授权页面地址,自定义授权成功回调,自定义授权失败回调。

@Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        // 定义授权服务配置器
        OAuth2AuthorizationServerConfigurer configurer = new OAuth2AuthorizationServerConfigurer();
        configurer.tokenEndpoint(tokenEndpoint -> tokenEndpoint
                        .accessTokenRequestConverter(new OAuth2PasswordAuthenticationConverter())
                        .accessTokenRequestConverter(new OAuth2EmailAuthenticationConverter(userInfoFeignService))
                        .accessTokenResponseHandler(new MyAuthenticationSuccessHandler(userService))
                        .errorResponseHandler(new MyAuthenticationFailureHandler()))
                // 自定义授权
                .authorizationEndpoint(authorizationEndpoint -> {
                    // 自定义授权地址
                    authorizationEndpoint.consentPage(consent);
                    // 自定义授权成功监听
                    authorizationEndpoint.authorizationResponseHandler(new MyAuthenticationSuccessHandler(userService));
                    // 自定义授权失败监听
                    authorizationEndpoint.errorResponseHandler(new MyAuthenticationFailureHandler());
                })
                // Enable OpenID Connect 1.0, 启用 OIDC 1.0
                .oidc(Customizer.withDefaults());


        // 获取授权服务器相关的请求端点
        RequestMatcher endpointsMatcher = configurer.getEndpointsMatcher();

        http
                // 拦截对授权服务器相关端点的请求
                .requestMatcher(endpointsMatcher)
                // 拦载到的请求需要认证
                .authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated())
                // 忽略掉相关端点的 CSRF(跨站请求): 对授权端点的访问可以是跨站的
                .csrf().disable()
                .exceptionHandling(exceptions -> exceptions
                        .authenticationEntryPoint(myAuthenticationEntryPoint)
                        .accessDeniedHandler(myAccessDeniedHandler)
                )
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                // 应用授权服务器的配置
                .apply(configurer);
        DefaultSecurityFilterChain securityFilterChain = http.build();
        // 注入自定义授权模式实现
        http.authenticationProvider(
                new OAuth2PasswordAuthenticationProvider(
                        http.getSharedObject(OAuth2AuthorizationService.class),
                        http.getSharedObject(JwtGenerator.class),
                        new OAuth2RefreshTokenGenerator(),
                        http.getSharedObject(AuthenticationManager.class)
                ));

        return securityFilterChain;
    }
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private final IUserService userService;

    public MyAuthenticationSuccessHandler(IUserService userService) {
        this.userService = userService;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse,
                                        Authentication authentication) {
        if (AuthorizationGrantType.PASSWORD.getValue().equals(httpServletRequest.getParameter(Constant.GRANT_TYPE))) {
            // 登录日志
            userService.saveLoginLog(httpServletRequest);
        }
        // 设置响应内容类型为 JSON,编码为 UTF-8
        httpServletResponse.setContentType(Constant.APPLICATION_JSON_UTF8_VALUE);
        // 设置响应状态码为 200 OK
        httpServletResponse.setStatus(HttpServletResponse.SC_OK);
        // 创建一个 HashMap 用于存储认证信息
        HashMap<String, Object> map = new HashMap<>();
        try (PrintWriter out = httpServletResponse.getWriter()) {
            if (authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken) {
                //使用客户端授权登录
                OAuth2AuthorizationCodeRequestAuthenticationToken auth2AuthorizationCodeRequestAuthenticationToken = (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;
                // 前端授权请求标识(为了解决跨域问题)
                String accredit = httpServletRequest.getParameter("accredit");
                // 回调地址
                String url = auth2AuthorizationCodeRequestAuthenticationToken.getRedirectUri() + "?code=" + auth2AuthorizationCodeRequestAuthenticationToken.getAuthorizationCode().getTokenValue();
                // 如果是前端请求授权则返回成功信息,移交给前端跳转到授权成功页面,否则直接重定向到成功页面
                if (accredit != null) {
                    out.write(JSONUtil.toJsonStr(RespJson.success(url)));
                } else {
                    httpServletResponse.sendRedirect(auth2AuthorizationCodeRequestAuthenticationToken.getRedirectUri() + "?code=" + auth2AuthorizationCodeRequestAuthenticationToken.getAuthorizationCode().getTokenValue());
                }
            }
        } catch (IOException e) {
            // 捕获 IO 异常,并抛出自定义异常
            throw new MyException(ErrorCode.ERROR);
        }
    }

}
  1. 如果已登录、已授权则会重定向到客户自己配置的重定向地址并且带code。

总结

完成上诉步骤我们就可以进行第三方授权登录了。复盘一下

  1. 自定义登录页面
  2. 自定义授权页面
  3. 请求授权地址AuthenticationEntryPoint监听重定向改造
  4. 登录成功监听改造(返回成功信息)
  5. 授权成功监听改造(如果是前端请求则返回成功信息,由前端重定向到成功回调地址)
  6. 前端登录改造(先请求我们自己的登录接口,成功后再请求oauth2提供的登录接口)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1598339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序商城和微商城的区别

移动互联网电商发展迅速&#xff0c;各种商城系统的类型越来越多&#xff0c;人们选择要从多方面考量再进行评估&#xff0c;选择过程变得困难了很多&#xff0c;比如现在火热的微商城和小程序商城&#xff0c;很多人都不太能分清楚。今天&#xff0c;我们就一起来看看这两种商…

BCLinux8U6系统部署oceanbase分布式数据库社区版之三、分布式数据库部署

本文是在完成步骤一、准备 OBD 中控机&#xff0c;步骤二3台数据库服务器准备后&#xff0c;正式开始oceanbase分布式数据库安装。 前序步骤&#xff1a;BCLinux8U6系统部署oceanbase分布式数据库社区版之一、准备 OBD 中控机 BCLinux8U6系统部署oceanbase分布式数据库社区版…

请求头包含“boundary=----WebKitFormBoundary”的request抓包

对于请求头包含“boundary----WebKitFormBoundary”&#xff0c;不能直接使用request.post请求&#xff0c;这类请求是文件上传请求。 s common_login(name, password) # 获取浏览器对象url archive_url /XXX# 这里先定义一个fields参数&#xff0c;格式为你可能需要一个包…

Linux yum搭建Keepalived,2 台机器都有虚拟 IP 问题

文章目录 Keepalived 搭建一、安装二、keepalived配置1、配置文件详解global_defs模块参数vrrp_instance模块参数vrrp_script模块参数 2、修改配置文件3、启动服务 Tips:1️⃣问题&#xff1a;两台机器上面都有VIP的情况2️⃣完整配置文件 Keepalived 搭建 服务IP服务器Keepal…

微信小程序wx.getLocation 真机调试不出现隐私弹窗

在小程序的开发过程中&#xff0c;首页中包含要获取用户地理位置的功能&#xff0c;所以在这里的onLoad&#xff08;&#xff09;中调用了wx.getLocation()&#xff0c;模拟调试时一切正常&#xff0c;但到了真机环境中就隐私框就不再弹出&#xff0c;并且出现了报错&#xff0…

ubuntu16.04安装Eclipse C/C++

1.安装 JDK 官网源码安装 首先打开JDK官网&#xff0c;JDK1.8的下载网址为&#xff1a;https://www.oracle.com/cn/java/technologies/downloads/#java8-windows&#xff0c;进入到网址如下图所示&#xff1a; 向下滑动到 JDK1.8的下载界面&#xff0c;如下图所示&#xff1a…

【软考】UML中的图之用例图

目录 1. 说明2. 建模2.1 说明2.2 语境建模2.3 需求建模 3. 图示4. 组成部分 1. 说明 1.用例图&#xff08;Use Case Diagram&#xff09;。2.展现了一组用例、参与者&#xff08;Actor&#xff09;以及它们之间的关系。3.用例图通常包括以下的内容&#xff1a;用例、参与者、用…

SpringBoot项目创建及简单使用

目录 一.SpringBoot项目 1.1SpringBoot的介绍 1.2SpringBoot优点 二.SpringBoot项目的创建 三.注意点 一.SpringBoot项目 1.1SpringBoot的介绍 Spring是为了简化Java程序而开发的&#xff0c;那么SpringBoot则是为了简化Spring程序的。 Spring 框架&#xff1a; Spring…

Python高质量函数编写指南

The Ultimate Guide to Writing Functions 1.视频 https://www.youtube.com/watch?vyatgY4NpZXE 2.代码 https://github.com/ArjanCodes/2022-funcguide Python高质量函数编写指南 1. 一次做好一件事 from dataclasses import dataclass from datetime import datetimedatacl…

如何解决selenium无头浏览器访问页面失败问题!!

无头浏览器简介 无头浏览器&#xff08;Headless browser&#xff09;是一种没有图形用户界面&#xff08;GUI&#xff09;的网络浏览器。它可以在后台运行&#xff0c;并通过编程接口进行控制和操作&#xff0c;而不需要显示界面。通常&#xff0c;传统的浏览器如 Chrome、Fi…

生产控制台厂家的技术要求深度解读

随着科技的不断进步和工业的快速发展&#xff0c;生产控制台在现代化生产线中的作用日益凸显。生产控制台作为生产线的“大脑”&#xff0c;要求厂家不仅具备高超的制造技术&#xff0c;还需对技术要求有深入的理解和掌握。本文将对生产控制台厂家的技术要求进行浅析。 生产控制…

vmware安装ubuntu-18.04系统

一、软件下载 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1fK2kygRdSux1Sr1sOKOtJQ 提取码&#xff1a;twsb 二、安装ubuntu系统 1、把ubuntu-18.04的压缩包下载下来&#xff0c;并且解压 2、打开vmware软件&#xff0c;点击文件-打开 3、选择我们刚刚解…

4.15 网络编程

思维导图 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> #include <semaphore.h> #inclu…

白盒测试之分支-条件覆盖

白盒测试之分支-条件覆盖&#xff08;蓝桥课学习笔记&#xff09; 实验介绍 分支&#xff08;判定&#xff09;覆盖是设计一定量的测试用例使程序中的每个判断语句的真假分支都得到覆盖&#xff0c;但是分支覆盖不能保证判断语句中每个条件的真、假分支都得到覆盖。那么&…

Linux的学习之路:5、粘滞位与vim

摘要 这里主要是把上章没说完的权限的粘滞位说一下&#xff0c;然后就是vim的一些操作。 目录 摘要 一、粘滞位 二、权限总结 三、vim的基本概念 四、vim的基本操作 五、vim正常模式命令集 1、插入模式 2、从插入模式切换为命令模式 3、移动光标 4、删除文字 5、复…

详解UART通信协议以及FPGA实现

文章目录 一、UART概述二、UART协议帧格式2.1 波特率2.2 奇校验ODD2.3 偶校验EVEN 三、UART接收器设计3.1 接收时序图3.2 Verilog代码3.3 仿真文件测试3.4 仿真结果3.5 上版测试 四、UART发送器设计4.1 发送时序图4.2 Verilog代码4.3 仿真文件测试4.4 仿真结果4.5 上板测试 五、…

温湿度传感器(DHT11)以及光照强度传感器(BH1750)的使用

前言 对于一些单片机类的环境检测或者智能家居小项目中&#xff0c;温湿度传感器&#xff08;DHT11&#xff09;以及光照强度传感器&#xff08;BH1750&#xff09;往往是必不可少的两个外设&#xff0c;下面我们来剖析这两个外设的原理&#xff0c;以及使用。 1. 温湿度传感…

仿真服务器介绍及应用

仿真服务器是一种高性能的计算设备&#xff0c;专门用于运行复杂的仿真软件和处理大量的计算任务。 仿真服务器通常具备以下特点&#xff1a; 1. 高性能硬件配置&#xff1a;为了满足仿真软件对计算能力的要求&#xff0c;仿真服务器通常配备高性能的CPU、大量的内存以及高速的…

Ollama教程——使用langchain:ollama与langchain的强强联合

相关文章: Ollama教程——入门&#xff1a;开启本地大型语言模型开发之旅 Ollama教程——模型&#xff1a;如何将模型高效导入到ollama框架 Ollama教程——兼容OpenAI API&#xff1a;高效利用兼容OpenAI的API进行AI项目开发 Ollama教程——使用langchain&#xff1a;ollama与…

【Windows】SecureCRT v9.2.3永久使用教程亲测可用

​ 软件介绍 SecureCRT 是一款常用的 SSH&#xff08;安全外壳协议&#xff09;和 Telnet 客户端软件&#xff0c;主要用于远程服务器管理和网络设备配置。它具有以下主要功能&#xff1a; 1.安全连接&#xff1a;SecureCRT 提供了强大的加密算法&#xff0c;保障用户与服务…