From 502b73f69cbccfbdb023b2b00728790220d358de Mon Sep 17 00:00:00 2001 From: Feng_Qi Date: Sat, 15 Jul 2023 23:53:42 +0800 Subject: [PATCH] support client credentials & password mode --- front-standalone/src/api/playground.ts | 14 +- .../playground/components/Authorization.vue | 1 - .../views/playground/components/Client.vue | 337 ++++++++--------- .../views/playground/components/Password.vue | 343 ++++++++---------- .../src/views/playground/index.vue | 4 +- 5 files changed, 321 insertions(+), 378 deletions(-) diff --git a/front-standalone/src/api/playground.ts b/front-standalone/src/api/playground.ts index 3a7a404..3368c9f 100644 --- a/front-standalone/src/api/playground.ts +++ b/front-standalone/src/api/playground.ts @@ -11,6 +11,7 @@ type Result = { } } +/** authorization_code */ /** Step 2 */ /** Get access_token with code */ export const fetchACToken = (data) => { @@ -26,4 +27,15 @@ export const fetchRefreshToken = (data) => { /** Get data from API */ export const fetchApiData = (data) => { return http.post("/api", data); -} \ No newline at end of file +} + + +/** Client Credentials */ +export const fetchACTokenByClient = (data) => { + return http.post("/oauth2/client_credentials", data); +}; + +/** Password */ +export const fetchACTokenByPassword = (data) => { + return http.post("/oauth2/password", data); +}; \ No newline at end of file diff --git a/front-standalone/src/views/playground/components/Authorization.vue b/front-standalone/src/views/playground/components/Authorization.vue index 32fcf1b..5b9dd6d 100644 --- a/front-standalone/src/views/playground/components/Authorization.vue +++ b/front-standalone/src/views/playground/components/Authorization.vue @@ -56,7 +56,6 @@ const responseInfo = reactive({ }); const rawJsonInfo = reactive({}); const exampleInfo = reactive({}); -const isWrapReq = ref(true);//控制body是否自动换行 const isWrapRes = ref(true);//控制body是否自动换行 function updateReqAndRes() { diff --git a/front-standalone/src/views/playground/components/Client.vue b/front-standalone/src/views/playground/components/Client.vue index 7f74c2c..8c239b2 100644 --- a/front-standalone/src/views/playground/components/Client.vue +++ b/front-standalone/src/views/playground/components/Client.vue @@ -1,10 +1,8 @@ +} diff --git a/front-standalone/src/views/playground/components/Password.vue b/front-standalone/src/views/playground/components/Password.vue index 7f74c2c..1103edc 100644 --- a/front-standalone/src/views/playground/components/Password.vue +++ b/front-standalone/src/views/playground/components/Password.vue @@ -2,9 +2,8 @@ import { onMounted, reactive, ref, watch } from 'vue' import { ElMessage } from 'element-plus' import { generateRandomString } from "/@/utils/x"; -import { LocalStorageService } from "/@/utils/persistence" import useClipboard from 'vue-clipboard3'; -import { fetchACToken, fetchApiData, fetchRefreshToken } from "/@/api/playground"; +import { fetchACTokenByPassword, fetchApiData } from "/@/api/playground"; const props = defineProps({ cfgData: { @@ -24,104 +23,104 @@ const props = defineProps({ }, }); const {toClipboard} = useClipboard(); -const isHorizontal = ref(true); +const isHorizontal = ref(false); const toggleLayout = () => { isHorizontal.value = !isHorizontal.value; }; // Http Info -const requestInfo = reactive({}); -const responseInfo = reactive({}); -const oauthInfo = reactive({}); +const requestInfo = reactive({ + meta: { + method: '', + path: '', + proto: '', + host: '', + }, + header: '', + body: '', + code: '', +}); +const responseInfo = reactive({ + meta: { + method: '', + path: '', + proto: '', + host: '', + status: '', + }, + header: '', + strHeader: '', + body: '', + code: '', +}); +const rawJsonInfo = reactive({}); const exampleInfo = reactive({}); +const isWrapRes = ref(true);//控制body是否自动换行 + +function updateReqAndRes() { + requestInfo.body = atob(requestInfo.body); + requestInfo.code = JSON.stringify(requestInfo.header, undefined, 0.1) + //去掉首尾的{} + requestInfo.code = requestInfo.code.slice(1, requestInfo.code.length - 1); + requestInfo.code = requestInfo.meta.method + ' ' + requestInfo.meta.path + ' ' + requestInfo.meta.proto + requestInfo.code; + requestInfo.code = requestInfo.code + 'Host: ' + requestInfo.meta.host; + requestInfo.code = requestInfo.code.replaceAll('"', ''); + + // responseInfo.meta = res["data"]["response"]["meta"]; + // responseInfo.body = res["data"]["response"]["body"]; + // responseInfo.body = atob(responseInfo.body); + responseInfo.body = decodeURIComponent(escape(window.atob(responseInfo.body))) + // responseInfo.header = res["data"]["response"]["header"]; + responseInfo.strHeader = JSON.stringify(responseInfo.header, undefined, 0.1) + //去掉首尾的{} + responseInfo.strHeader = responseInfo.strHeader.slice(1, responseInfo.strHeader.length - 1) + responseInfo.strHeader = responseInfo.meta.proto + ' ' + responseInfo.meta.status + responseInfo.strHeader; + responseInfo.strHeader = responseInfo.strHeader.replaceAll('"', '') +} // Step 1 const activeName = ref('1'); -const s1Data = reactive({ - authorization_endpoint: "", - redirect_uri: window.location.href.split("?")[0], - scope: "", - response_type: "code", - state: "", -}); - -const initialAddress = ref(""); - -function handleS1Change() { - initialAddress.value = s1Data.authorization_endpoint.concat( - "?response_type=code", - s1Data.scope?.length > 0 ? "&scope=".concat(s1Data.scope) : "", - props.cfgData.client_id?.length > 0 ? "&client_id=".concat(props.cfgData.client_id) : "", - "&redirect_uri=", - s1Data.redirect_uri, - s1Data.state?.length > 0 ? "&state=".concat(s1Data.state) : "" - ); -} - -function handleGetAuthorizationCode() { - if(s1Data.scope.length === 0){ - ElMessage.error('scope不能为空'); - return; - }else if(s1Data.state.length === 0){ - ElMessage.error('state不能为空'); - return; - }else{ - if(props.cfgData.client_id.length === 0){ - ElMessage.error('client_id is empty, please click the config button on the right side, and check the configuration'); - return; - }else{ - const lss = new LocalStorageService(); - const ci = {key: "id", value: props.cfgData.client_id}; - const cs = {key: "secret", value: props.cfgData.client_secret}; - lss.addItem(ci); - if(cs.value.length > 0){ - lss.addItem(cs); - } - window.location.href = initialAddress.value; - } - } -} -// Step 2 -const code = ref(""); -const state = ref(""); +const username = ref(""); +const password =ref("") const currentToken = ref(""); -const currentRefreshToken = ref(""); -async function handleGetToken() { - if(code.value.length === 0){ - ElMessage.error('code不能为空'); +async function handleGetTokenByPassword() { + if(username.value.length === 0){ + ElMessage.error('username cannot be empty'); + return; + }else if(password.value.length === 0){ + ElMessage.error('password cannot be empty'); return; }else if(props.cfgData.client_id.length === 0){ - ElMessage.error('client_id不能为空'); + ElMessage.error('client_id cannot be empty'); return; }else if(props.cfgData.client_secret.length === 0){ - ElMessage.error('client_secret不能为空'); + ElMessage.error('client_secret cannot be empty'); return; }else{ const dataObject = { - code: code.value, client_id: props.cfgData.client_id, client_secret: props.cfgData.client_secret, - scope: props.cfgData.default_scope, - redirect_uri: window.location.href.split("?")[0] + username : username.value, + password : password.value }; - fetchACToken(dataObject).then(({code,msg,data}) => { - if(code===0){ - const {request, response, oauth2, example} = data; - const {access_token, refresh_token} = oauth2; - currentToken.value = access_token; - currentRefreshToken.value = refresh_token; - s3CurrentToken.value = access_token; + fetchACTokenByPassword(dataObject).then(({code, msg, data}) => { + if(code === 0){ + const {request, response, rawjson, example} = data; + const {access_token} = rawjson; + currentToken.value = access_token??"Uncertain"; + s3CurrentToken.value = access_token??"Uncertain"; toClipboard(access_token).finally(() => { - ElMessage.success(`已复制access_token: ${access_token}`); + ElMessage.success(`get access_token success: ${access_token}`); }); Object.assign(requestInfo, request); Object.assign(responseInfo, response); - Object.assign(oauthInfo, oauth2); + Object.assign(rawJsonInfo, rawjson); Object.assign(exampleInfo, example); window.history.replaceState({}, document.title, window.location.pathname); + updateReqAndRes(); }else{ ElMessage.error(msg); } @@ -130,44 +129,6 @@ async function handleGetToken() { } } -function handleRefreshToken() { - if(props.cfgData.client_id.length === 0){ - ElMessage.error('client_id is empty, please click the config button on the right side, and check the configuration'); - return; - }else if(props.cfgData.client_secret.length === 0){ - ElMessage.error('client_secret is empty, please click the config button on the right side, and check the configuration'); - return; - }else if(currentRefreshToken.value.length === 0){ - ElMessage.error('refresh_token is empty, please get the access_token firstly'); - return; - }else{ - const dataObject = { - refresh_token: currentRefreshToken.value, - client_id: props.cfgData.client_id, - client_secret: props.cfgData.client_secret - }; - - fetchRefreshToken(dataObject).then(({code,msg,data}) => { - if(code === 0){ - const {request, response, oauth2, example} = data; - const {access_token, refresh_token} = oauth2; - currentToken.value = access_token; - currentRefreshToken.value = refresh_token; - s3CurrentToken.value = access_token; - toClipboard(access_token).finally(() => { - ElMessage.success(`已复制新access_token: ${access_token}`); - }); - Object.assign(requestInfo, request); - Object.assign(responseInfo, response); - Object.assign(oauthInfo, oauth2); - Object.assign(exampleInfo, example); - }else{ - ElMessage.error(msg); - return; - } - }); - } -} // Step 3 const requestUri = ref(""); @@ -192,10 +153,10 @@ function handleMethodChange(value) { async function handleRequestAPI() { if(requestUri.value.length === 0){ - ElMessage.error('请求地址不能为空'); + ElMessage.error('api address cannot be empty'); return; }else if(s3CurrentToken.value.length === 0){ - ElMessage.error('access_token不能为空'); + ElMessage.error('access_token is empty, please get the access_token firstly'); return; }else{ const dataObject = { @@ -206,13 +167,14 @@ async function handleRequestAPI() { header: {}, http_body: "" }; - fetchApiData(dataObject).then(({code,msg,data}) => { + fetchApiData(dataObject).then(({code, msg, data}) => { if(code === 0){ - const {request, response, oauth2, example} = data; + const {request, response, rawjson, example} = data; Object.assign(requestInfo, request); Object.assign(responseInfo, response); - Object.assign(oauthInfo, oauth2); + Object.assign(rawJsonInfo, rawjson); Object.assign(exampleInfo, example); + updateReqAndRes(); }else{ ElMessage.error(msg); return; @@ -222,89 +184,102 @@ async function handleRequestAPI() { } watch(props.cfgData, (newValue) => { - s1Data.authorization_endpoint = newValue.authorization_endpoint; - s1Data.scope = newValue.default_scope; - initialAddress.value = newValue.authorization_endpoint.concat( - "?response_type=code", - newValue.default_scope?.length > 0 ? "&scope=".concat(newValue.default_scope) : "", - newValue.client_id?.length > 0 ? "&client_id=".concat(newValue.client_id) : "", - "&redirect_uri=", - s1Data.redirect_uri, - s1Data.state?.length > 0 ? "&state=".concat(s1Data.state) : "" - ); requestUri.value = newValue.userinfo_endpoint; s3TokenType.value = newValue.access_token_type; }); onMounted(() => { - // handleDrag(agS1Ref.value.$el, agS1ContainerRef.value.$el); - s1Data.state = generateRandomString(props.cfgData.default_scope); - - const urlParams = new URLSearchParams(window.location.search); - if(urlParams.get('code')?.length > 0){ - code.value = urlParams.get('code'); - state.value = urlParams.get('state'); - activeName.value = '2'; - // window.history.replaceState({}, document.title, window.location.pathname); - } requestUri.value = props.cfgData.userinfo_endpoint; s3TokenType.value = props.cfgData.access_token_type; }); - -const agS1ContainerRef = ref(null); -const agS1Ref = ref(null); -const handleDrag = (floatButton, container) => { - // todo: 浮动球拖动效果考虑的因素比较多,bug点也比较多,后期有时间看看能否完善 - // todo: 1. 需要同时考虑手机和pc页面不同的场景元素移动计算有所区别 - // todo: 2. 拖动过程中,浮动球的位置需要跟随鼠标移动,但是浮动球的位置需要相对在容器内,就需要做边缘检测 - // todo: 3. 拖动的整个周期需要阻止单击事件流 - // todo: 4. 拖动刚开始需要检测用户是否是真的想要拖动,可以检测拖动的时间或距离来综合判断 - // todo: 5. pc端还要考虑浏览器的的选择事件 - // todo: 6. 边缘吸附的时候需要考虑浮动球的位置,是吸附在容器的边缘还是容器内的边缘 - - let isDragging = false; - let mouseOffset = {x: 0, y: 0}; - let touchOffset = {x: 0, y: 0}; - - floatButton.addEventListener('mousedown', function (event) { - - }); - - floatButton.addEventListener('touchstart', function (event) { - - }); - - document.addEventListener('mousemove', function (event) { - if(isDragging){ - - }else{ - } - }); - - document.addEventListener('touchmove', function (event) { - if(isDragging){ - - }else{ - } - }); - - document.addEventListener('mouseup', function () { - isDragging = false; - }); - - document.addEventListener('touchend', function () { - isDragging = false; - }); -}; - +} diff --git a/front-standalone/src/views/playground/index.vue b/front-standalone/src/views/playground/index.vue index 329f8a3..84866a1 100644 --- a/front-standalone/src/views/playground/index.vue +++ b/front-standalone/src/views/playground/index.vue @@ -68,8 +68,8 @@ onMounted(() => { Authorization Code - - + Resource Owner Password Credentials + Client Credentials