什么是 oauth2.0
OAuth 不是一个 API 或者服务, 而是一个验证授权(Authorization)的开放标准,所有人都有基于这个标准实现自己的 OAuth。 更具体来说,OAuth 是一个标准,app 可以用来实现 secure delegated access. OAuth 基于 HTTPS,以及 APIs,Service 应用使用 access token 来进行身份验证。 OAuth 主要有 OAuth 1.0a 和 OAuth 2.0 两个版本,并且二者完全不同,且不兼容。OAuth2.0 是目前广泛使用的版本,我们多数谈论 OAuth 时,为 OAuth2.0。 更多的话我推荐 阮一峰 写的文章 oauth2.0
实现 oauth2.0 授权登录
其实 oauth 登录方式有四种,常用就是授权登录,我这里用 gitee 第三方登录举例 先要去 gitee 上创建第三方应用登录,网址 gitee-api
上边的这个图主要东西就是client_id和client_secret,这两个东西你创建完应用就会有
client_id用来获取code,code和client_secret用来获取access_token
当然还有回调地址 redirect_uri 也很重要。主要步骤如下:
- 调用第三方服务器接口来获取code,用get请求,参数主要是client_id和redirect_uri。client_id需要创建第三方应用登录才会有
- 第三方服务器调用redirect_uri并返回code
- 接着去调用第三方服务器接口来获取access_token,post请求,参数主要是client_id,client_secret。 client_id和client_secret需要创建第三方应用登录才会有
- 有了access_token后就可以调用第三方服务器接口获取用户的个人信息
要仔细地查看第三方服务器接口的文档,gitee 的话 -> gitee
代码演示(next.js)
- 组件调用第三方接口
const handleOAuthGitee = () => {
const { ClientID, RedirectUri } = giteeLoginInfo
window.open(
`https://gitee.com/oauth/authorize?client_id=${ClientID}&redirect_uri=${RedirectUri}&response_type=code`
)
}
- 自己的服务器调用接口
import type { NextApiRequest, NextApiResponse } from 'next'
import { withIronSessionApiRoute } from 'iron-session/next'
// import {getIronSession} from 'iron-session'
import { ISession, ResponseData } from 'pages/api/index'
import { ironOptions } from 'config'
import { prepareConnection } from 'db/index'
import { User, UserAuth } from 'db/entity/index'
import { Cookie } from 'next-cookie'
import { setCookie } from 'utils/index'
import { giteeLoginInfo } from 'config/index'
import request from 'service/fetch'
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
const session: ISession = req.session
//http://localhost:3003/api/oauth/gitee
const cookies = Cookie.fromApiRoute(req, res)
const { code, error } = req.query
if (!code && error === 'access_denied') {
res.redirect(302, '/')
return
}
// 自己的git登录时需要的一些参数配置
const { ClientID, ClientSecrets, RedirectUri } = giteeLoginInfo
// 调用第三方接口获取access_token
try {
const url = `https://gitee.com/oauth/token?grant_type=authorization_code&code=${code}&client_id=${ClientID}&redirect_uri=${RedirectUri}`
const result = await request({
url,
method: 'post',
data: { client_secret: ClientSecrets },
}).catch((err) => {
throw new Error(err.message)
})
const { access_token } = result as any
const giteeUserInfo = await request({
url: 'https://gitee.com/api/v5/user',
params: { access_token },
})
const db = await prepareConnection()
const userAuth = await db.getRepository(UserAuth).findOne(
{
identity_type: 'gitee',
identifier: ClientID,
},
{ relations: ['user'] }
)
if (userAuth) {
//之前用gitee登录过
const user = userAuth.user
const { id, nickname, avatar } = user
//更新credential
try {
await db
.getRepository(UserAuth)
.update({ identifier: ClientID }, { credential: access_token })
} catch (error) {
console.log('error', error)
}
session.userId = id
session.nickname = nickname
session.avatar = avatar
await session.save()
setCookie(cookies, { userId: id, nickname, avatar })
res.redirect(302, '/')
} else {
//新用户
const { login = '', avatar_url = '' } = giteeUserInfo as any
const user = new User()
user.nickname = login
user.avatar = avatar_url
const userAuth = new UserAuth()
userAuth.identity_type = 'gitee'
userAuth.identifier = ClientID
userAuth.credential = access_token
userAuth.user = user
const userAuthRepo = db.getRepository(UserAuth)
const resUserAuth = await userAuthRepo.save(userAuth)
const { id, nickname, avatar } = resUserAuth.user
session.userId = id
session.nickname = nickname
session.avatar = avatar
await session.save()
setCookie(cookies, { userId: id, nickname, avatar })
res.redirect(302, '/')
}
} catch (error) {
console.log('error', error)
}
}
export default withIronSessionApiRoute(handler, ironOptions)