管理系统搭建一般步骤(会话跟踪 路由导航守卫 响应拦截器)

news2025/1/23 2:43:58

1,vue-cli进行项目搭建

2,使用ELement-UI

3,使用vue组件路由

4,点击登录,向后端进行账号密码比对

三种情况:

密码有误

服务器忙

密码正确。

具体步骤:

首先写好前端一个大体框架,然后点击登录按钮时触发的函数,还有使用v-model对数据进行双向绑定。

<template>
	<!-- 一个组件中只能有一个根标签-->
	<div class="login_container">
		<!-- 登录盒子-->
		<div class="login_box">
			<!-- 头像盒子-->
			<div class="img_box">
				<img src="./assets/logo.png" />
			</div>
			<div style="padding: 100px 30px 30px 0px;">
				<el-form label-width="80px">
					<el-form-item label="账号">
						<el-input v-model="from.account"></el-input>
					</el-form-item>
					<el-form-item label="密码">
						<el-input type="password" v-model="from.password"></el-input>
					</el-form-item>
					<el-form-item>
						<el-button type="primary" round @click="login()">登录</el-button>
						<el-button>取消</el-button>
					</el-form-item>
				</el-form>
			</div>
		</div>
	</div>
</template>

 其次就是js代码:这是vue框架所以格式是按照vue框架给定的来的。这段代码主要作用就是,前端传给后端账号密码后,后端给前端的响应,前端对响应做出判断,通过状态码做出判断 。

<script>
	export default {
		data() {
			return {
				from: {
					account: "adele",
					password: "12345678"
				}

			}
		},
		methods: {
			login() {
				this.$http.post("LoginServlet", jsonToString(this.from)).then(resp => {
					if (resp.data.code == 201) {
						this.$message({
							message: resp.data.message,
							type: 'warning'
						});
					} else if (resp.data.code == 200) {
						this.$message({
							message: resp.data.message,
							type: 'success'
						});
						sessionStorage.setItem("account", resp.data.data.account);
						sessionStorage.setItem("userToken", resp.data.data.token);
						this.$router.push("/main"); //在js中进行路由导航
					}

				})

			}
		},
	}
	//将json对象序列化为键=值&键=值
	function jsonToString(jsonobj) {
		console.log(jsonobj)
		var str = "";
		for (var s in jsonobj) {
			str += s + "=" + jsonobj[s] + "&";
		}
		return str.substring(0, str.length - 1);
	}
</script>

由于后端 

5,接下来就是后端:

 在写后端之前需要在数据库中创建一个管理员的表,代表登录人信息,因为后端需要和数据库进行比对,如果数据库中存在当前登录的信息才可以进入主页面。

这是登录的servlet,读入流,调用dao的函数进行账号密码比对,通过比对结果向前端传入数据。

由于json跨平台兼容,数据体积小等优点,所以我们通过json格式化java对象传给前端。 

package com.ffyc.webserver.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffyc.webserver.Models.CommonData;
import com.ffyc.webserver.dao.LoginDao;
import com.ffyc.webserver.Models.User;
import com.ffyc.webserver.util.JWTUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;

public class LoginServlet extends HttpServlet {
    private LoginDao dao=new LoginDao();

//    @Override
//    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//
//    }
//    @Override
//    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        String account=req.getParameter("account");//接收请求中我们自己提交的数据
//        User user=new User("adele","123456789");
//        ObjectMapper objectMapper=new ObjectMapper();
//        String stu=objectMapper.writeValueAsString(user);
//        resp.getWriter().print(stu);
//        System.out.println(objectMapper.writeValueAsString(user));
//        if(account.equals("admin"))
//        {
//            resp.getWriter().print("账号已注册");
//        }
//        else
//        {
//            resp.getWriter().print("账号未注册");
//        }
//    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String account=req.getParameter("account");
        String password=req.getParameter("password");
        System.out.println(account);
        System.out.println(password);
        PrintWriter printWriter=resp.getWriter();
        User user= null;
        CommonData commonData=null;
        try {
            user = dao.logindao(account,password);
            if(user==null) {
                commonData=new CommonData(201,"账号或密码输入有误,请重新输入!");
            }
            else {
                user.setToken(JWTUtil.getToken(user));
                commonData=new CommonData(200,user,"登录成功");
                System.out.println(user.getToken());
            }
        } catch (Exception throwables) {

            commonData=new CommonData(500,"服务器忙...请稍后重试!");
        }
        ObjectMapper objectMapper=new ObjectMapper();
        String json=objectMapper.writeValueAsString(commonData);
        printWriter.print(json);
    }
}

然后就是dao层的调用了,因为只要是对数据库进行增删改查操作时,我们都需要调用一次数据库,所以,将数据库连接打包成一个类,每当进行操作,调用该类即可。

DBUtil.java

package com.ffyc.webserver.util;

import java.sql.*;

public class DButils {
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {

        String url = "jdbc:mysql://127.0.0.1:3306/userdb?serverTimezone=Asia/Shanghai";
        Connection connection = DriverManager.getConnection(url, "root", "123456");
        return connection;
    }

    public static void close(ResultSet resultSet, Statement statement, Connection connection) throws SQLException {

        if (resultSet != null) {
            resultSet.close();
        }
        if (statement != null) {
            statement.close();
        }
        if (connection != null) {
            connection.close();
        }

    }
}

然后再写一个登录对账号密码验证的dao:使用sql语言调用数据库进行查询,最后将查询到数据以对象形式返回。如果对象为空代表未在数据库查到当前账号,说明账号或密码有误,不为空则说明登录成功。

package com.ffyc.webserver.dao;

import com.ffyc.webserver.Models.User;
import com.ffyc.webserver.util.DButils;

import java.sql.*;

public class LoginDao {
    public User logindao(String account, String password) throws SQLException {
        String sql="select * from user where account=? and password=?";
        Connection connection=DButils.getConnection();
        PreparedStatement preparedStatement= connection.prepareStatement(sql);
        preparedStatement.setObject(1,account);
        preparedStatement.setObject(2,password);
        ResultSet resultSet=preparedStatement.executeQuery();
        User user=null;

        if(resultSet.next())
        {
            user=new User();
            String psw=resultSet.getString("password");
            user.setAccount(account);
            user.setPassword(psw);
        }
        DButils.close(resultSet,preparedStatement,connection);
        return user;
    }
}

 因为要以对象形式返回数据,所以需要创建登录者信息类:

package com.ffyc.webserver.Models;

public class User {
    String id;
    String account;
    String password;
    String token;

    public String getToken() {
        return this.token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getAccount() {
        return account;
    }

    public String getPassword() {
        return password;
    }
}

最后,不要忘记配置当前servlet:

    <servlet>
        <servlet-name>Login</servlet-name>
        <servlet-class>com.ffyc.webserver.servlet.LoginServlet</servlet-class>
<!--        <load-on-startup>0</load-on-startup>-->
    </servlet>
    <servlet-mapping>
        <servlet-name>Login</servlet-name>
        <url-pattern>/LoginServlet</url-pattern>
    </servlet-mapping>

这样,一个简单的登录就完成了。

6,我们发现,类似于状态码为500的公共拦截码如果每次都写一遍很费时间,所以在前端为axios配置响应拦截器,将500这种公共拦截码进行拦截。

import axios from 'axios';
//设置访问后台服务器地址
axios.defaults.baseURL = "http://localhost:8088/webServer/";
//将 axios 挂载到 vue 全局对象中,使用 this 可以直接访问

//axios 请求拦截 每次向后端发送
axios.interceptors.request.use(config => {
	//为请求头对象,添加 Token 验证的 token 字段
	config.headers.userToken = window.sessionStorage.getItem('userToken');
	return config;
})
axios.interceptors.response.use((resp) => { //正常响应拦截
	if (resp.data.code == 500) {
		ElementUI.Message({
			message: resp.data.message,
			type: "error"
		})
	}
	if (resp.data.code == 402) {
		ElementUI.Message({
			message: resp.data.message,
			type: "error"
		})
		router.replace("/login");
	}
	return resp;
});

Vue.prototype.$http = axios;

 为axios框架配置响应拦截器,一旦后端做出响应,响应器拦截。

7,存在的问题:在登录页面的地址栏输入主页面地址可以直接访问,无需验证账号密码。

解决办法:在登录前判断验证用户是否登录,未登录就不能访问/main。

路由导航守卫:每当发生路由,自动调用此方法。

        路由导航守卫是一种在路由导航过程中进行拦截和控制的机制。它允许开发者在用户导航到某个路由之前、之后或在路由发生变化时执行相应的操作。

        路由导航守卫可以用于实现诸如身份验证、权限检查、日志记录等功能。通过注册守卫函数,开发者可以在路由导航事件发生时介入,并根据需要执行特定的逻辑。

to:访问组件地址

from:从哪个组件发起的路由

next():放行函数。 

写入index.js中:导出路由器对象,并将代码包装在模块中并导出对象

import Vue from 'vue';
import router from 'vue-router'; /* 导入路由 */
/* 导入需要路由的组件 */
import Login from "../Login.vue";
import Main from "../Main.vue";
import student from '../student/student.vue';
import marjor from '../marjor/marjor.vue';
Vue.use(router)
/* 定义组件路由 */
var rout = new router({
	routes: [{
			path: '/',
			component: Login
		},
		{
			path: '/login',
			/* 地址 */
			name: 'login',
			/* 地址名 */
			component: Login /* 调用地址 */
		},
		{
			path: '/main',
			component: Main,
			children: [{
				path: "/student",
				component: student,
			}, {
				path: "/marjor",
				component: marjor
			}],
		}
	]
});
//导出路由对象
rout.beforeEach((to, from, next) => {
	if (to.path == '/login') {
		//如果用户访问的登录页, 直接放行
		return next();
	} else {
		var account = window.sessionStorage.getItem("account");
		if (account == null) {
			return next("/login");
		} else {
			next();
		}
	}
})
export default rout;

8, 前端显示用户信息,并实现安全退出。

<script>
	export default {
		name: "main",
		data() {
			return {
				account: ""
			}
		},
		methods: {
			open() {
				this.$confirm('此操作将退出当前账号, 是否继续?', '操作提示', {
					confirmButtonText: '确定',
					cancelButtonText: '取消',
					type: 'warning'
				}).then(() => {
					this.$message({
						type: 'success',
						message: '账号已退出!',
					});
					sessionStorage.clear();
					this.$router.push("/login"); //在js中进行路由导航
				});
			},
			test() {
				this.$http.get("admin/LoginServlet");
			}

		},
		mounted() {
			this.account = sessionStorage.getItem("account");
		}
	}
</script>

9,登录成功后,进入管理后台,需要会话跟踪。

由于http请求是无状态的,默认不携带用户信息,所以在一次会话中,需要多次向后端发送请求,我们需要让后端程序知道是谁发送的请求。这个过程称为会话跟踪。 

 ①传统的session会话:登录成功后,在后端生成HttpSession对象。里面可以存储用户信息和id,存储在服务器端,id号响应给前端,前端提交id到后端,查询session对象。(不适合分布式架构)

         在分布式架构中,各个子系统通常相互独立且自治,它们可以并行地处理任务,从而提高系统的处理能力。每个子系统可以运行在自己的硬件设备上,也可以共享资源,例如数据库、存储等。通过将负载均衡和故障容错机制引入到系统中,分布式架构可以实现系统的高可用性和容错性。

 ②token会话:

jwt搭建:

①配置安装jar包;

②创建生成token方法;

③之后每次请求都要发送token(在前端axios中配置拦截器,每次向后端发送) 

//axios 请求拦截 每次向后端发送
axios.interceptors.request.use(config => {
	//为请求头对象,添加 Token 验证的 token 字段
	config.headers.userToken = window.sessionStorage.getItem('userToken');
	return config;
})

④每次做出响应之前进行token验证

package com.ffyc.webserver.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ffyc.webserver.Models.User;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 */
public class JWTUtil {

    /**
     * 根据用户id,账号生成token
     * @param u
     * @return
     */
    public static String getToken(User u) {
        String token = "";
        try {
            //过期时间 为1970.1.1 0:0:0 至 过期时间  当前的毫秒值 + 有效时间
            Date expireDate = new Date(new Date().getTime() + 3600*1000);
            //秘钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
            //设置头部信息
            Map<String,Object> header = new HashMap<>();
            header.put("typ","JWT");
            header.put("alg","HS256");
            //携带id,账号信息,生成签名
            token = JWT.create()
                    .withHeader(header)
                    .withClaim("id",u.getId())
                    .withClaim("account",u.getAccount())
                    .withExpiresAt(expireDate)
                    .sign(algorithm);
        }catch (Exception e){
            e.printStackTrace();
            return  null;
        }
        return token;
    }

    /**
     * 验证token是否有效
     * @param token
     * @return
     */
    public static boolean verify(String token){
        try {
            //验签
            Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception e) {//当传过来的token如果有问题,抛出异常
            return false;
        }
    }

    /**
     * 获得token 中playload部分数据,按需使用
     * @param token
     * @return
     */
    public static DecodedJWT getTokenInfo(String token){
        return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);
    }

}
package com.ffyc.webserver.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffyc.webserver.Models.CommonData;
import com.ffyc.webserver.util.JWTUtil;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;

public class TokenFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest=(HttpServletRequest) servletRequest;
        String userToken = httpServletRequest.getHeader("userToken");
        if(JWTUtil.verify(userToken))
        {
            filterChain.doFilter(servletRequest, servletResponse);
        }else{
            PrintWriter printWriter=servletResponse.getWriter();
            CommonData commonData=new CommonData(402,"Token认证失败!");
            ObjectMapper objectMapper=new ObjectMapper();
            String json=objectMapper.writeValueAsString(commonData);
            printWriter.print(json);
        }
    }
}

会话跟踪实现:

第一次登录成功时,在后端根据管理员信息以及密钥生成的token(字符串),响应给前端,之后每次请求都携带token到后端,后端对token进行验证。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1094667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度强化学习第 1 章 机器学习基础

1.1线性模型 线性模型&#xff08;linear models&#xff09;是一类最简单的有监督机器学习模型&#xff0c;常被用于简单的机 器学习任务。可以将线性模型视为单层的神经网络。本节讨论线性回归、逻辑斯蒂回归&#xff08;logistic regression&#xff09;、 softmax 分类器等…

Windows 中环境变量的查看与设置

接触了LLM应用开发后&#xff0c;经常要用到环境变量的设置&#xff08;openAI apikey啥的&#xff09; 但是老忘记&#xff0c;今天来学习和总结一下 主要用到以下几种&#xff1a;使用 PowerShell、CMD 和 Python 来查看和设置环境变量 文章目录 1. PowerShell查看环境变量&a…

掌握深入挖掘数据本质的方法

文章目录 掌握深入挖掘数据本质的方法1. 确定数据类型2. 数据清洗3. 数据可视化4. 探索性数据分析5. 特征工程6. 机器学习算法7. 自然语言处理 &#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华…

C语言:冒泡排序

C语言 基础开发----目录 冒泡排序 1. 原理 对一个有n个数据的数组进行遍历&#xff0c;依次对相邻两个数据进行比较大小&#xff0c;若大的数据在前面则交换位置&#xff08;升序&#xff09;&#xff0c;完成一次遍历后数组中最大的数据到了数组的末尾位置&#xff0c;就象水…

六、RocketMQ发送事务消息

事务消息介绍 在一些对数据一致性有强需求的场景&#xff0c;可以用 Apache RocketMQ 事务消息来解决&#xff0c;从而保证上下游数据的一致性。 以电商交易场景为例&#xff0c;用户支付订单这一核心操作的同时会涉及到下游物流发货、积分变更、购物车状态清空等多个子系统的…

MySQL有时候命中索引有时候又不命中

索引失效的情况 -----可能 索引主要看where 、group by 、order by 1.组合索引不遵循最佳左前缀法制。最佳左前缀法制&#xff1a;如果索引了多列&#xff0c;要遵循最左前缀法则&#xff0c;指的是查询从索引的最左前列开始并且不跳过索引中的列。如组合索引为A B C 只有ABC,A…

【蓝桥】数树数

一、题目 1、题目描述 给定一个层数为 n n n 的满二叉树&#xff0c;每个点编号规则如下&#xff1a; 具体来说&#xff0c;二叉树从上往下数第 p p p 层&#xff0c;从左往右编号分别为&#xff1a;1,2,3,4&#xff0c;…, 2p-1。 给你一条从根节点开始的路径&#xff0…

Node.js初体验

Node.js简介 node.js的运行环境 1.V8引擎对js代码进行解析与执行 2.内置API&#xff1a;fs、path、http...等&#xff0c;提供了一些能力&#xff0c;能够使得js调用这些API去做一些后端的事情 流程&#xff1a;我们在node.js的运行环境中编写待执行的JavaScript代码&#…

Spring Cloud Gateway 使用 Redis 限流使用教程

从本文开始&#xff0c;笔者将总结 spring cloud 相关内容的教程 版本选择 为了适应 java8&#xff0c;笔者选择了下面的版本&#xff0c;后续会出 java17的以SpringBoot3.0.X为主的教程 SpringBoot 版本 2.6.5 SpringCloud 版本 2021.0.1 SpringCloudAlibaba 版本 2021.0.1.…

单目3D目标检测——MonoCon 模型训练 | 模型推理

本文分享 MonoCon 的模型训练、模型推理、可视化3D检测结果、以及可视化BEV效果。 模型原理&#xff0c;参考我这篇博客&#xff1a;【论文解读】单目3D目标检测 MonoCon&#xff08;AAAI2022&#xff09;_一颗小树x的博客-CSDN博客 源码地址&#xff1a;https://github.com/2…

在vs code中创建一个名为 “django_env“ 的虚拟环境报错?!以下或许方法可以解决

# vs code 终端窗口中运行&#xff1a; mkvirtualenv django_env # 拓展&#xff1a; mkvirtualenv django_env 是一个命令&#xff0c;用于创建一个名为 "django_env" 的虚拟环境。虚拟环境是一种用于隔离不同Python项目所需依赖的工具。通过创建虚拟环境&#x…

【分布式计算】九、容错性 Fault Tolerance

分布式系统应当有一定的容错性&#xff0c;发生故障时仍能运行 一些概念&#xff1a; 可用性Availability&#xff1a;系统是否准备好立即使用 可靠性Reliability&#xff1a;系统连续运行不发生故障 安全性&#xff1a;衡量安全故障的指标&#xff0c;没有严重事件发生 可维护…

zabbix内置宏、自动发现与注册

一、zabbix内置宏 1、概念&#xff1a; 在Zabbix中&#xff0c;内置宏是一种特殊的变量&#xff0c;通常用在 Trigger 名称和表达式中&#xff0c;引用有关监控对象的信息。 2、种类&#xff1a; {HOST.NAME} 主机名 {HOST.IP} 主机 IP 地址 {TRIGGER.DESCRIPTION} 触…

Unity中Shader的深度缓冲区

文章目录 前言一、什么是深度缓冲区深度缓冲区是和颜色缓冲区、模板缓冲区平行的一个缓冲区在这里插入图片描述 二、什么是深度信息三、深度缓冲区的作用 前言 Unity中的深度缓冲区 一、什么是深度缓冲区 深度缓冲区是和颜色缓冲区、模板缓冲区平行的一个缓冲区 深度缓冲区&a…

勒索软件组织声称它“损害了所有索尼系统”

新晋勒索软件组织 RansomedVC 声称已成功入侵娱乐巨头索尼的计算机系统。正如勒索软件团伙所做的那样&#xff0c;它在其暗网网站上发布了这一消息&#xff0c;并在那里出售从受害者计算机网络中窃取的数据。 该公告称索尼的数据正在出售&#xff1a; 索尼集团公司&#xff08…

数据结构与算法--并查集结构

数据结构与算法--并查集结构 1 岛问题 2 并查集结构 1 岛问题 一个矩阵中只有0和1两种值&#xff0c;每个位置都可以和自己的上、下、左、右 四个位置相连&#xff0c;如果有一片1连在一起&#xff0c;这个部分叫做一个岛&#xff0c;求一个矩阵中有多少个岛? 【举例】 001…

FutureTask的测试使用和方法执行分析

FutureTask类图如下 java.util.concurrent.FutureTask#run run方法执行逻辑如下 public void run() {if (state ! NEW ||!RUNNER.compareAndSet(this, null, Thread.currentThread()))return;try {Callable<V> c callable;if (c ! null && state NEW) {V res…

【软考】9.4 图的概念/存储/遍历/最小生成树/拓扑/查找

《图》 图的存储&#xff08;顶点和边&#xff09; 邻接矩阵&#xff1a;适合边数较多的图&#xff0c;不易造成浪费无向图&#xff1a;不分方向&#xff1b;对称矩阵 邻接链表&#xff1a;顶点&#xff0c;边——>&#xff08;编号&#xff0c;权值&#xff09;&#xff1b…

二维数组的行指针与列指针

二维数组的行指针与列指针 笔记来源&#xff1a;懒猫老师-C语言-用指针访问二维数组&#xff08;指针与二维数组&#xff09; 声明&#xff1a;本文笔记来自bili懒猫老师&#xff0c;仅供学习参考 回顾一维数组 int a[3];//其中a或a[0]是数组首地址 //a1指向第二个元素的地址…