天河低价网站建设,哪里有零基础网站建设教学公司,茶叶企业网站开发源码,拖拽做网站实现过程
去第三方平台拿到client-id和client-secret#xff0c;并配置一个能够外网访问回调地址redirect-uri供第三方服务回调搭建后端服务#xff0c;引入justauth-spring-boot-starter直接在配置文件中定义好第一步的三个参数#xff0c;并提供获取登录页面的接口和回调…实现过程
去第三方平台拿到client-id和client-secret并配置一个能够外网访问回调地址redirect-uri供第三方服务回调搭建后端服务引入justauth-spring-boot-starter直接在配置文件中定义好第一步的三个参数并提供获取登录页面的接口和回调接口前端项目中新建一个登录窗口和一个登录中转页面登录窗口的url从第二步第一个接口获取中转页面从第二步的第二个接口返回中转页面从url中读取登录成功的用户信息并存放到pinia中,关闭登录窗口并刷新主窗口
1必要信息获取
第三方平台的client-id和client-secret一般注册开发者平台都能获取。 回调地址需要外网可以使用花生壳内网穿透随便搞一个映射到本地的后台服务端口当后天服务启动成功后确保连接成功 前端代理也可以直接代理到这个域名前后端完全分离
2后台服务搭建
2.1 后台如果使用springboot2.x可以从开源框架直接使用
https://gitee.com/justauth/justauth-spring-boot-starter 只需将上一步获取的三个参数配置到yml文件中
2.2 AuthRequestFactory错误
如果使用的springboot3.x可能会报错提示 ‘com.xkcoding.justauth.AuthRequestFactory’ that could not be found. 只需要将AuthRequestFactory、JustAuthProperties、AuthStateRedisCache从源码复制一份到项目中补全Configuration、Component然后补上一个Bean即可 Beanpublic AuthRequestFactory getAuthRequest(JustAuthProperties properties, AuthStateRedisCache authStateCache) {return new AuthRequestFactory(properties,authStateCache);}2.3 redis错误
justauth-spring-boot-starter项目中的redis配置是springboot2.x的配置 如果是3.x的项目需要将 spring:reids改为 spring:data:reids
2.4 代码案例
import com.alibaba.fastjson.JSONObject;
import io.geekidea.springboot.cache.AuthRequestFactory;
import io.geekidea.springboot.service.UserService;
import io.geekidea.springboot.vo.ResponseResult;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import io.geekidea.springboot.cache.JustAuthProperties;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthBaiduRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** Description https://blog.csdn.net/weixin_46684099/article/details/118297276* Date 2024/10/23 16:30* Author 余乐**/
Slf4j
Controller
RequestMapping(/oauth)
RequiredArgsConstructor(onConstructor_ Autowired)
public class JustAuthController {private final UserService userService;private final AuthRequestFactory factory;private final JustAuthProperties properties;GetMappingpublic ListString list() {return factory.oauthList();}RequestMapping(/render/{source})ResponseBodypublic ResponseResult renderAuth(PathVariable(source) String source) {AuthRequest authRequest null;//特定平台需要自定义参数的可以单独写AuthConfigif (baidu.equals(source)) {//百度账号默认只有basic需要网盘权限需要单独定义ListString list new ArrayList();list.add(basic);list.add(netdisk);MapString,AuthConfig configMap properties.getType();AuthConfig authConfig configMap.get(BAIDU);authConfig.setScopes(list);authRequest new AuthBaiduRequest(authConfig);} else {//其他平台账号登录authRequest factory.get(source);}String state AuthStateUtils.createState();String authorizeUrl authRequest.authorize(state);return ResponseResult.success(authorizeUrl);}/*** oauth平台中配置的授权回调地址以本项目为例在创建github授权应用时的回调地址应为http://127.0.0.1:8444/oauth/callback/github*/RequestMapping(/callback/{source})public void login(PathVariable(source) String source, AuthCallback callback, HttpServletResponse response2) throws IOException {log.info(进入callback{}callback params{}, source, JSONObject.toJSONString(callback));AuthRequest authRequest null;//特定平台需要自定义参数的可以单独写AuthConfigif (baidu.equals(source)) {//百度账号默认只有basic需要网盘权限需要单独定义ListString list new ArrayList();list.add(basic);list.add(netdisk);MapString,AuthConfig configMap properties.getType();AuthConfig authConfig configMap.get(BAIDU);authConfig.setScopes(list);authRequest new AuthBaiduRequest(authConfig);} else {//其他平台账号登录authRequest factory.get(source);}AuthResponseAuthUser response authRequest.login(callback);String userInfo JSONObject.toJSONString(response.getData());log.info(回调用户信息:{}, userInfo);if (response.ok()) {userService.save(response.getData());String userInfoParam URLEncoder.encode(userInfo, UTF-8);//将用户信息放到中转页面的路由参数中前端从路由参数获取登陆结果response2.sendRedirect(http://localhost:5173/loginback?data userInfoParam);}}/*** 注销登录 前端需要同步清理用户缓存** param source* param uuid* return* throws IOException*/RequestMapping(/revoke/{source}/{uuid})ResponseBodypublic ResponseResult revokeAuth(PathVariable(source) String source, PathVariable(uuid) String uuid) throws IOException {AuthRequest authRequest factory.get(source.toLowerCase());AuthUser user userService.getByUuid(uuid);if (null user) {return ResponseResult.fail(用户不存在);}AuthResponseAuthToken response null;try {response authRequest.revoke(user.getToken());if (response.ok()) {userService.remove(user.getUuid());return ResponseResult.success(用户 [ user.getUsername() ] 的 授权状态 已收回);}return ResponseResult.fail(用户 [ user.getUsername() ] 的 授权状态 收回失败 response.getMsg());} catch (AuthException e) {return ResponseResult.fail(e.getErrorMsg());}}/*** 刷新token** param source* param uuid* return*/RequestMapping(/refresh/{source}/{uuid})ResponseBodypublic ResponseResultString refreshAuth(PathVariable(source) String source, PathVariable(uuid) String uuid) {AuthRequest authRequest factory.get(source.toLowerCase());AuthUser user userService.getByUuid(uuid);if (null user) {return ResponseResult.fail(用户不存在);}AuthResponseAuthToken response null;try {response authRequest.refresh(user.getToken());if (response.ok()) {user.setToken(response.getData());userService.save(user);return ResponseResult.success(用户 [ user.getUsername() ] 的 access token 已刷新新的 accessToken: response.getData().getAccessToken());}return ResponseResult.fail(用户 [ user.getUsername() ] 的 access token 刷新失败 response.getMsg());} catch (AuthException e) {return ResponseResult.fail(e.getErrorMsg());}}
}3 新建登录窗口和中转页面
3.1 在src/main/index.ts中新增登录窗口
let loginWindow//监听打开登录窗口的事件
ipcMain.on(openLoginWin, (event, url) {console.log(打开登录窗口, url)createLoginWindow(url)
})// 创建登录窗口
function createLoginWindow(url: string) {loginWindow new BrowserWindow({width: 800,height: 600,frame: false,titleBarStyle: hidden, autoHideMenuBar: true,parent: mainWindow, //父窗口为主窗口modal: true,show: false,webPreferences: {preload: join(__dirname, ../preload/index.js),nodeIntegration: true,contextIsolation: true}})// 加载登录 URLloginWindow.loadURL(url)loginWindow.on(ready-to-show, () {loginWindow.show()})
}// 关闭登录窗口并刷新主窗口
ipcMain.handle(close-login, () {if (loginWindow) {loginWindow.close()}if (mainWindow) {mainWindow.reload() // 刷新主窗口 }}
})3.2 新增中转页面并配置路由
/views/setting/LoginBack.vue
templateel-row justifycentercl-col :span17h2登陆结果/h2el-icon stylecolor:#00d28c;font-size: 50pxi-mdi-check-circle //el-icon/cl-col/el-row
/templatescript setup langts
import { useRoute } from vue-router
import { onMounted } from vue
import { useThemeStore } from /store/themeStoreconst route useRoute()
const data route.query.data
const themeStore useThemeStore()
//登陆成功自动关闭窗口
onMounted(() {console.log(登陆结果,data)themeStore.setCurrentUser(JSON.parse(data))setTimeout(() {//关闭当前登录回调的窗口并且刷新主窗口页面window.electron.ipcRenderer.invoke(close-login)}, 1000)
})
/script
3.3 新增路由
{path: loginback, component: ()import(/views/setting/LoginBack.vue),},这里的路由对应的就是后台/callback 接口重定向的地址
4.管理用户登录信息
后端用户登录信息保存在redis中如果过期可以使用客户端中缓存的用户uuid刷新token
前端的一般是使用pinia做持久化维护安装piniad 插件 pinia-plugin-persistedstate 新增用户themeStore.ts
import { defineStore } from pinia;export const useThemeStore defineStore(userInfoStore, {state: () {// 从 localStorage 获取主题如果没有则使用默认值//const localTheme localStorage.getItem(localTheme) || cool-black;return {currentTheme: cool-black,userInfo: {}};},actions: {setCurrentThemeId(theme: string) {console.log(修改主题, theme);this.currentTheme theme; // 更新当前主题document.body.setAttribute(data-theme, theme); // 更新 data-theme},setCurrentUser(user: any) {console.log(修改账号, user);this.userInfo user; // 更新当前账号},},//开启持久化 》 localStoragepersist: {key: userInfoStore,onstorage: localStorage,path: [currentTheme,userInfo]}
});
5. 运行调试
5.1 在顶部登录页面
div v-ifuserInfo.avatarel-avatar :srcuserInfo.avatar :size30/el-popover :width300 triggerclicktemplate #referencep{{userInfo.nickname}}/p/templatetemplate #defaultdiv classdemo-rich-conent styledisplay: flex; gap: 16px; flex-direction: columnel-avatar:size60srchttps://avatars.githubusercontent.com/u/72015883?v4stylemargin-bottom: 8px/el-divider /h5 clicklogout(userInfo.uuid)退出登录/h5/div/template/el-popover/divdiv v-else click.stopopenLoginCardel-avatar :iconUserFilled :size30/p未登录/p/divscript langts setup
import {ref} from vue
import { LoginOut } from /api/baidu
import {useThemeStore} from /store/themeStore;
import { UserFilled } from element-plus/icons-vue
import { useRouter } from vue-router
import { getLoginPageUrl } from ../../api/baidu
const themeStore useThemeStore();
const router useRouter()
let searchVal ref()
let userInforef({})if (themeStore.userInfo){userInfo.value themeStore.userInfo
}
//打开登录弹窗
function openLoginCard(){getLoginPageUrl().then(resp {console.log(获取登陆地址,resp.data)window.electron.ipcRenderer.send(openLoginWin,resp.data.data)});
}
//退出登录
function logout(uuid:string){LoginOut(uuid).then(resp {console.log(注销登录,resp.data)themeStore.setCurrentUser({})window.location.reload()});
}
/script