Vue + Volo.Abp 实现OAuth2.0客户端授权模式认证

关于无感刷新Token,我是这样子做的 JWT是全称是,是一个开放标准,用于将各方数据信息作为JSON格式进行对象传递,可以对数据进行可选的数字加密,可使用RSA或ECDSA进行公钥/私钥签名。 阅读详情


Volo.Abp的 身份服务器模块默认使用 IdentityServer4实现身份认证。

IdentityServer4是一个开源的OpenID Connect和OAuth 2.0框架,它实现了这些规范中的所有必需功能。

OAuth 2.0支持多种认证模式,本文主要介绍客户端授权模式认证。客户端授权模式流程如下图所示:

在这里插入图片描述

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

更多关于OAuth 2.0的介绍,可以参考阮一峰的理解OAuth 2.0。

注册Client

Client是第三方应用,它获得资源所有者的授权后便可以去访问资源。通常第三方登录中的第三方应用就是Client。如微博登录、QQ登录等。

要使用OAuth API需要先注册Client。通常,授权服务器需要提供一个管理页面,其中设置第三方应用的信息,包括应用名称、应用网站地址、应用简介、应用logo、授权回调地址等。

在本示例中,Client的注册是通过SeedData实现的。SeedData是在应用启动时自动执行的,它可以用来初始化数据库,如创建初始用户、角色、权限、和创建Client。

使用Abp.Cli创建一个认证服务分离的项目,

在认证服务项目AuthServer中打开appsettings.json,将vue的地址(http://localhost:8081)配置到ClientUrl和RedirectAllowedUrls

  "App": {
    "SelfUrl": "https://localhost:44350",
    "ClientUrl": "http://localhost:8081",
    "CorsOrigins": "https://*.Matoapp.com,https://localhost:44328,https://localhost:44377,http://localhost:8081,http://localhost:8082,http://localhost:8083",
    "RedirectAllowedUrls": "http://localhost:8081/continue,https://localhost:44380,https://localhost:44328,https://localhost:44369"
  },

项目中已经包含了一个SeedData类和一些配置,我们需要理解并修改这些配置。

我们将创建一个名为Matoapp的Client

appsettings.json配置如下

"OpenIddict": {
    "Applications": {
      "Matoapp_App": {
        "ClientId": "Matoapp_App",
        "RootUrl": "http://localhost:8081"
      },
      ...
    }
}

在OpenIddictDataSeedContributor中配置ClientType为public;

grantTypes为authorization_code和password;

scopes为该项目的服务名称以及需要的额外userinfo信息;

redirectUri客户端的回调地址。设置为http://localhost:8081/continue。

完整的CreateApplicationsAsync方法如下:

private async Task CreateApplicationsAsync()
{
    var commonScopes = new List<string>
    {
        OpenIddictConstants.Permissions.Scopes.Address,
        OpenIddictConstants.Permissions.Scopes.Email,
        OpenIddictConstants.Permissions.Scopes.Phone,
        OpenIddictConstants.Permissions.Scopes.Profile,
        OpenIddictConstants.Permissions.Scopes.Roles,
        "Matoapp",
    };

    var configurationSection = _configuration.GetSection("OpenIddict:Applications");

    

    //Console Test / Angular Client
    var consoleAndAngularClientId = configurationSection["Matoapp_App:ClientId"];
    if (!consoleAndAngularClientId.IsNullOrWhiteSpace())
    {
        var consoleAndAngularClientRootUrl = configurationSection["Matoapp_App:RootUrl"]?.TrimEnd('/');
        await CreateApplicationAsync(
            name: consoleAndAngularClientId!,
            type: OpenIddictConstants.ClientTypes.Public,
            consentType: OpenIddictConstants.ConsentTypes.Implicit,
            displayName: "Console Test / Angular Application",
            secret: null,
            grantTypes: new List<string>
            {
                OpenIddictConstants.GrantTypes.AuthorizationCode,
                OpenIddictConstants.GrantTypes.Password,
            },
            scopes: commonScopes,
            redirectUri: $"{consoleAndAngularClientRootUrl}/continue",
            clientUri: consoleAndAngularClientRootUrl,
            postLogoutRedirectUri: consoleAndAngularClientRootUrl,
                permissions: new List<string> {
                OpenIddictConstants.Permissions.Scopes.Roles,
                OpenIddictConstants.Permissions.Scopes.Profile,
                OpenIddictConstants.Permissions.Scopes.Email,
                OpenIddictConstants.Permissions.Scopes.Address,
                OpenIddictConstants.Permissions.Scopes.Phone,
                "Matoapp",
                }
        );
    }
    ... 
}


OAuth2.0授权

我们将使用oidc-client-ts简化Vue中的OAuth2.0授权。有关oidc-client-ts的更多信息,请参阅oidc-client-ts

创建一个vue项目,前端使用element-ui

安装oidc-client-ts

yarn add oidc-client-ts

创建vue-oidc-client

创建vue-oidc-client.ts文件,编写代码如下:

// vue 2 version
import Router from 'vue-router'
import Vue from 'vue'
import {
  UserManagerSettings,
  Log,
  User,
  UserManager,
  UserProfile,
  WebStorageStateStore,
  UserManagerEvents
} from 'oidc-client-ts'

/**
 * Indicates the sign in behavior.
 */
export enum SignInType {
  /**
   * Uses the main browser window to do sign-in.
   */
  Window,
  /**
   * Uses a popup window to do sign-in.
   */
  Popup
}

/**
 * Logging level values used by createOidcAuth().
 */
export enum LogLevel {
  /**
   * No logs messages.
   */
  None = 0,
  /**
   * Only error messages.
   */
  Error = 1,
  /**
   * Error and warning messages.
   */
  Warn = 2,
  /**
   * Error, warning, and info messages.
   */
  Info = 3,
  /**
   * Everything.
   */
  Debug = 4
}



/**
 * Creates an openid-connect auth instance.
 * @param authName - short alpha-numeric name that identifies the auth instance for routing purposes.
 * This is used to generate default redirect urls (slugified) and identifying routes that needs auth.
 * @param defaultSignInType - the signin behavior when `signIn()` and `signOut()` are called.
 * @param appUrl - url to the app using this instance for routing purposes. Something like `https://domain/app/`.
 * @param oidcConfig - config object for oidc-client.
 * See https://github.com/IdentityModel/oidc-client-js/wiki#configuration for details.
 * @param logger - logger used by oidc-client. Defaults to console.
 * @param logLevel - minimum level to log. Defaults to LogLevel.Error.
 */
export function createOidcAuth(
  authName: string,
  defaultSignInType: SignInType,
  appUrl: string,
  oidcConfig: UserManagerSettings,
) {
  // arg check
  if (!authName) {
    throw new Error('Auth name is required.')
  }
  if (
    defaultSignInType !== SignInType.Window &&
    defaultSignInType !== SignInType.Popup
  ) {
    throw new Error('Only window or popup are valid default signin types.')
  }
  if (!appUrl) {
    throw new Error('App base url is required.')
  }
  if (!oidcConfig) {
    throw new Error('No config provided to oidc auth.')
  }



  const nameSlug = slugify(authName)

  // merge passed oidcConfig with defaults
  const config = {
    automaticSilentRenew: true,
    userStore: new WebStorageStateStore({
      store: sessionStorage
    }),
    ...oidcConfig // everything can be overridden!
  }

  const mgr = new UserManager(config)

  let _inited = false
  const auth = new Vue({
    data() {
      return {
        user: null as User | null,
        myRouter: null as Router | null
      }
    },
    computed: {
      appUrl(): string {
        return appUrl
      },
      authName(): string {
        return authName
      },
      isAuthenticated(): boolean {
        return !!this.user && !this.user.expired
      },
      accessToken(): string {
        return !!this.user && !this.user.expired ? this.user.access_token : ''
      },
      userProfile(): UserProfile {
        return !!this.user && !this.user.expired
          ? this.user.profile
          : {
            iss: '',
            sub: '',
            aud: '',
            exp: 0,
            iat: 0
          }
      },
      events(): UserManagerEvents {
        return mgr.events
      }
    },

    methods: {
      startup() {
        let isCB = false // CB = callback
        if (matchesPath(config.popup_redirect_uri)) {
          mgr.signinPopupCallback()
          isCB = true
        } else if (matchesPath(config.silent_redirect_uri)) {
          mgr.signinSilentCallback()
          isCB = true
        } else if (matchesPath(config.popup_post_logout_redirect_uri)) {
          mgr.signoutPopupCallback()
          isCB = true
        }
        if (isCB) return Promise.resolve(false)

        if (_inited) {
          return Promise.resolve(true)
        } else {
          // load user from storage
          return mgr
            .getUser()
            .then(test => {
              _inited = true
              if (test && !test.expired) {
                this.user = test
              }
              return true
            })
            .catch(err => {
              return false
            })
        }
      },
      signIn(args?: any) {
        return signInReal(defaultSignInType, args)
      },
      signOut(args?: any) {
        if (defaultSignInType === SignInType.Popup) {
          const router = this.myRouter
          return mgr
            .signoutPopup(args)
            .then(() => {
              redirectAfterSignout(router)
            })
            .catch(() => {
              // could be window closed
              redirectAfterSignout(router)
            })
        }
        return mgr.signoutRedirect(args)
      },
      startSilentRenew() {
        mgr.startSilentRenew()
      },
      stopSilentRenew() {
        mgr.stopSilentRenew()
      }
    }
  })

  function signInIfNecessary() {
    if (auth.myRouter) {
      const current = auth.myRouter.currentRoute
      if (current && current.meta.authName === authName) {
        signInReal(defaultSignInType, { state: { current } })
          .then(() => {
            // auth.myRouter()
          })
          .catch(() => {
            setTimeout(signInIfNecessary, 5000)
          })
        // window.location.reload();
        // auth.myRouter.go(); //replace('/');
      }
    }
  }

  function signInReal(type: SignInType, args?: any) {
    switch (type) {
      case SignInType.Popup:
        return mgr.signinPopup(args)
      // case SignInType.Silent:
      //   return mgr.signinSilent(args)
    }
    return mgr.signinRedirect(args)
  }

  function redirectAfterSignout(router: Router | null) {
    if (router) {
      const current = router.currentRoute
      if (current && current.meta.authName === authName) {
        router.replace('/')
        return
      }
    }
    //   window.location.reload(true);
    if (appUrl) window.location.href = appUrl
  }

  /**
   * Translates user manager events to vue events and perform default actions
   * if necessary.
   */
  function handleManagerEvents() {
    mgr.events.addUserLoaded(user => {
      auth.user = user
    })

    mgr.events.addUserUnloaded(() => {
      auth.user = null

      // redirect if on protected route (best method here?)
      // signInIfNecessary()
    })

    mgr.events.addAccessTokenExpired(() => {
      auth.user = null
      signInIfNecessary()
      // if (auth.isAuthenticated) {
      //   mgr
      //     .signinSilent()
      //     .then(() => {
      //       Log.debug(`${authName} auth silent signin after token expiration`)
      //     })
      //     .catch(() => {
      //       Log.debug(
      //         `${authName} auth silent signin error after token expiration`
      //       )
      //       signInIfNecessary()
      //     })
      // }
    })

    mgr.events.addSilentRenewError(e => {
      // TODO: need to restart renew manually?
      if (auth.isAuthenticated) {
        setTimeout(() => {
          mgr.signinSilent()
        }, 5000)
      } else {
        signInIfNecessary()
      }
    })

    mgr.events.addUserSignedOut(() => {
      auth.user = null
      signInIfNecessary()
    })
  }

  handleManagerEvents()
  return auth
}

// general utilities

/**
 * Gets the path portion of a url.
 * @param url - full url
 * @returns
 */
function getUrlPath(url: string) {
  const a = document.createElement('a')
  a.href = url
  let p = a.pathname
  if (p[0] !== '/') p = '/' + p
  return p
}

/**
 * Checks if current url's path matches given url's path.
 * @param {String} testUrl - url to test against.
 */
function matchesPath(testUrl: string) {
  return (
    window.location.pathname.toLocaleLowerCase() ===
    getUrlPath(testUrl).toLocaleLowerCase()
  )
}

function slugify(str: string) {
  str = str.replace(/^\s+|\s+$/g, '') // trim
  str = str.toLowerCase()

  // remove accents, swap ñ for n, etc
  const from = 'ãàáäâẽèéëêìíïîõòóöôùúüûñç·/_,:;'
  const to = 'aaaaaeeeeeiiiiooooouuuunc------'
  for (let i = 0, l = from.length; i < l; i++) {
    str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i))
  }

  str = str
    .replace(/[^a-z0-9 -]/g, '') // remove invalid chars
    .replace(/\s+/g, '-') // collapse whitespace and replace by -
    .replace(/-+/g, '-') // collapse dashes

  return str
}

这里基于vue-oidc-client进行修改,因为vue-oidc-client是基于oidc-client,而oidc-client已经不再维护,所以我们使用oidc-client-ts

创建OAuth2.0认证跳转

在登录页面login/index.vue中,添加Auth2.0认证跳转按钮

<el-row
    type="flex"
    class="row-bg"
    justify="center"
    :gutter="10"
  >
    <el-col :span="10">
      <el-button
        :loading="loading"
        type="primary"
        @click.native.prevent="handleLogin"
      >
        登录
      </el-button>
    </el-col>
    <el-col :span="10">
      <el-button
        :loading="loading"
        @click.native.prevent="handleLoginAuth2"
      >
        Auth2.0
      </el-button>
    </el-col>
  </el-row>
</el-form>

在这里插入图片描述

点击跳转后,自动跳转到认证服务器的登录页面

在这里插入图片描述

当正确输入用户名和密码后,跳转到客户端的回调地址,并携带授权码code参数

环境变量中配置CLIENT_ID,资源服务器地址和认证服务器地址

VUE_APP_BASE_API = 'https://localhost:44377/'
VUE_APP_BASE_IDENTITY_SERVER = 'https://localhost:44350/'
VUE_APP_CLIENT_ID = 'Matoapp_App'

在login/index.vue中添加handleLoginAuth2方法

注意此处的redirect_uri要和认证服务器中的配置一致,否则跳转时会引发400 Bad Request

async handleLoginAuth2() {
  var loco = window.location;
  var appRootUrl = `${loco.protocol}//${loco.host}`;
  var idsrvAuth = createOidcAuth("main", SignInType.Window, appRootUrl, {
    authority: baseUrl,
    client_id: "Matoapp_App", // 'implicit.shortlived',
    response_type: "code",
    scope: "Matoapp",
    // test use
    prompt: "login",
    metadata: {
      issuer: baseUrl,
      authorization_endpoint: `${baseUrl}connect/authorize`,
      end_session_endpoint: `${baseUrl}logout`,
      token_endpoint: `${baseUrl}connect/token`,
    },
    redirect_uri: appRootUrl + "/continue",
    disablePKCE: true,
  });
  await idsrvAuth.signIn();
}

获取令牌

获取完成授权码后,需要通过授权码获取令牌(Token),此处将调用后端的接口,认证服务器核对了授权码和重定向URI后发放令牌

function Login(data, baseURL?: string) {
    return await ajaxRequest('/connect/token', 'POST', data, "/", baseURL)
}


@Action
public async LoginByCode(codeInfo: {
  code: string
  redirect_uri: string
}) {
  var code = codeInfo.code;
  var redirect_uri = codeInfo.redirect_uri;
  var data = null;
  var baseUrl = process.env.VUE_APP_BASE_IDENTITY_SERVER;
  await Login({
    grant_type: 'authorization_code',
    code: code,
    client_id: process.env.VUE_APP_CLIENT_ID,
    redirect_uri: redirect_uri
  }, baseUrl)
    .then(async (res) => {
      data = res;
      setToken(data.access_token);
      this.SET_TOKEN(data.access_token);
      return data
    })
  return data
}

获取Token后将其保存到vuex或Cookies中

创建回调页面

回调页面是在登录成功后,从回调到登录完成的过渡页面。

创建continue/index.vue,简单的显示登录成功的提示,常用的提示有“登录成功,正在为您继续”,“登录成功,正在为您跳转”等友好提示


<template>
  <div class="login-container">
    <el-result
      icon="success"
      title="登录成功"
      subTitle="正在继续,请稍候.."
    ></el-result>
  </div>
</template>

在这里插入图片描述

在登录成功后,会跳转到continue页面,回调地址会携带授权码,然后调用认证服务器的connect/token接口,获取token

创建onRouteChange函数,在此解析页面地址中的参数

export default class extends BaseVue {
  redirect?: string;
  otherQuery: Dictionary<string> = {};

  @Watch("$route", { immediate: true })
  private async onRouteChange(route: Route) {
    var loco = window.location;
    var appRootUrl = `${loco.protocol}//${loco.host}`;
    var query = route.query as Dictionary<string>;
    if (query) {
      this.redirect = query.redirect;
      this.otherQuery = this.getOtherQuery(query);
      if (this.otherQuery.code) {
        await UserModule.LoginByCode({
          code: this.otherQuery.code,
          redirect_uri: appRootUrl + "/continue",
        }).then(async (re) => {
          GetCurrentUserInfo(baseUrl)
            .then((re) => {
              var result = re as any;
              this.afterLoginSuccess(result);
            })
            .catch((err) => {
              console.warn(err);
            });
        });
      }
    }
  }

成功后将跳转到首页或者redirect指定的页面

  async afterLoginSuccess(userinfo) {
    this.$router
      .push({
        // path: this.redirect || "/",
        path: "/",
        query: this.otherQuery,
      })
      .catch((err) => {
        console.warn(err);
      });
  }

创建退出登录

只需要清除vuex或Cookies中的token即可,可以调用vue-oidc-client的signOut,但只是跳转到配置的登出地址,不会清除token(前提是redirectAfterSignout为true,并设置了post_logout_redirect_uri)

最终效果

在这里插入图片描述

volo.abp.freesql:FreeSql是一个优秀的Orm框架,首先感谢FreeSql的作者,♡Volo.Abp.FreeSql作为扩展模块,集成到Abp.VNext中。方便应用 volo.abp.freesql FreeSql是一个优秀的Orm框架,首先感谢FreeSql的作者,Abp VNext是一个很优秀的后台框架。 使用者做好任何事情也与本作者无关。觉得好用请给个星 FreeSql与Abp Venxt的设计模式交替,导致FreeSql某些功能不能在Abp Venxt应用 须知: 每个SqlConnection GetFreeSql()返回的IFreeSql实例相同;即同一个TDbContext类型的多个DbContext实例返回的IFreeSql实例相同 可以对fsql设置Aop事件,选择监视SQL; IFreeSql的ICodeFirst不可用 IFreeSql本身的成员IDbFirst,Transaction不可用; 安装 dotnet添加软件包AbpVNext.FreeSql 使用 ///在一个模块引入Volo.Abp.FreeSql 立即下载

相关推荐

oauth2-oidc-client:通用OAuth2OpenID Connect客户端库

OAUTH2 / OIDC客户端 安装: npm install oauth2-oidc-client --save 用法 auth.ts(Angular NativeScript) import { Component, OnInit } from "@angular/core"; import { RouterExtensions, PageRoute } from "nativescript-angular/router"; import * as webViewModule from "tns-core-modules/ui/web-view"; import * as url

怎样优雅地增删查改(一):从0开始搭建Volo.Abp项目

软件系统中数据库或者持久层的基本操作功能可以用Curd描述,Curd即 增加(Create)、更新(Update)、读取查询(Retrieve)和删除(Delete), 这4个单词的首字母。 在常见的业务系统中,对数据的大部分操作都是Curd,在实践的过程中对数据的筛选、排序、分页、关联查询等功能抽象和封装。 本系列博文将从0开始,逐步搭建一个基于Volo.Abp + Vue 的前后端分离的,具有Curd通用查询功能的项目。 项目介绍 本项目是基于一个简单的用户健康数据管理系统,我们将对业务常用的查询功能进

jevonsflash的专栏 1671

Hypermesh静力学仿真分析与模态分析-02

hypermesh中对较复杂实体进行静力学分析和模态分析,包含材料属性添加,载荷设置等等

Look_forward_的博客 6777

推荐开源项目:oidc-client-ts - 强大的JavaScript客户端身份验证库

在Web开发中,确保用户认证和授权的安全性始终是至关重要的。今天,我们向您推荐一个名为oidc-client-ts的开源项目,它是基于TypeScript实现的OpenID Connect (OIDC) 和OAuth2协议支持的浏览器端JavaScript库。这个项目不仅提供了一流的API体验,还注重未来协议的支持和发展。 ## 项目介绍 oidc-client-ts是一个由原 oidc-cl

gitblog_00066的博客 1021

超速U盘选购避坑指南:如何通过Rx Detect机制识别真伪USB3.0设备

本文深入解析了USB 3.0超速U盘的选购避坑要点,重点介绍了通过Rx Detect机制识别真伪设备的核心原理。文章提供了从软件检测到实际性能测试的完整方案,帮助消费者和技术人员有效辨别虚假的USB3.0设备,确保获得真正的超速传输体验。

weixin_29189373的博客 443

使用python实现随机正态分布数据,并导出到表格(超详细)

最近需要大量的正态分布的数据,为了方便,使用python实现随机正态分布,画出理想正态分布图和实际的矩形分布,并导出到表格里面。以上就是今天要讲的内容,本文仅仅简单介绍了使用python随机生成正态分布的数据,画图,并导出。正态分布是机器学习中很常见的一种需求,往往需要大量的数据进行测试,此时,本文的方法就可以大大减少因数据而花费的时间啦!如有问题,欢迎评论区留言啊!

m0_53533553的博客 1万+

探索高效管理:Id4Admin - 简约且强大的身份认证与管理框架

探索高效管理:Id4Admin - 简约且强大的身份认证与管理框架 在数字化转型的时代,一个强大而易用的身份管理和认证系统是每个企业、网站或应用的基础。今天,我们向您推荐一款开源项目——Id4Admin,它是一个基于.NET Core和Abp Framework构建的高效能、模块化身份认证解决方案。 项目简介 Id4Admin 是对 IdentityServer4 的封装,集成了 Volo.Abp...

gitblog_00046的博客 943

把 SAP 配置当成一张地图:用 IMG 把实施、变更与治理串成一条清晰的路

摘要 SAP IMG(Implementation Guide)是SAP实施的核心导航体系,它将客户需求转化为系统配置,提供结构化路径和文档化支持。IMG不是简单的菜单树,而是包含业务落地路径、标准化配置活动和文档化知识的实施语言。通过三种视图(Reference、Enterprise、Project)解决不同管理问题,其中Project IMG尤其重要,可管理项目范围、分工和进度。实际工作中应通过SPRO事务码访问IMG,将探索与执行分开管理。案例显示,跨国企业通过Project IMG可有效控制组织架构

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 229

.NET Core之 ABP从入门到精通

ABP是一套基于WEB的快速开发引用框架,和DDD领域驱动开发有很好的结合。 主要特性:模块化,多租户,认证授权,虚拟文件系统,主题系统,后台作业,事件总线,对象映射,依赖注入,数据过滤等。 接下来用代码的方式介绍ABP框架的使用。 一.VS创建一个ABP控制台程序打出Hello word! 1.首先引入框架包 Nuget中安装volo.abp.core 2.创建模块 所谓的ABP模块在代码中体现就是类,我们创建一个HelloABPModule的类,然后引入using Volo.Abp.Modulari

csdn2990的博客 1万+

在 ABAP Push Channel 里玩转 Detached Client:用 Attach Handle 把 WebSocket 连接交接给业务逻辑

摘要:本文介绍了ABAP Push Channels (APC)中的Detached Client技术,该技术通过将WebSocket连接建立与后续处理分离,实现更高效的实时通信。核心流程包括创建连接、使用connect_and_detach交接连接、以及通过attach handle重新附着发送消息。文章还探讨了PCP协议和AMC技术在企业级推送场景中的应用,并通过示例代码展示了Detached Client的具体实现方式。这种设计解决了传统轮询的资源浪费问题,特别适用于生产线监控、客服系统等需要服务端主

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 2315

SE80 Repository Information System 在 SAP CRM WebClient UI 组件分析中的实战指南

在 SAP CRM WebClient UI 开发过程中,如何快速定位组件、视图、上下文节点以及各类运行时对象,一直是调试效率的关键。(后文简称RIS)正是隐藏在SE80里的瑞士军刀,帮助开发者按照组件集、数据元素、上下文节点属性等多维条件进行交叉检索。官方博客将它描述为本文结合多年项目经验,系统梳理RIS的功能脉络与实战技巧,同时引入真实案例,示范如何借助它在几分钟内完成以往动辄数小时的排查任务。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 2万+

oidc-client-ts 项目常见问题解决方案

`oidc-client-ts` 是一个开源项目,旨在为基于浏览器的 JavaScript 应用程序提供 OpenID Connect (OIDC) 和 OAuth2 协议的支持。该项目是 `IdentityModel/oidc-client-js` 的一个分支,并且已经从 JavaScript 迁移到 TypeScript,提供了类似的 API。`oidc-client-ts` 主要用于客户端应...

gitblog_01158的博客 765

YOLOv13改进目录一览 | 涉及卷积层、轻量化、注意力、损失函数、Backbone、SPPF、Neck、检测头等全方位改进

1️⃣ 已更新种不同的改进方案,专栏内的每篇文章皆,均可顺利运行。2️⃣ 订阅专栏即可进群获取以及从的各种答疑内容。3️⃣ 全新的YOLOv13改进专栏,,只为更好的满足论文发表的要求。专栏内容,专栏实时评分,全网最高,质量保证。🎫。。

Limiiiing的博客 9156

oidc-client-ts 项目使用教程

oidc-client-ts 项目的目录结构如下: ``` oidc-client-ts/ ├── docs/ ├── samples/ │ └── Parcel/ ├── scripts/ ├── src/ ├── test/ ├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .gitattributes ├── .giti...

gitblog_01123的博客 723

软考高级系统架构设计师历年论文真题深度分析(2011-2025)

本文系统分析了2011-2025年系统架构设计师论文真题,归纳出十大主题类别,包括架构风格、企业集成、数据技术、非功能属性等。研究发现新兴技术占比最高(18.8%),企业集成、数据架构和非功能属性为三大核心考点。论文命题呈现明显技术演进特征,从经典架构理论逐步转向云原生、AI融合等前沿方向。备考建议重点关注高频考点和技术前沿,同时强化场景化解决方案能力。分析表明,架构师考试持续跟踪行业技术发展,注重理论与实践结合,为考生提供了明确的备考方向。

一几文的编程学习笔记。 2038

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

推荐开源项目:oidc-client-ts - 强大的TypeScript实现的OpenID Connect和OAuth2库

在Web开发中,安全认证和授权是不可忽视的关键环节。oidc-client-ts是一个由TypeScript编写的JavaScript库,它为客户端浏览器应用程序提供了对OpenID Connect(OIDC)和OAuth2协议的支持。无论是新手还是经验丰富的开发者,这个库都将是你实现身份验证和访问控制的理想选择。 ## 项目介绍 oidc-client-ts从著名的oidc-client-j

gitblog_00819的博客 1164

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

oidc-client-ts 开源项目教程

`oidc-client-ts` 是一个 TypeScript 库,旨在为基于浏览器的 JavaScript 应用程序提供 OpenID Connect (OIDC) 和 OAuth2 协议支持。该库不仅提供协议支持,还包括用户会话和访问令牌管理功能。项目源自于已停止开发的 `oidc-client-js`,并已迁移到 TypeScript,以继续支持 OAuth 2.1 协议。 ## 项目快速

gitblog_00193的博客 624

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Volo.Abp升级小记(二)创建全新微服务模块

假设有一个按照搭建的微服务项目,并安装好了abp-cli。需要创建一个名为GDMK.CAH.Common的模块,并在模块中创建标签管理功能。

jevonsflash的专栏 2459

vue+springmvc前后端分离开发(十四)(用oauth2来管理用户令牌)

这一节主要讲解了如何在后端配置oauth2,数据库中oauth2的体现,以及如何对oauth2提供的api进行交互

qq_44938644的博客 913

Oauth2单点登录 vue前后端分离实现

Oauth2+Vue实现前后端分离,多域名下的单点登录。

weixin_39515823的博客 5608

utils中的oidclient文件

utils中的oidclient文件

qq_46617584的博客 309

微服务+vue架构+oauth2+权限系统的实现

思路: 1.登录认证得到token 2用token去经过网关解析成id,跳转微服务getinfo。。 网关不过滤就重启 1.网关配置:拦截器实现,要放行oauth/token,所有请求接口经过网关ip转发。 网关白名单放行/yz/oauth/token,不经过过滤器, (网关都要加前缀,好进行分发) 大坑,ip不要用前端映射,其实是需要重启。有缓存。 网关搭建 2.配置认证服务器 任何接口资源/users/me都要放行,不带yz网关前缀,因为是网关转过来的。 @Configuration

qq_42434039的博客 2878

使用 Vue.js 构建 OAuth2 授权同意页面

本文介绍了在前后端分离项目中如何使用Vue.js构建OAuth2授权同意页面,结合Spring Authorization Server和Spring Security构建OAuth2服务器和客户端。

new_ord的博客 2062

Abp源码分析之虚拟文件系统Volo.Abp.VirtualFileSystem

Volo.Abp.VirtualFileSystem 是 ABP 框架中的一个重要组件,提供了一种抽象文件系统的方式,使应用程序可以轻松访问和管理文件资源。本文介绍了如何在 MVC 项目中使用 `Volo.Abp.VirtualFileSystem`,包括新建项目、配置模块、添加资源文件以及读取资源文件的具体步骤。通过统一的接口处理文件和目录,无论实际存储位置如何,应用程序都能更加灵活地切换不同的文件存储方式。

shiningrise的博客 582

C# Volo.Abp使用原生sql语句查询

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用

KKKoyomi的博客 3832
上一篇: 怎样优雅地增删查改(一):从0开始搭建Volo.Abp项目
下一篇: 怎样优雅地增删查改(二):扩展身份管理模块
林晓lx
林晓lx 2017年优快云移动跨平台大赛优秀奖获得者 2017年优快云移动跨平台大赛优秀奖获得者
博客等级 码龄15年 3761粉丝 123原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林晓lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值