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

15 KiB
Raw Blame History

title tags categories date summary
毕业设计(4)
java
ssm
react
ant design
typescript
毕业设计 2022-12-14 12:40:32 登录实现

十、管理登录逻辑后端实现

    管理员账号的账号密码从配置文件中读写

@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的配置文件中配置拦截器配置除了登陆地址以外全部拦截

<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>

拦截器类

@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模板然后添加依赖

添加依赖

yarn add antd
yarn add axios
yarn add react-router-dom


yarn add http-proxy-middleware -D

因为在项目开发过程中,需要解决跨域的问题,所以我们使用开发依赖 http-proxy-middleware,将前端的接口转发到本地开发地址。

解决本地调试跨域

setupProxy.js

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

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

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

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

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表单。

登录主页面

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