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

编程入门 行业动态 更新时间:2024-10-04 05:29:07

OAuth2 完成<a href=https://www.elefans.com/category/jswz/34/1771301.html style=用户登录【详解】(含码云 gitee 的实现范例)"/>

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

什么是 OAuth2 登录?

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

OAuth2 的工作原理

OAuth2 实战范例

前端第三方登录图标

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

核心代码

    <!-- gitee登录 --><el-avatarclass="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(`=${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: '=authorization_code',giteeUserAPI: '',};

.env

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

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

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

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

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

5. 点击创建应用按钮

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

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 {// 生成 tokenconst 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_tokenconst 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// 假如已经存在,直接返回tokenconst 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;}// 假如不存在,新建用户后返回 tokenconst 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 实现了跨域传参,其详细用法可参考
/

前端监听后端消息获取到 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中获取tokenconst token = sessionStorage.getItem("token");if (!isPublic && token) {// 若当前api不是公开接口,并且token存在,则向headers中添加tokenconfig.headers.Authorization = "Bearer " + token;}

axios 更详细的使用,详见

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

  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>

更多推荐

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

本文发布于:2024-02-28 12:02:21,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1769542.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:用户登录   范例   详解   gitee   含码云

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!