未完成-React V18 实现登录访问控制

发布时间 2023-07-05 18:20:07作者: 希望能摸鱼的凛耶酱

项目采用前后端分离进行设计,实现token登录验证
该示例只展示React Login Model 设计思路
前端:React 18 , Router V6; 后端(服务端) Springboot 2.4.5

React版本(可从package.json中查看)

"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.12.1",

目的:
对项目进行登录访问控制,即未进行登录的用户不可访问登录可见页面。
未登录时,页面会自动跳转到登录页面,进行登录。

我们需要:

  • 通过【路由】方式访问,需要从路由角度进行访问控制
  • 创建 Login 组件,结合路由控制,来判断是否登录 : 1 未登录实现自动跳转到登录页面 2 登录后可跳转到访问页面

从实现一个简单的表单请求开始

简单页面展示效果

(注:这里无用户注册步骤,只有输入用户名密码后进行登录的功能)

登录验证通常会使用表单发送请求的方式

参考展示示例,如下为简单的html表单请求代码,通过点击button组件,上传输入的数据

<form method="post" action="/post">
    <label for="name">User name</label>
    <input id="name" name="name" />
    <label for="password">Password</label>
    <input id="password" name="password" />
    <button>Login</button>
</form>

鉴于以上方式会刷新浏览器,并经历时代发展已被新方法Ajax替代,改用JavaScript编写代码提交请求

const form = document.querySelector('form');
form.addEventListener('submit', handleSubmit);

function handleSubmit(event) {
  const form = event.currentTarget;
  fetch(form.action, {
    method: form.method,
    body: new FormData(form)
  });
  event.preventDefault();
}
  • 通过获取表单元素,监听submit事件,点击提交按钮使用fetch() 发送接口请求,实现服务端的数据发送。
  • event.preventDefault()可以阻止浏览器的刷新,这就是典型的异步请求,可以使浏览器在不刷新的情况下,完成交互。

更进一步:Fetch API的使用

倘若我们快速点击Login按钮,代码中未设置阻止请求方法,从而会产生大量接口请求,导致服务器过载。
我们需要新的解决方法----在发送请求时,中止上一次请求。而Fetch恰好可以做到。

Fetch是下一代Ajax技术,通过Promise方式来处理数据,具有更简洁明了的API.
从MDN Web Docs 社区 使用Fetch的wiki 可以了解到:

  • Fetch API 提供了一个JavaScript接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。
  • 它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

一个基本的 fetch 请求设置如下:

fetch(URL)
  .then(response => response.json())
  .then(data => console.log(data));
  • Fetch函数通过网络(URL)获取所需数据(返回值可以是Json,HashMap等,方法中展示的是获取JSON文件), 并将其(data)打印到控制台。
  • 代码中'response'只是一个 HTTP 响应,而不是真的JSON。为了获取JSON的内容,我们需要使用response.json() 方法(该方法返回一个将响应 body 解析成JSON格式的Promise对象),再通过.then()函数,获取Pormise对象中的JSON对象

接下来我们可以更近一步,参考fetch(),上传Json、上传多文件、发送带凭证等请求。以下为示例

//Example POST method implementation:
async function postData(url = '', data = {}) {
  // Default options are marked with *
  const response = await fetch(url, {
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, *cors, same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
      'Content-Type': 'application/json'
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    redirect: 'follow', // manual, *follow, error
    referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: JSON.stringify(data) // body data type must match "Content-Type" header
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://example.com/answer', { answer: 42 })
  .then(data => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

  • 该示例涉及 async/await 语法,postData()是一个异步函数,因为被标记为async关键字
  • await fetch('url')开始一个HTTP请求到urlURL,因为await关键字的存在,异步函数被暂停,直到请求完成
  • 当请求完成过后,获取response,response.json().then(data)获取response中的Json文件数据

登录受控组件设计

现在我们可以开始着手创建我们自己的Login组件

一个简单的Login组件示例

function Login(){
    return(
        <div>This is Login page</div>
    );
}
export default Login;

我们使用函数定义了名为Login的组件,在页面上显示返回值“This is Login page”
结合上述我们了解到的方法,我们可以开始构造复合组件

还是引用之前的页面展示:

登录操作:输入框中输入用户的Username, Password,点击Login按钮进行登录

我们设置<Input>元素默认值为"",文本输入的操作会更改Input的值
可借助变更<Input>元素的值,触发HTMLchange事件,从而达到值的存储及后续函数调用

由于我们需要处理多个input的元素的,可参考以下方法写多个input的触发change事件
React handleChange() function explained - handle single/ multiple inputs

  const [users, setUser] = useState({
    username: '',
    password: '',
  });
  const handleChange = (event) => {
    const { name, value } = event.target
    setUser({ ...users, [name]: value })
  }

  return (<div>
          <Form>
            <FormGroup>
              <Label for="username">User Name</Label>
              <Input type="text" name="username" id="username" value={users.username || ''}
                    onChange={handleChange} />
            </FormGroup>
            <FormGroup>
              <Label for="password">Password</Label>
              <Input type="password" name="password" id="password" value={users.password || ''}
                    onChange={handleChange} />
            </FormGroup>
          </Form>
    </div>
  )