Spring Authorization Server入门 (十八) Vue项目使用PKCE模式对接认证服务

news2024/11/28 4:48:09

Vue单页面项目使用授权码模式对接流程说明

以下流程摘抄自官网

在本例中为授权代码流程。 授权码流程的步骤如下:

  1. 客户端通过重定向到授权端点来发起 OAuth2 请求。 对于公共客户端,此步骤包括生成code_verifier 并计算code_challenge,然后将其作为查询参数发送。

  2. 如果用户未通过身份验证,授权服务器将重定向到登录页面。 身份验证后,用户将再次重定向回授权端点。

  3. 如果用户未同意所请求的范围并且需要同意,则会显示同意页面。

  4. 一旦用户同意,授权服务器会生成一个authorization_code并通过redirect_uri重定向回客户端。

  5. 客户端通过查询参数获取authorization_code并向Token Endpoint发起请求。 对于公共客户端,此步骤包括发送code_verifier参数而不是用于身份验证的凭据。

Vue项目中修改内容

安装crypto-js依赖

已安装可以忽略,该依赖是为了计算Code Challenge使用

npm install crypto-js

TypeScript下额外添加@types/crypto-js依赖

npm install @types/crypto-js

编写公共方法

编写Code Verifier生成与Code Challenge的计算方法

创建PKCE工具js文件

import CryptoJS from 'crypto-js'

/**
 * 生成 CodeVerifier
 *
 * return CodeVerifier
 */
export function generateCodeVerifier() {
    return generateRandomString(32)
}

/**
 * 生成随机字符串
 * @param length 随机字符串的长度
 * @returns 随机字符串
 */
export function generateRandomString(length: number) {
    let text = ''
    const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    for (let i = 0; i < length; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length))
    }
    return text
}

/**
 * 生成 Code Challenge
 * @param code_verifier 上边生成的 CodeVerifier
 * @returns Code Challenge
 */
export function generateCodeChallenge(code_verifier: string) {
    return base64URL(CryptoJS.SHA256(code_verifier))
}

/**
 * 将字符串base64加密后在转为url string
 * @param str 字符串
 * @returns bese64转码后转为url string
 */
export function base64URL(str: CryptoJS.lib.WordArray) {
    return str
        .toString(CryptoJS.enc.Base64)
        .replace(/=/g, '')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
}

/**
 * 将字符串加密为Base64格式的
 * @param str 将要转为base64的字符串
 * @returns 返回base64格式的字符串
 */
export function base64Str(str: string) {
    return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(str));
}

编写获取地址栏参数方法

略,已在上篇文章中贴出

编写请求Token方法

略,已在上篇文章中贴出

在环境变量配置文件中添加配置

# 认证服务地址(token签发地址)
VITE_OAUTH_ISSUER=http://127.0.0.1:8080
# PKCE流程使用的客户端Id
VITE_PKCE_CLIENT_ID=pkce-message-client
# 授权码模式使用的回调地址
VITE_PKCE_REDIRECT_URI=http://127.0.0.1:5173/PkceRedirect

创建处理回调的页面PkceRedirect.vue

页面加载时会尝试从地址栏获取参数code,如果能获取到说明是从认证服务回调过来的,执行换取token流程,如果没有获取到code说明需要发起授权申请。跟之前的授权码流程是一致的。

<script setup lang="ts">
import router from '../../router'
import { getToken } from '@/api/Login'
import { getQueryString } from '@/util/GlobalUtils'
import { createDiscreteApi } from 'naive-ui'
import { generateCodeVerifier, generateCodeChallenge } from '@/util/pkce'

const { message } = createDiscreteApi(['message'])

// 生成CodeVerifier
let codeVerifier: string = generateCodeVerifier()
// codeChallenge
let codeChallenge: string = generateCodeChallenge(codeVerifier)
// 生成state
let state: string = generateCodeVerifier()

// 获取地址栏授权码
const code = getQueryString('code')

if (code) {
  // 从缓存中获取 codeVerifier
  const state = localStorage.getItem('state')
  // 校验state,防止cors
  const urlState = getQueryString('state')
  if (urlState !== state) {
    message.warning('state校验失败.')
  } else {
    // 从缓存中获取 codeVerifier
    const code_verifier = localStorage.getItem('codeVerifier')
    getToken({
      grant_type: 'authorization_code',
      client_id: import.meta.env.VITE_PKCE_CLIENT_ID,
      redirect_uri: import.meta.env.VITE_PKCE_REDIRECT_URI,
      code,
      code_verifier,
      state
    })
      .then((res: any) => {
        localStorage.setItem('accessToken', JSON.stringify(res))
        router.push({ path: '/' })
      })
      .catch((e) => {
        message.warning(`请求token失败:${e.data.error || e.message || e.statusText}`)
      })
  }
} else {
  // 缓存state
  localStorage.setItem('state', state)
  // 缓存codeVerifier
  localStorage.setItem('codeVerifier', codeVerifier)
  window.location.href = `${
    import.meta.env.VITE_OAUTH_ISSUER
  }/oauth2/authorize?response_type=code&client_id=${
    import.meta.env.VITE_PKCE_CLIENT_ID
  }&redirect_uri=${encodeURIComponent(
    import.meta.env.VITE_PKCE_REDIRECT_URI
  )}&scope=message.write%20message.read&code_challenge=${codeChallenge}&code_challenge_method=S256&state=${state}`
}
</script>

<template>加载中...</template>

添加路由

{
    path: '/PkceRedirect',
    name: 'PkceRedirect',
    component: () => import('../views/login/PkceRedirect.vue')
}

认证服务修改内容

在数据库中添加对应客户端的回调地址

重要:例如文中的就需要给客户端pkce-message-client添加一个回调地址http://127.0.0.1:5173/PkceRedirect

重要:例如文中的就需要给客户端pkce-message-client添加一个回调地址http://127.0.0.1:5173/PkceRedirect

重要:例如文中的就需要给客户端pkce-message-client添加一个回调地址http://127.0.0.1:5173/PkceRedirect

经过以上配置授权码模式的对接就完成了,接下来就可以测试了,在首页或者需要触发登录的地方添加一个按钮,点击跳转到/PkceRedirect之后会自动引导发起授权申请流程。

最后

一直都有提到PKCE流程是授权码流程的扩展,通过这两篇文章也可以看出两种流程的授权申请流程基本是一样的,只不过PKCE将客户端密钥换成了code_verifiercode_challenge,虽然稍微麻烦了些,但是安全性也提高了很多;至于移动app或者pc端应用对接的流程也是一样的,只不过是将回调地址换成了URLSchema,其它都是一样的;测试的流程与授权码模式基本一致,这里就不带大家测试了,读者可自行测试,然后观察请求跳转情况。

这里贴一张获取token成功的图片

获取token成功

如果有什么问题可以在评论区指正,谢谢

附录

代码仓库地址:Gitee

文档地址:How-to: Authenticate using a Single Page Application with PKCE

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

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

相关文章

实验5 跨交换机实现VLAN

交换机端口隔离&#xff08;access模式&#xff09; 实验目的实验拓扑实验步骤&#xff08;1&#xff09;在未划分vlan前&#xff0c;配置pc1、pc2、pc3、pc4的地址&#xff0c;如图所示&#xff08;2&#xff09;测试两台pc机的连通性&#xff08;3&#xff09;在S1中创建vlan…

必看!S3File Sink Connector 使用文档

S3File 是一个用于管理 Amazon S3&#xff08;Simple Storage Service&#xff09;的 Python 模块。当前&#xff0c;Apache SeaTunnel 已经支持 S3File Sink Connector&#xff0c;为了更好地使用这个 Connector&#xff0c;有必要看一下这篇使用文档指南。 描述 将数据输出…

springboot druid多数据源配置,及druid监控

基础配置&#xff1a; springboot2.x版本 jdk1.8 依赖&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version> </dependency> &…

分享一个基于微信小程序的高校图书馆预约座位小程序 图书馆占座小程序源码 lw 调试

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

Linux高性能服务器编程 学习笔记 第五章 Linux网络编程基础API

我们将从以下3方面讨论Linux网络API&#xff1a; 1.socket地址API。socket最开始的含义是一个IP地址和端口对&#xff08;ip&#xff0c;port&#xff09;&#xff0c;它唯一表示了使用TCP通信的一端&#xff0c;本书称其为socket地址。 2.socket基础API。socket的主要API都定…

打开泰坦陨落提示msvcp120.dll丢失怎么办?三个解决方法快速解决

首先&#xff0c;我们来了解一下msvcr120.dll是什么文件。msvcr120.dll是一个动态链接库文件&#xff0c;它是Microsoft Visual C 2013 Redistributable中的一个组件。这个文件对于一些软件的运行是非常重要的&#xff0c;如果缺失或损坏&#xff0c;就会导致软件无法正常运行。…

从淘宝数据分析产品需求(商品销量总销量精准月销)

淘宝数据分析总体来说可以分为商品分析、客户分析、地区分析、时间分析四大维度(参考数据雷达的分析思路)。在这里我重点说商品分析。 在淘宝上开店的竞争还是非常激烈的&#xff0c;随便拿出一个单品就有很多竞品存在&#xff0c;所以做起来还是很难的&#xff0c;而想要在众…

Git:利用Git模拟企业级项目管理

文章目录 基础知识Git分支设计规范master分支release分支develop分支feature分支hotfix分支 模拟进行企业级项目管理 本篇主要总结的是企业级开发模型以及利用Git模拟企业级别的项目管理方式 基础知识 前面已经进行了全部的关于Git的各项操作&#xff0c;那么Git是作用于企业…

<Altium Designer>向PCB导入网表(.NET)

目录 01 AD PCB导入网表(.NET) 添加.NET文件到AD工程 通过show Differences操作导入器件 02 文章总结 大家好&#xff0c;这里是程序员杰克。一名平平无奇的嵌入式软件工程师。 硬件工程师使用的是Cadence的OrCAD画原理图&#xff0c;输出的是.NET网表&#xff0c;而杰克使…

自动化测试---选择框

radio框选择选项&#xff0c;直接用WebElement的click方法&#xff0c;模拟用户点击就可以了。 比如, 我们要在下面的html中&#xff1a; 1.先打印当前选中的老师名字 2.再选择 小雷老师 <div id"s_radio"><input type"radio" name"teach…

在静态方法中访问@Value注入的静态变量!!

一、 静态变量 static修饰的成员变量&#xff0c;称为静态成员变量&#xff0c;静态成员变量最大的特性&#xff1a;不属于某个具体的对象&#xff0c;是所有对象所共享的 简单来说&#xff1a;在某些类的对象中存在一些相同的成员变量&#xff0c;那么这种成员变量就可以设置…

解决VSCode下载速度很慢

这是VSCode的官网&#xff1a; Visual Studio Code - Code Editing. Redefined 按照官网的下载链接&#xff0c;速度实在是感人&#xff01; 解决办法也很简单&#xff0c;把链接换为CDN加速的链接 把下载链接中的az764295.vo.msecnd.net 替换为&#x1f449; vscode.cdn.azu…

MySQL数据库入门到精通1--基础篇(MySQL概述,SQL)

1. MySQL概述 1.1 数据库相关概念 目前主流的关系型数据库管理系统&#xff1a; Oracle&#xff1a;大型的收费数据库&#xff0c;Oracle公司产品&#xff0c;价格昂贵。 MySQL&#xff1a;开源免费的中小型数据库&#xff0c;后来Sun公司收购了MySQL&#xff0c;而Oracle又收…

压电换能器的工作原理和应用(功率放大器)

在日常生活中&#xff0c;可能会遇到很难测量的物理量&#xff0c;例如施加在金属上的机械应力、温度、压力水平等……对于所有这些应用&#xff0c;需要一种能够以我们熟悉的单位和校准来测量这些未知量的设备&#xff0c;而比较常用的设备是换能器。 换能器是一种电气设备&am…

华为OD机试 - 计算面积 - 逻辑分析(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

491. 递增子序列

题目链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 思路&#xff1a; 注意的点&#xff1a;1.是在原有的序列里找递增的子序列 示例 2&#xff1a; 输入&#xff1a;nums [4,4,3,2,1] 输出&#xff1a;[[4,4]] 记一…

业务上云的容器排障与思考

1 前言 此前我们部门已经完成了业务上云的目标&#xff0c;而随着业务请求量的激增&#xff0c;上云应用系统也面临着一些复杂的故障和挑战。 下文我就结合最近的容器排障工作&#xff0c;跟大家一起探讨如何优化系统的性能、扩展性和容错能力&#xff0c;为读者提供参考和借鉴…

Python从入门到放弃系列教程01

Python从入门到放弃系列教程01 第一章 01 初识Python Python的起源 1989年&#xff0c;为了打发圣诞节假期&#xff0c;吉多范罗苏姆&#xff08;龟叔&#xff09;决定开发一个新的解释程序&#xff08;Python雏形&#xff09;&#xff0c;1991年&#xff0c;第一个Python解…

QT支持的平台

简述&#xff1a; Qt是一个商业和开源许可的跨平台应用程序和UI框架。它由Qt公司与Qt项目社区一起在开源治理模式下开发。 使用Qt&#xff0c;您可以编写一次GUI应用程序&#xff0c;然后将它们部署到桌面&#xff0c;移动和嵌入式操作系统中&#xff0c;而无需重写源代码。 Qt…

【医学影像数据处理】 Dicom 文件格式处理汇总

在医学影像的数据存储领域&#xff0c;是存在一定的行业标准的。X光、CT机器等等医疗器械等生产企业&#xff0c;会依据行业标准&#xff0c;对采集的数据进行规范化的存储。 这里面就包括了大名鼎鼎的DICOM 3.0协议&#xff0c;上述的摄影形式大部分也都是以这种形式进行存储…