diff --git a/README.md b/README.md index 143dc1fd6..e625162f9 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ OpenIsle 基于 Spring Boot 构建,提供社区后台常见的注册、登录 * **用户体系**:注册、登录,密码使用 BCrypt 加密 * **JWT 认证**:登录后获得 Token,接口通过 `Authorization: Bearer` 认证 * **Google 登录**:支持使用 Google OAuth 登录 +* **GitHub 登录**:支持使用 GitHub OAuth 登录 * **邮件通知**:抽象 `EmailSender`,默认实现基于 Resend * **角色权限**:内置 `ADMIN` 与 `USER`,管理员接口以 `/api/admin/**` 提供 * **文章与评论**:支持分类、评论及多级回复 @@ -43,10 +44,13 @@ OpenIsle 基于 Spring Boot 构建,提供社区后台常见的注册、登录 - `MYSQL_USER`:数据库用户名 - `MYSQL_PASSWORD`:数据库密码 - `RESEND_API_KEY`:Resend 邮件服务 API Key - - `COS_BASE_URL`:腾讯云 COS 访问域名 - - `GOOGLE_CLIENT_ID`:Google OAuth 客户端 ID - - `VUE_APP_GOOGLE_CLIENT_ID`:前端 Google OAuth 客户端 ID - - `JWT_SECRET`:JWT 签名密钥 + - `COS_BASE_URL`:腾讯云 COS 访问域名 + - `GOOGLE_CLIENT_ID`:Google OAuth 客户端 ID + - `VUE_APP_GOOGLE_CLIENT_ID`:前端 Google OAuth 客户端 ID + - `GITHUB_CLIENT_ID`:GitHub OAuth 客户端 ID + - `GITHUB_CLIENT_SECRET`:GitHub OAuth 客户端密钥 + - `VUE_APP_GITHUB_CLIENT_ID`:前端 GitHub OAuth 客户端 ID + - `JWT_SECRET`:JWT 签名密钥 - `JWT_EXPIRATION`:JWT 过期时间(毫秒) - `PASSWORD_STRENGTH`:密码强度(LOW、MEDIUM、HIGH) - `CAPTCHA_ENABLED`:是否启用验证码(true/false) @@ -68,6 +72,7 @@ mvn spring-boot:run - `POST /api/auth/register`:注册新用户 - `POST /api/auth/login`:登录并获取 Token - `POST /api/auth/google`:Google 登录并获取 Token +- `POST /api/auth/github`:GitHub 登录并获取 Token - `GET /api/config`:查看验证码开关配置 - 需要认证的接口示例:`GET /api/hello`(需 `Authorization` 头) - 管理员接口示例:`GET /api/admin/hello` diff --git a/open-isle-cli/src/assets/icons/github.svg b/open-isle-cli/src/assets/icons/github.svg new file mode 100644 index 000000000..c4af465ca --- /dev/null +++ b/open-isle-cli/src/assets/icons/github.svg @@ -0,0 +1,301 @@ + + + + + + + Page not found · GitHub + + + + +
+
+ +
+
+
+ + 404 “This is not the web page you are looking for” + + + + + + + + + + + + +
+ +
+ +
+ + +
+ Contact Support — + GitHub Status — + @githubstatus +
+ + + + +
+ + diff --git a/open-isle-cli/src/main.js b/open-isle-cli/src/main.js index b284c951d..09b01c013 100644 --- a/open-isle-cli/src/main.js +++ b/open-isle-cli/src/main.js @@ -18,6 +18,7 @@ export const API_PORT = 8081 // export const API_BASE_URL = API_PORT ? `${API_DOMAIN}:${API_PORT}` : API_DOMAIN export const API_BASE_URL = ""; export const GOOGLE_CLIENT_ID = '777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com' +export const GITHUB_CLIENT_ID = '' export const toast = useToast() initTheme() diff --git a/open-isle-cli/src/router/index.js b/open-isle-cli/src/router/index.js index a870d149c..cfeef79ed 100644 --- a/open-isle-cli/src/router/index.js +++ b/open-isle-cli/src/router/index.js @@ -11,6 +11,7 @@ import NewPostPageView from '../views/NewPostPageView.vue' import SettingsPageView from '../views/SettingsPageView.vue' import ProfileView from '../views/ProfileView.vue' import NotFoundPageView from '../views/NotFoundPageView.vue' +import GithubCallbackPageView from '../views/GithubCallbackPageView.vue' const routes = [ { @@ -68,6 +69,11 @@ const routes = [ name: 'users', component: ProfileView }, + { + path: '/github-callback', + name: 'github-callback', + component: GithubCallbackPageView + }, { path: '/404', name: 'not-found', diff --git a/open-isle-cli/src/utils/github.js b/open-isle-cli/src/utils/github.js new file mode 100644 index 000000000..9b2e4bc78 --- /dev/null +++ b/open-isle-cli/src/utils/github.js @@ -0,0 +1,41 @@ +import { API_BASE_URL, GITHUB_CLIENT_ID, toast } from '../main' +import { setToken, loadCurrentUser } from './auth' + +export function githubAuthorize(state = '') { + if (!GITHUB_CLIENT_ID) { + toast.error('GitHub 登录不可用') + return + } + const redirectUri = `${window.location.origin}/github-callback` + const url = `https://github.com/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=user:email&state=${state}` + window.location.href = url +} + +export async function githubExchange(code, state, reason) { + try { + const res = await fetch(`${API_BASE_URL}/api/auth/github`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code, redirectUri: `${window.location.origin}/github-callback`, reason, state }) + }) + const data = await res.json() + if (res.ok && data.token) { + setToken(data.token) + await loadCurrentUser() + toast.success('登录成功') + return true + } else if (data.reason_code === 'NOT_APPROVED') { + toast.info('当前为注册审核模式,请填写注册理由') + sessionStorage.setItem('github_code', code) + return 'NEED_REASON' + } else if (data.reason_code === 'IS_APPROVING') { + toast.info('您的注册理由正在审批中') + return true + } else { + toast.error(data.error || '登录失败') + } + } catch (e) { + toast.error('登录失败') + } + return false +} diff --git a/open-isle-cli/src/views/GithubCallbackPageView.vue b/open-isle-cli/src/views/GithubCallbackPageView.vue new file mode 100644 index 000000000..c8d285698 --- /dev/null +++ b/open-isle-cli/src/views/GithubCallbackPageView.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/open-isle-cli/src/views/LoginPageView.vue b/open-isle-cli/src/views/LoginPageView.vue index e78462850..f8bc84df0 100644 --- a/open-isle-cli/src/views/LoginPageView.vue +++ b/open-isle-cli/src/views/LoginPageView.vue @@ -34,6 +34,10 @@ Google Logo
Google 登录
+
+ GitHub Logo +
GitHub 登录
+
@@ -42,6 +46,7 @@ import { API_BASE_URL, toast } from '../main' import { setToken, loadCurrentUser } from '../utils/auth' import { googleSignIn } from '../utils/google' +import { githubAuthorize } from '../utils/github' import BaseInput from '../components/BaseInput.vue' export default { name: 'LoginPageView', @@ -89,6 +94,9 @@ export default { }, () => { this.$router.push('/signup-reason?google=1') }) + }, + loginWithGithub() { + githubAuthorize() } } } diff --git a/open-isle-cli/src/views/SignupPageView.vue b/open-isle-cli/src/views/SignupPageView.vue index f68bbc7ff..348217030 100644 --- a/open-isle-cli/src/views/SignupPageView.vue +++ b/open-isle-cli/src/views/SignupPageView.vue @@ -76,6 +76,10 @@ Google Logo
Google 注册
+
+ GitHub Logo +
GitHub 注册
+
@@ -83,6 +87,7 @@