blog/毕业论文/毕业论文-4.md

505 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 毕业设计(4)
tags:
- java
- ssm
- react
- ant design
- typescript
categories: 毕业设计
date: 2022-12-14 12:40:32
summary: 登录实现
---
## 十、管理登录逻辑后端实现
    管理员账号的账号密码从配置文件中读写
```java
@RestController
@PropertySource("classpath:config/config.properties")
@RequestMapping("/admin")
public class AdminController {
@Value("${admin.username}")
String username;
@Value("${admin.password}")
String password;
@PostMapping("/login")
public ResponseEntity<Resp<HashMap<String, String>>> login(@RequestBody Map<String,String> params){
String user = params.get("phone");
String pass = params.get("password");
HashMap<String, String> map = new HashMap<>();
if (user.equals(username) && pass.equals(password)){
map.put("phone",username);
map.put("password",password);
map.put("type","admin");
String token = Jwt.sign(map);
map.remove("password");
map.put("token",token);
return ResponseEntity.ok(Resp.Ok(map));
}
return ResponseEntity.status(HttpStatus.PAYMENT_REQUIRED).body(Resp.Err(403,null,map));
}
}
```
**config.propertie** 文件中中读取管理员的账号密码然后配置登录的controller类首先判断账号密码是否正确正确就调用 **Jwt** 生成登录token,返回token到前端。
鉴权拦截器
在spring mvc的配置文件中配置拦截器配置除了登陆地址以外全部拦截
```xml
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 配置三个登陆地址不进行鉴权 -->
<mvc:exclude-mapping path="/admin/login"/>
<mvc:exclude-mapping path="/teacher/login"/>
<mvc:exclude-mapping path="/student/login"/>
<bean class="org.gjs.interceptor.AuthInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
```
拦截器类
```java
@PropertySource("classpath:config/config.properties")
public class AuthInterceptor implements HandlerInterceptor {
@Value("${admin.username}")
String username;
@Value("${admin.password}")
String password;
@Resource
StudentService studentService;
@Resource
TeacherService teacherService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authorization = request.getHeader("Authorization");
if (authorization == null || authorization.equals("")){
response.sendError(401,"the auth fail!");
return false;
}else {
HashMap<String,Object> map = Jwt.verify(authorization.split(" ")[1], HashMap.class);
if (map==null){
throw new MyException("未登录!");
}
request.getSession().setAttribute("login_type",map.get("type"));
Object type = map.get("type");
if ("admin".equals(type)) {
if (map.get("phone").equals(username) && map.get("password").equals(password)) {
return true;
} else {
throw new MyException("未登录!");
}
} else if ("student".equals(type)) {
Student student = new Student();
student.setPhone((String) map.get("phone"));
student.setId((Integer) map.get("id"));
Page<Student> students = studentService.queryByPage(student, PageRequest.of(0, 100));
return students.getContent().size() >= 1;
} else if ("teacher".equals(type)) {
Teacher teacher = new Teacher();
teacher.setPhone((String) map.get("phone"));
teacher.setId((Integer) map.get("id"));
Page<Teacher> teachers = teacherService.queryByPage(teacher, PageRequest.of(0, 100));
return teachers.getContent().size() >= 1;
}
throw new Exception("权限检测错误!");
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
```
调用jwt解析前端携带的token,检测账号密码是否正确,如果正确则返回 **true**,否则抛出异常错误。
## 十一、前端基本配置
项目前端采用 **react+ant design**编写,语言使用 **typescript** ,开发工具使用 **webstorm**
首先使用webstorm创建react项目使用typescript模板然后添加依赖
### 添加依赖
```shell
yarn add antd
yarn add axios
yarn add react-router-dom
yarn add http-proxy-middleware -D
```
因为在项目开发过程中,需要解决跨域的问题,所以我们使用开发依赖 **http-proxy-middleware**,将前端的接口转发到本地开发地址。
### 解决本地调试跨域
**setupProxy.js**
```javascript
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use("/api",createProxyMiddleware({
target: "http://127.0.0.1:8080",
changeOrigin: true,
pathRewrite:{
"/api":"/"
}
}))
}
```
配置项目前端路由在index.tsx中添加 **HashRouter**节点
### 配置根路由
**index.tsx**
```typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {HashRouter} from "react-router-dom";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<HashRouter>
<App />
</HashRouter>
</React.StrictMode>
);
reportWebVitals();
```
在App.tsx中添加基本页面因为使用类组件开发类组件中无法使用hooks等回调所以将App组件使用WithRouter进行包装一下。
### 配置主页面
**App.tsx**
```typescript
import React, {Component} from 'react';
import './App.css';
import {
useLocation,
useNavigate,
useParams,
Routes,
Route
} from "react-router-dom";
import LoginPage from "./pages/LoginPage";
import {PageProps} from "./models/props";
import AdminPage from "./pages/admin/AdminPage";
import TeacherPage from "./pages/teacher/TeacherPage";
import StudentPage from "./pages/student/StudentPage";
import AddTeacher from "./pages/admin/AddTeacher";
function withRouter(Component: any) {
function ComponentWithRouterProp(props: any) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{location, navigate, params}}
/>
);
}
return ComponentWithRouterProp;
}
class App extends Component<PageProps, any> {
render() {
return <>
<Routes>
<Route path={"/login"} element={<LoginPage router={this.props.router}/>}/>
<Route path={"/admin/*"} element={<AdminPage router={this.props.router}/>}>
</Route>
<Route path={"/teacher"} element={<TeacherPage router={this.props.router}/>}/>
<Route path={"/student"} element={<StudentPage router={this.props.router}/>}/>
<Route path={"/"} element={<LoginPage router={this.props.router}/>}/>
</Routes>
</>
}
}
export default withRouter(App);
```
## 十一、前端登录页面实现
首先封装axios封装axios的接口为工具类
后端返回的json将其封装为 **IResponseData**
### 封装axios
**request.ts**
```typescript
import axios, {AxiosInstance, AxiosRequestConfig, AxiosResponse} from "axios";
import {message} from "antd";
type TAxiosOption = {
baseURL: string;
timeout: number;
}
class Http {
service: AxiosInstance;
constructor(config:TAxiosOption) {
this.service = axios.create(config);
this.service.defaults.withCredentials = true
this.service.interceptors.request.use(
(value)=>{
if (value.headers !== null){
// @ts-ignore
value.headers.Authorization = "Bearer "+sessionStorage.getItem("work_token")
}
return value
},()=>{
console.log("请求异常")
})
this.service.interceptors.response.use((value:AxiosResponse<IResponseData<any>>)=>{
console.log(value)
return value
},(error)=>{
console.log(error.response.data.message)
if (error.response.data.message !== ""){
message.error(error.response.data.message).then(() => {})
}
if (error.response.status === 401){
window.location.hash = "/login"
}
return error
})
}
get<T>(url: string, params?: object, _object = {}): Promise<AxiosResponse<IResponseData<T>>> {
return this.service.get(url, { params, ..._object })
}
post<T>(url: string, data?: object, _object:AxiosRequestConfig = {}): Promise<AxiosResponse<IResponseData<T>>> {
return this.service.post(url, data, _object)
}
put<T>(url: string, params?: object, _object = {}): Promise<AxiosResponse<IResponseData<T>>> {
return this.service.put(url, params, _object)
}
delete<T>(url: string, params?: any, _object = {}): Promise<AxiosResponse<IResponseData<T>>> {
return this.service.delete(url, { params, ..._object })
}
}
export default Http
export interface IResponseData<T> {
success: boolean;
message?:string;
data:T;
code: number;
error?:string
}
```
封装调用后端api的接口为一个类
### 封装接口
**api.ts**
```typescript
import Http, {IResponseData} from "./request";
import {
AdminClass,
GetAdminClassResp,
GetStudentResp,
GetTeacherClassResp,
GetTeacherResp,
LoginResp,
Student,
Teacher
} from "../models/resp";
let http = new Http({
baseURL: "/",
timeout: 30000
});
let base = process.env.REACT_APP_BASE_URL
class Api {
login = async function(type:string, username: string, password: string) {
let data = await http.post<LoginResp>(`${base}/${type}/login`,{
"phone": username,
"password": password
});
return data.data
}
}
export default new Api()
```
登录页面组件采用Layout进行管理在header组件中添加页面标题在content组件中添加Form表单。
### 登录主页面
```typescript
import {Component} from "react";
import {PageProps} from "../models/props";
import {Button, Checkbox, Form, Input, Layout, Select} from "antd";
import { Col, Row } from 'antd';
import loginImg from "../img/loginnew-img.jpg";
import '../utils/api'
import api from "../utils/api";
const { Header, Footer, Content } = Layout;
const options = [
{
"label": "学生",
"value": "student"
},
{
"label": "老师",
"value": "teacher"
},
{
"label": "家长",
"value": "patriarch"
},
{
"label": "管理员",
"value": "admin"
}
]
class LoginPage extends Component<PageProps, any>{
onSubmit = (value:any) => {
api.login(value.type,value.phone,value.password).then(resp => {
if (resp.code === 200){
sessionStorage.setItem("work_token",resp.data.token)
this.props.router.navigate(`/${value.type}`)
}
})
}
render() {
return (<>
<Layout style={{height: "100%"}}>
<Header style={{backgroundColor:"white",height: "18%"}}>
<Row style={{height: "100%"}}>
<Col style={{height:"100%"}} span={8}>
<h1 style={{textAlign:"right",fontSize:"30px",marginTop:"40px",color:"#88c9b4"}}> </h1>
</Col>
<Col span={16} />
</Row>
</Header>
<Content>
<Row style={{height: "100%"}}>
<Col style={{height:"100%"}} span={12}>
<img width={"100%"} height={"100%"} src={loginImg} alt="图片"/>
</Col>
<Col span={6}>
<h1 style={{textAlign:"center",fontSize:"30px",paddingLeft:"70px"}}> </h1>
<div style={{height:"5%"}}/>
<Form
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
initialValues={{ remember: true }}
autoComplete="off"
onFinish={this.onSubmit}
>
<Form.Item
label="角色"
name="type"
initialValue={"student"}
rules={[{ required: true, message: '请选择角色!' }]}
>
<Select size={"large"} options={options} />
</Form.Item>
<Form.Item
label="账号"
name="phone"
rules={[{ required: true, message: '请输入账号!' }]}
>
<Input size={"large"}/>
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[{ required: true, message: '请输入密码!' }]}
>
<Input.Password size={"large"} />
</Form.Item>
<Form.Item name="remember" valuePropName="checked" wrapperCol={{ offset: 8, span: 16 }}>
<Checkbox>记住密码</Checkbox>
</Form.Item>
<Form.Item wrapperCol={{ offset: 13, span: 16 }}>
<Button size={"large"} type="primary" htmlType="submit">
</Button>
</Form.Item>
</Form>
</Col>
<Col span={6}>
</Col>
</Row>
</Content>
<Footer style={{backgroundColor:"white",height: "23%"}} />
</Layout>
</>)
}
}
export default LoginPage
```