Nic*_*lva 5 javascript token local-storage react-native
我认为这是一个简单的任务,存储我的令牌,设置一个计时器并在计时器到期时获取令牌,我错了,在观看并阅读了几篇有关如何解决此问题的文章后,我现在非常迷茫,我需要帮助存储我的令牌(或两者,用户名数据加令牌?不再确定),并在令牌过期时刷新。
是的,我在堆栈溢出中看到了很多与此相关的问题,但其中许多与特定问题有关,而不是如何去做。
我的应用程序通过来自 api(net core 2.0)的 microsoft graph 连接到 office 365。
在我的应用程序中,我使用此代码从 api 获取数据并传递我的用户名和密码参数
async ApiLogin(loginRequestObject: LoginRequest) {
var serviceResult = new ServiceResult();
await NetInfo.fetch().then(async state => {
var param = JSON.stringify(loginRequestObject);
if (state.isConnected) {
try {
await fetch(ServiceProperties.URLauthentication, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: param,
})
.then(response => {
return response.json();
})
.then(responseJson => {
if (JSON.stringify(responseJson) != null) {
serviceResult.Success = true;
serviceResult.Message = 'Service Authentication ok';
serviceResult.ResponseBody = responseJson;
serviceResult.StatusCode = 0;
} else {
serviceResult.Success = false;
serviceResult.Message = 'Service Authentication not ok';
serviceResult.ResponseBody = null;
serviceResult.StatusCode = -100;
}
});
} catch (error) {
serviceResult.Success = false;
serviceResult.Message = 'Service Authentication not ok';
serviceResult.ResponseBody = null;
serviceResult.StatusCode = -999;
}
} else {
serviceResult.Success = false;
serviceResult.Message = 'Service internet not ok';
serviceResult.ResponseBody = null;
serviceResult.StatusCode = -1;
}
});
console.log(JSON.parse(serviceResult.ResponseBody));
return serviceResult;
}
Run Code Online (Sandbox Code Playgroud)
结果是这样。
{"Username":"sensitive data","DisplayName":"sensitive data","GivenName":"sensitive data","SurName":"sensitive data","Email":"sensitive data","Token":"ZSI6Im42aGRfdDVGRHhrSzBEVklJUXpxV09DWHZ4dWc0RlhWVkI4ZVJ6dEFsWDAiLCJhbGciOiJSUzI1NiIsIng1dCI6IlNzWnNCTmhaY0YzUTlTNHRycFFCVEJ5TlJSSSIsImtpZCI6IlNzWnNCTmhaYm5ldC8zOTBmODU5NS1kZTFlLTRmNmQtYTk1NC0yNWY2N5MjkwMTYsImV4cCI6MTU5MjkzMjkxNiButVBqe3E3QwcBr1P0G_dWyC9ASQU0psGDPnsQPHp0T070ROZ_mcPitgquNfsO5JZ8-o056l_aePhXSMO7bHWmUBbVn7TA1UoYIz3lAoOzvE6juadve4aU3goeaBj8PIrhG0M2zEEfKgOL1Al9MSU1GGUmRW9dBofeA4e1cGmlGQrUKnt73n0sHap6","PhotoBase64":null}
Run Code Online (Sandbox Code Playgroud)
这几乎就是我得到的全部,目前,我在这个应用程序上使用了异步存储,但至少可以说只存储一个带有“无用”数据的对象,我不确定异步存储是否适合这与否,如果没有,我该怎么办?
编辑:阅读更多内容后,我发现我需要请求第二个令牌,来自 microsoft graph https://massivescale.com/microsoft-v2-endpoint-primer/的刷新令牌仍然需要有关如何存储数据的帮助并在过期时刷新令牌,
编辑 2:不幸的是,我既没有从 api 中获得刷新令牌,也没有获得 expiresAt 值
我无法帮助该特定身份验证提供程序(从未与 Office 365 合作过),但这是您需要遵循的一般步骤:
setTimeout以刷新这是我的 Reactjs 身份验证解决方案(不幸的是没有 RN 示例),它使用 JWT 根据我自己的 API 对客户端进行身份验证。这种情况下的访问令牌也是刷新令牌。我使用一种没有 Redux 的方法,只使用纯 React 和 JS。我希望这会帮助你。
import { useCallback, useState, useEffect } from "react";
import JWT from "jsonwebtoken";
import { ENV } from "../config";
import { useLanguageHeaders } from "./i18n";
const decodeToken = (token) =>
typeof token === "string" ? JWT.decode(token) : null;
//This class is responsible for authentication,
//refresh and global auth state parts
//I create only one instance of AuthProvider and export it,
//so it's kind of singleton
class AuthProvider {
//Getter for _authStatus
get authStatus() {
return this._authStatus;
}
constructor({ tokenEndpoint, refreshEndpoint, refreshLeeway = 60 }) {
this._tokenEndpoint = tokenEndpoint;
this._refreshEndpoint = refreshEndpoint;
this._refreshLeeway = refreshLeeway;
//When app is loaded, I load token from local storage
this._loadToken();
//And start refresh function that checks expiration time each second
//and updates token if it will be expired in refreshLeeway seconds
this._maybeRefresh();
}
//This method is called in login form
async authenticate(formData, headers = {}) {
//Making a request to my API
const response = await fetch(this._tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
...headers,
},
redirect: "follow",
body: JSON.stringify(formData),
});
const body = await response.json();
if (response.status === 200) {
//Authentication successful, persist token and update _authStatus
this._updateToken(body.token);
} else {
//Error happened, replace stored token (if any) with null
//and update _authStatus
this._updateToken(null);
throw new Error(body);
}
}
//This method signs user out by replacing token with null
unauthenticate() {
this._updateToken(null);
}
//This is needed so components and routes are able to
//react to changes in _authStatus
addStatusListener(listener) {
this._statusListeners.push(listener);
}
//Components need to unsubscribe from changes when they unmount
removeStatusListener(listener) {
this._statusListeners = this._statusListeners.filter(
(cb) => cb !== listener
);
}
_storageKey = "jwt";
_refreshLeeway = 60;
_tokenEndpoint = "";
_refreshEndpoint = "";
_refreshTimer = undefined;
//This field holds authentication status
_authStatus = {
isAuthenticated: null,
userId: null,
};
_statusListeners = [];
//This method checks if token refresh is needed, performs refresh
//and calls itself again in a second
async _maybeRefresh() {
clearTimeout(this._refreshTimer);
try {
const decodedToken = decodeToken(this._token);
if (decodedToken === null) {
//no token - no need to refresh
return;
}
//Note that in case of JWT expiration date is built-in in token
//itself, so I do not need to make requests to check expiration
//Otherwise you might want to store expiration date in _authStatus
//and localStorage
if (
decodedToken.exp * 1000 - new Date().valueOf() >
this._refreshLeeway * 1000
) {
//Refresh is not needed yet because token will not expire soon
return;
}
if (decodedToken.exp * 1000 <= new Date().valueOf()) {
//Somehow we have a token that is already expired
//Possible when user loads app after long absence
this._updateToken(null);
throw new Error("Token is expired");
}
//If we are not returned from try block earlier, it means
//we need to refresh token
//In my scenario access token itself is used to get new one
const response = await fetch(this._refreshEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
redirect: "follow",
body: JSON.stringify({ token: this._token }),
});
const body = await response.json();
if (response.status === 401) {
//Current token is bad, replace it with null and update _authStatus
this._updateToken(null);
throw new Error(body);
} else if (response.status === 200) {
//Got new token, replace existing one
this._updateToken(body.token);
} else {
//Network error, maybe? I don't care unless its 401 status code
throw new Error(body);
}
} catch (e) {
console.log("Something is wrong when trying to refresh token", e);
} finally {
//Finally block is executed even if try block has return statements
//That's why I use it to schedule next refresh try
this._refreshTimer = setTimeout(this._maybeRefresh.bind(this), 1000);
}
}
//This method persist token and updates _authStatus
_updateToken(token) {
this._token = token;
this._saveCurrentToken();
try {
const decodedToken = decodeToken(this._token);
if (decodedToken === null) {
//No token
this._authStatus = {
...this._authStatus,
isAuthenticated: false,
userId: null,
};
} else if (decodedToken.exp * 1000 <= new Date().valueOf()) {
//Token is expired
this._authStatus = {
...this._authStatus,
isAuthenticated: false,
userId: null,
};
} else {
//Token is fine
this._authStatus = {
...this._authStatus,
isAuthenticated: true,
userId: decodedToken.id,
};
}
} catch (e) {
//Token is so bad that can not be decoded (malformed)
this._token = null;
this._saveCurrentToken();
this._authStatus = {
...this._authStatus,
isAuthenticated: false,
userId: null,
};
throw e;
} finally {
//Notify subscribers that _authStatus is updated
this._statusListeners.forEach((listener) => listener(this._authStatus));
}
}
//Load previously persisted token (called in constructor)
_loadToken() {
this._updateToken(window.localStorage.getItem(this._storageKey));
}
//Persist token
_saveCurrentToken() {
if (typeof this._token === "string") {
window.localStorage.setItem(this._storageKey, this._token);
} else {
window.localStorage.removeItem(this._storageKey);
}
}
}
//Create authProvider instance
const authProvider = new AuthProvider(ENV.auth);
//This hook gives a component a function to authenticate user
export const useAuthenticate = () => {
const headers = useLanguageHeaders();
return useCallback(
async (formData) => {
await authProvider.authenticate(formData, headers);
},
[headers]
);
};
//This hook gives a function to unauthenticate
export const useUnauthenticate = () => {
return useCallback(() => authProvider.unauthenticate(), []);
};
//This hook allows components to get authentication status
//and react to changes
export const useAuthStatus = () => {
const [authStatus, setAuthStatus] = useState(authProvider.authStatus);
useEffect(() => {
authProvider.addStatusListener(setAuthStatus);
return () => {
authProvider.removeStatusListener(setAuthStatus);
};
}, []);
return authStatus;
};
Run Code Online (Sandbox Code Playgroud)
功能组件中的这行代码允许知道用户是否经过身份验证: const { isAuthenticated } = useAuthStatus();
| 归档时间: |
|
| 查看次数: |
4622 次 |
| 最近记录: |