Javascript React - 处理登录和身份验证的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/49819183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
React - What is the best way to handle login and authentication?
提问by Vincent Nguyen
New to react and working on an application with authentication/logging in. It currently works but feels hacked together. Right now I have my isAuthenticatedstate located in my routes.jslike so:
刚开始使用身份验证/登录来响应和处理应用程序。它目前可以工作,但感觉被黑客攻击了。现在我的isAuthenticated状态位于我的状态routes.js:
class Routes extends Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: false,
}
}
On my login page, I need to know when a user is authenticated to redirect them to the homepage. What is the best design pattern to allow access and manipulation of this isAuthenticatedstate? How I currently have it set up is I have a function that sets the state inside the routes.jsand sends the state as a prop like so:
在我的登录页面上,我需要知道用户何时通过身份验证以将他们重定向到该home页面。允许访问和操作此isAuthenticated状态的最佳设计模式是什么?我目前如何设置它是我有一个函数,可以在里面设置状态routes.js并将状态作为道具发送,如下所示:
setAuthenticated = (isAuthenticated) => {
this.setState({isAuthenticated});
}
and down below in the router...
并在路由器下方...
<Route path="/" exact component={() =>
<div>
<Login
isAuthenticated={this.state.isAuthenticated}
setAuthenticated={this.setAuthenticated}
</div>
} />
Yes, I understand this is bad design because this is changing props values which are supposed to be immutable. This is also bad because when I change this value in my login.jsit causes multiple unnecessary re-renders. Should I be declaring isAuthenticatedas some type of global variable? I am not using any state management by the way.
是的,我知道这是一个糟糕的设计,因为这改变了本来应该是不可变的 props 值。这也很糟糕,因为当我更改此值时,login.js它会导致多次不必要的重新渲染。我应该声明isAuthenticated为某种类型的全局变量吗?顺便说一下,我没有使用任何状态管理。
Edit: I am setting isAuthenticatedbased on a response from my server which confirms correct login/password combination.
编辑:我isAuthenticated根据服务器的响应进行设置,确认正确的登录名/密码组合。
回答by GG.
Handling isAuthenticatedonly in the statemeans the user will be unauthenticated every time he refreshes the page. That's not really user-friendly! :)
isAuthenticated仅以state用户每次刷新页面时未经身份验证的方式进行处理。这不是真正的用户友好!:)
So instead, the Login page should store an access_token(coming from your backend) in the cookiesor localStorageof the browser. An access_tokenproves the user is authenticated and also verifies his identity. You will usually pass this access_tokento every next requests to your server, to check if this user is allowed to access the data he's requesting, or allowed to create, edit and delete the things he's trying to create, edit and delete.
因此,登录页面应该在浏览器的or中存储一个access_token(来自您的后端)。An证明用户已通过身份验证并验证他的身份。您通常会将其传递给服务器的每个下一个请求,以检查是否允许此用户访问他请求的数据,或者是否允许创建、编辑和删除他尝试创建、编辑和删除的内容。cookieslocalStorageaccess_tokenaccess_token
Then you can check this access_tokenon every other pages as well and redirect the user to the Login page if he's not authenticated anymore.
然后,您也可以access_token在所有其他页面上检查这一点,如果用户不再通过身份验证,则将用户重定向到登录页面。
A brief aside on the difference between access_tokenand refresh_token– this will help you understand the code bellow, but feel free to skip ahead if you are already familiar with it.
简要介绍access_token和之间的区别refresh_token-这将帮助您理解下面的代码,但如果您已经熟悉它,请随时跳过。
Your backend probably uses OAuth2, which is the most common authentication protocol nowadays. With OAuth2, your app makes a first request to the server containing the username and password of the user to authenticate. Once the user is authenticated, he receives 1) an access_token, which usually expires after an hour, and 2) a refresh_token, which expires after a very long time (hours, days). When the access_tokenexpires, instead of asking the user for his username and password again, your app sends the refresh_tokento the server to obtain a new access_tokenfor this user.
您的后端可能使用OAuth2,这是当今最常见的身份验证协议。使用OAuth2,您的应用程序向服务器发出第一个请求,其中包含用户的用户名和密码以进行身份验证。用户通过身份验证后,他会收到 1) an access_token,通常在一个小时后过期,以及 2) a refresh_token,它会在很长时间(几小时、几天)后过期。当access_token过期时,您的应用程序refresh_token不会再次询问用户他的用户名和密码,而是将 发送到服务器以获取access_token该用户的新用户名。
A brief aside on the differences between cookiesand localStorage– feel free to skip it too!
简要介绍cookies和之间的区别localStorage- 也可以跳过它!
localStorageis the most recent technology between both. It's a simple key/value persistence system, which seems perfect to store the access_tokenand its value. But we also need to persist its date of expiration. We could store a second key/value pair named expiresbut it would be more logic to handle on our side.
localStorage是两者之间的最新技术。这是一个简单的键/值持久性系统,它似乎非常适合存储access_token和它的值。但是我们也需要坚持它的过期日期。我们可以存储命名的第二个键/值对,expires但在我们这边处理会更具逻辑性。
On the other hand, cookieshave a native expiresproperty, which is exactly what we need! cookiesare an old technology and are not very developer-friendly, so I personally use js-cookie, which is a small library to manipulate cookies. It makes it look like a simple key/value persistence system too: Cookies.set('access_token', value)then Cookies.get('access_token').
另一方面,cookies拥有一个原生expires属性,这正是我们所需要的!cookies是一项老技术,对开发人员不是很友好,所以我个人使用js-cookie,这是一个小型库来操作cookies。它也使它看起来像一个简单的键/值持久化系统:Cookies.set('access_token', value)then Cookies.get('access_token').
Other pro for the cookies: they are cross subdomains! If your Login app is login.mycompany.comand your Main app is app.mycompany.com, then you can create a cookieon the Login app and access it from the Main app. This is not possible with LocalStorage.
其他专业人士cookies:它们是跨子域!如果您的登录应用程序是login.mycompany.com并且您的主应用程序是app.mycompany.com,那么您可以cookie在登录应用程序上创建一个并从主应用程序访问它。这是不可能的LocalStorage。
Here are some of the methods and special React components I use for authentication:
以下是我用于身份验证的一些方法和特殊的 React 组件:
isAuthenticated()
isAuthenticated()
import Cookies from 'js-cookie'
export const getAccessToken = () => Cookies.get('access_token')
export const getRefreshToken = () => Cookies.get('refresh_token')
export const isAuthenticated = () => !!getAccessToken()
authenticate()
认证()
export const authenticate = async () => {
if (getRefreshToken()) {
try {
const tokens = await refreshTokens() // call an API, returns tokens
const expires = (tokens.expires_in || 60 * 60) * 1000
const inOneHour = new Date(new Date().getTime() + expires)
// you will have the exact same setters in your Login page/app too
Cookies.set('access_token', tokens.access_token, { expires: inOneHour })
Cookies.set('refresh_token', tokens.refresh_token)
return true
} catch (error) {
redirectToLogin()
return false
}
}
redirectToLogin()
return false
}
redirectToLogin()
重定向登录()
const redirectToLogin = () => {
window.location.replace(
`${getConfig().LOGIN_URL}?next=${window.location.href}`
)
// or history.push('/login') if your Login page is inside the same app
}
AuthenticatedRoute
认证路由
export const AuthenticatedRoute = ({
component: Component,
exact,
path,
}) => (
<Route
exact={exact}
path={path}
render={props =>
isAuthenticated() ? (
<Component {...props} />
) : (
<AuthenticateBeforeRender render={() => <Component {...props} />} />
)
}
/>
)
AuthenticateBeforeRender
在渲染前进行身份验证
class AuthenticateBeforeRender extends Component {
state = {
isAuthenticated: false,
}
componentDidMount() {
authenticate().then(isAuthenticated => {
this.setState({ isAuthenticated })
})
}
render() {
return this.state.isAuthenticated ? this.props.render() : null
}
}
回答by Agney
If you are using an application where the authentication lasts only for one session, storing it in state is enough. But do note that this means, the user will lose the authenticated status on page refresh.
如果您使用的应用程序的身份验证仅持续一个会话,则将其存储在状态中就足够了。但请注意,这意味着用户将在页面刷新时失去已验证状态。
Here is an example using React Context, where we create context using createContextand use Consumerto access it across the application.
这是一个使用 React Context 的示例,我们在其中创建上下文 usingcreateContext并用于Consumer跨应用程序访问它。
const AuthenticationContext = React.createContext();
const { Provider, Consumer } = AuthenticationContext;
function Login(props) {
return (
<Consumer>
{
value=>
<button onClick={value.login}>Login</button>
}
</Consumer>
);
}
function Logout() {
return (
<Consumer>
{
value=>
<button onClick={value.logout}>Logout</button>
}
</Consumer>
);
}
function AnotherComponent() {
return (
<Consumer>
{
value=>{
return value.isAuthenticated?
<p>Logged in</p>:
<p>Not Logged in</p>
}
}
</Consumer>
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.login = ()=> {
this.setState({
isAuthenticated: true
});
}
this.logout = ()=> {
this.setState({
isAuthenticated: false
});
}
this.state = {
isAuthenticated: false,
login: this.login,
logout: this.logout
}
}
render() {
return (
<Provider value={this.state}>
<Login />
<Logout />
<AnotherComponent />
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
回答by Waweru Mwaura
you can set the access token in the local storage on login and clear it after the user logs out. the is authenticated method will then be used to check if there is a token and whether the token is valid while making an API call
您可以在登录时在本地存储中设置访问令牌,并在用户注销后清除它。然后将使用经过身份验证的方法来检查是否有令牌以及在进行 API 调用时令牌是否有效

