OAuth2 完成用户登录【详解】(含码云 gitee 的实现范例)

简介: OAuth2 完成用户登录【详解】(含码云 gitee 的实现范例)

什么是 OAuth2 登录?

OAuth2 是当下广泛流行的第三方应用授权登录方案,如微信扫码登录。

OAuth2 的工作原理

OAuth2 实战范例

前端第三方登录图标

以码云 gitee 为例,在登录页面添加登录图标(vue+element)

核心代码

    <!-- gitee登录 -->
    <el-avatar
      class="bnt"
      size="medium"
      :src="giteeUrl"
      @click.native="loginByGitee"
    ></el-avatar>
import giteeLogo from "@/assets/images/giteeLogo.svg";
 

data() 中

giteeUrl: giteeLogo,
 
    loginByGitee() {
      // 后端服务器跳转到gitee登录页的接口
      window.open("http://127.0.0.1:7002/api/users/passport/gotoGitee");
    },

后端跳转到第三方应用

egg.js 搭建的后端服务器为例

app\router.ts

  // 跳转到 gitee 官网进行登录认证
  router.get('/api/users/passport/gotoGitee', controller.user.gotoGitee);

app\controller\user.ts

  async gotoGitee() {
    const { app, ctx } = this;
    const { cid, redirectURL } = app.config.giteeOauthConfig;
    ctx.redirect(
      `https://gitee.com/oauth/authorize?client_id=${cid}&redirect_uri=${redirectURL}&response_type=code`
    );
  }

config\config.default.ts

  // gitee 的认证配置
  const giteeOauthConfig = {
    cid: process.env.GITEE_CID,
    secret: process.env.GITEE_SECRET,
    redirectURL: 'http://localhost:7002/api/users/passport/gitee/callback',
    authURL: 'https://gitee.com/oauth/token?grant_type=authorization_code',
    giteeUserAPI: 'https://gitee.com/api/v5/user',
  };

env

GITEE_CID = "e7440791b41de36978e65b258228e26b5d746**********"
GITEE_SECRET = "c1744f6a7224182b3c39df2b76f2608e750a46c4f907b3569*****"

GITEE_CID 和 GITEE_SECRET 来自码云自动生成的密钥,创建方法如下:

  1. 登录码云官网,进入设置页
    https://gitee.com/
  2. 打开第三方应用
  3. 创建应用
  4. 录入相关信息

自定义需使用 OAuth2 登录的应用名称

上传需使用 OAuth2 登录的应用的logo,填写应用的域名(暂时没购买也没关系),填写应用回调地址,通常对应后端接口。

点击创建应用按钮

默认登录后可以访问获取用户信息的接口,若需访问其他接口,可根据需要打钩

6. 完成创建后,便会得到 GITEE_CID 和 GITEE_SECRET

后端响应第三方应用授权后的回调生成 token

app\router.ts

  // 通过gitee登录的回调
  router.get(
    '/api/users/passport/gitee/callback',
    controller.user.oauthByGitee
  );

app\controller\user.ts

  async oauthByGitee() {
    const { ctx } = this;
    const { code } = ctx.request.query;
    try {
      // 生成 token
      const token = await ctx.service.user.loginByGitee(code);
      // 渲染授权成功页面(向前端传递token)
      await ctx.render('success.nj', { token });
    } catch (e) {
      return ctx.helper.error({ ctx, errorType: 'giteeOauthError' });
    }
  }

app\service\user.ts

  async loginByGitee(code: string) {
    const { ctx, app } = this;
    // 获取 access_token
    const accessToken = await this.getAccessToken(code);
    // 获取用户的信息
    const user = await this.getGiteeUserData(accessToken);

    // 检查用户是否已注册
    const { id, name, avatar_url, email } = user;
    // id 默认为数字类型,此处转换为字符串类型
    const stringId = id.toString();
    // 为避免不同平台id相同,此处在id前加上平台名称,如Gitee + id,Github + id

    // 假如已经存在,直接返回token
    const existUser = await this.findByAccount(`Gitee${stringId}`);
    if (existUser) {
      const token = app.jwt.sign(
        { account: existUser.account, _id: existUser._id },
        app.config.jwt.secret,
        { expiresIn: app.config.jwtExpires }
      );
      return token;
    }
    // 假如不存在,新建用户后返回 token
    const userCreatedData: Partial<UserProps> = {
      oauthID: stringId,
      provider: 'gitee',
      account: `Gitee${stringId}`,
      picture: avatar_url,
      nickName: name,
      email,
      type: 'oauth',
    };
    const newUser = await ctx.model.User.create(userCreatedData);
    const token = app.jwt.sign(
      { account: newUser.account, _id: newUser._id },
      app.config.jwt.secret,
      { expiresIn: app.config.jwtExpires }
    );
    return token;
  }

获取 access_token

  async getAccessToken(code: string) {
    const { ctx, app } = this;
    const { cid, secret, redirectURL, authURL } = app.config.giteeOauthConfig;
    const { data } = await ctx.curl(authURL, {
      method: 'POST',
      contentType: 'json',
      dataType: 'json',
      data: {
        code,
        client_id: cid,
        redirect_uri: redirectURL,
        client_secret: secret,
      },
    });
    app.logger.info(data);
    return data.access_token;
  }

获取用户信息

  async getGiteeUserData(access_token: string) {
    const { ctx, app } = this;
    const { giteeUserAPI } = app.config.giteeOauthConfig;
    const { data } = await ctx.curl<GiteeUserResp>(
      `${giteeUserAPI}?access_token=${access_token}`,
      {
        dataType: 'json',
      }
    );
    return data;
  }

后端渲染页面向前端传递 token

app\view\success.nj

<!doctype html>
<html class="no-js" lang="">

<head>
  <meta charset="utf-8">
  <title>授权成功</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <h1>授权成功</h1>
</body>
<script>
  window.onload = function() {
    setTimeout(() => {
      const message = {
        type: 'oauth-token',
        token: '{{token}}'
      }
      window.opener.postMessage(message, 'http://localhost:8080')
      window.close()
    }, 1000)
  }
</script>
</html>

此处使用 window.opener.postMessage 实现了跨域传参,其详细用法可参考

https://juejin.cn/post/7041363060522975246/

前端监听后端消息获取到 token

获取到 token 后存入sessionStorage,并获取用户信息

  mounted() {
    window.addEventListener("message", (res) => {
      const { type, token } = res.data;
      sessionStorage.setItem("token", token);
      if (type === "oauth-token") {
        this.$http.get("/api/api/users/getUserInfo").then((res) => {
          this.userInfo = res;
          sessionStorage.setItem("userName", res.data.data.nickName);
          this.$router.push("/index");
        });
      }
    });
  },

针对需 token 的接口,统一添加 token

src\axios.js

// 添加请求拦截器
axios.interceptors.request.use(
  function (config) {
    // 无需带token的请求路径,正则校验(/public 和 /login 开头的api 无需token )
    let publicPath = [/^\/public/, /^\/login/];
    // 是否是公开接口(公开接口无需token)
    let isPublic = false;
    // 判断当前api是否是公开接口
    publicPath.map((path) => {
      isPublic = isPublic || path.test(config.url);
    });
    // 从sessionStorage中获取token
    const token = sessionStorage.getItem("token");
    if (!isPublic && token) {
      // 若当前api不是公开接口,并且token存在,则向headers中添加token
      config.headers.Authorization = "Bearer " + token;
    }

axios 更详细的使用,详见

https://blog.csdn.net/weixin_41192489/article/details/113878619

前端首页获取用户信息更新登录状态

  mounted() {
    this.userName = sessionStorage.getItem("userName");
  },
      <div class="loginBox" v-if="!userName">
        <el-button type="text" class="btn" @click="gotoLogin">登录</el-button>
        <el-divider direction="vertical"></el-divider>
        <el-button type="text" class="btn" @click="gotoRegister">注册</el-button>
      </div>
      <div class="helloBox" v-else>
        <span>欢迎你,{{ userName }} !</span>
        <el-button @click="logout" type="text" class="btn">退出</el-button>
      </div>
    </div>


目录
相关文章
|
2月前
|
JSON 缓存 JavaScript
❤Nodejs 第十章(用户信息token认证和登录接口开发)
【4月更文挑战第10天】本文介绍了Node.js中实现用户信息token认证和登录接口的步骤。express-jwt的使用,接着创建基本的Express服务器,然后导入并使用jsonwebtoken和express-jwt。设置一个密钥,并定义一个中间件处理token验证。示例展示了登录接口的实现。遇到登录判断失效的问题后,对判断条件进行了优化。
82 2
|
2月前
|
开发工具 数据安全/隐私保护 git
Github新的认证方式
Github新的认证方式
98 0
|
存储 API 数据安全/隐私保护
12-SpringSecurity:通过OAuth2集成Github登录
12-SpringSecurity:通过OAuth2集成Github登录
402 0
12-SpringSecurity:通过OAuth2集成Github登录
|
Java 应用服务中间件 nginx
GitLab 配置 OAuth2 实现第三方登录,简直太方便了!
GitLab 配置 OAuth2 实现第三方登录,简直太方便了!
GitLab 配置 OAuth2 实现第三方登录,简直太方便了!
|
23天前
|
安全 关系型数据库 API
Nuxt3 实战 (九):使用 Supabase 实现 Github 认证鉴权
这篇文章介绍了如何在Supabase中使用Github授权登录并实现用户身份验证。文章首先说明了Supabase采用postgresql的Row Level Security(RLS)机制来控制不同用户对数据表访问权限的重要性,然后详细介绍了配置Github OAuth Apps的过程,包括创建新的OAuth应用、填写项目信息、设置回调URL等步骤。接着,文章展示了在Nuxt3中实现登录界面的代码示例,最后总结了通过本教程可以学习到的技能,并预告了下一篇文章将介绍如何在Nuxt3中创建RESTful风格API并结合Supabase数据库完成CURD操作。
Nuxt3 实战 (九):使用 Supabase 实现 Github 认证鉴权
|
2月前
|
JSON 安全 Java
SpringBoot 整合 JustAuth 实现第三方登录 | gitee登录
SpringBoot 整合 JustAuth 实现第三方登录 | gitee登录
143 0
|
10月前
|
网络安全 开发工具 数据安全/隐私保护
GitHub不再支持密码验证解决方案:SSH免密与Token登录配置
今天提交代码,push到GitHub上,突然出现这个问题。 remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information. fatal: unable to acce
|
存储 数据安全/隐私保护 索引
接入 GitHub OAuth 登录,user表设计
接入 GitHub OAuth 登录,user表设计
74 0
jira学习案例43-用useAsync获取用户信息
jira学习案例43-用useAsync获取用户信息
66 0
jira学习案例43-用useAsync获取用户信息
|
存储 安全 数据安全/隐私保护
OAuth2.0整合gitee第三方登录注册
OAuth2.0整合gitee第三方登录注册
OAuth2.0整合gitee第三方登录注册

热门文章

最新文章

  • 1
    流量控制系统,用正则表达式提取汉字
    25
  • 2
    Redis09-----List类型,有序,元素可以重复,插入和删除快,查询速度一般,一般保存一些有顺序的数据,如朋友圈点赞列表,评论列表等,LPUSH user 1 2 3可以一个一个推
    26
  • 3
    Redis08命令-Hash类型,也叫散列,其中value是一个无序字典,类似于java的HashMap结构,Hash结构可以将对象中的每个字段独立存储,可以针对每字段做CRUD
    26
  • 4
    Redis07命令-String类型字符串,不管是哪种格式,底层都是字节数组形式存储的,最大空间不超过512m,SET添加,MSET批量添加,INCRBY age 2可以,MSET,INCRSETEX
    27
  • 5
    S外部函数可以访问函数内部的变量的闭包-闭包最简单的用不了,闭包是内层函数+外层函数的变量,简称为函数套函数,外部函数可以访问函数内部的变量,存在函数套函数
    24
  • 6
    Redis06-Redis常用的命令,模糊的搜索查询往往会对服务器产生很大的压力,MSET k1 v1 k2 v2 k3 v3 添加,DEL是删除的意思,EXISTS age 可以用来查询是否有存在1
    31
  • 7
    Redis05数据结构介绍,数据结构介绍,官方网站中看到
    22
  • 8
    JS字符串数据类型转换,字符串如何转成变量,+号只要有一个是字符串,就会把另外一个转成字符串,- * / 都会把数据转成数字类型,数字型控制台是蓝色,字符型控制台是黑色,
    20
  • 9
    JS数组操作---删除,arr.pop()方法从数组中删除最后一个元素,并返回该元素的值,arr.shift() 删除第一个值,arr.splice()方法,删除指定元素,arr.splice,从第一
    20
  • 10
    定义好变量,${age}模版字符串,对象可以放null,检验数据类型console.log(typeof str)
    19