之前已经对这个管理系统进行了大体上的结构的展现,后面的篇章将对其中的前端代码进行详细的介绍与展示。
目录
一、数据库建表
-- 管理员表
CREATE TABLE admin(
id INT PRIMARY KEY AUTO_INCREMENT,
account VARCHAR(20),
PASSWORD VARCHAR(50),
gender CHAR(1),
phone VARCHAR(11),
oper_time DATETIME
)
管理员是不可能从前端的操作页面上添加信息的,所以一般都是在数据库中添加好管理员的信息。
二、登录界面前端代码
1.样式展示
2.代码详解
前端是以vue框架为基础搭建的,所以代码会有三部分(<template>,<script>,<style>)
(1)template部分
<template>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="账号" style="width: 300px;">
<el-input v-model="form.account"></el-input>
</el-form-item>
<el-form-item label="密码" style="width: 300px;">
<el-input type="password" v-model="form.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
</template>
这部分是引用element-UI组件的代码,然后对代码进行修改,已达到自己想要的样式(Element - 网站快速成型工具)
<el-input></el-input>简单的文本框输入,type中可添加“password”使输入的内容隐藏
<el-button></el-button>按钮组件
(2)script部分
<script>
export default {
data() {
return {
form: {
account: "admin",
password: "4321"
}
}
},
methods: {
onSubmit() {
if (this.form.account.length == 0) {
this.$message({
message: '账号不能为空!',
type: 'warning'
});
return;
}
if (this.form.password.length == 0) {
this.$message({
message: '密码不能为空!',
type: 'warning'
});
return;
}
//与后端交互
this.$http.post("login", "account=" + this.form.account + "&password=" + this.form.password).then((
resp) => {
if (resp.data.code == 200) {
sessionStorage.setItem("account", resp.data.result.account);
sessionStorage.setItem("gender", resp.data.result.gender);
sessionStorage.setItem("phone", resp.data.result.phone);
sessionStorage.setItem("token", resp.data.result.token);
this.$router.push("/Main");
}
if (resp.data.code == 201) {
this.$message({
message: resp.data.desc,
type: 'warning'
});
return;
} else {
this.$message({
message: resp.data.desc,
type: 'warning'
});
return;
}
});
}
}
}
</script>
1.data中是前端中的数据,登录需要9账号(account)和密码(password)两个数据,可以将这两个数据放在form中,方便调用
为了测试方便,就直接在前端给账号密码赋值了,后续测试中就不用来回输入数据了,在整体的项目结束后,不要忘了在前端赋值的数据删除
2.methods——方法,用来写js中的函数,方法包含一系列语句和算法,用于执行特定的任务。通过调用方法,可以对对象进行操作、访问字段或返回特定的结果。方法是类中的核心组成部分,用于封装可重用的代码块。
3.onSubmit()函数:用来判断账号密码是否为空,不为空时才能进入到操作页面。
this.$http.post("login", "account=" + this.form.account + "&password=" + this.form.password).then((
resp)
在js中的数据是json类型的,要传到前端需要进行序列化(即将json对象序列化为 键=值&键=值)所以需要这么一段话进行拼接,但是这只适合少量的数据,如果数据量大的话,就会十分麻烦,所以就可以写一个函数专门用来对象序列化:
function jsonToString(form) {
var str = "";
for (var s in form) {
str += s + "=" + form[s] + "&";
}
return str.substring(0, str.length - 1);
}
后面就可以直接调用这个函数即可。
4.从网页接受data.code值,然后进行判断。如果为200,就将管理员的数据一并由路由导航跳转到后面的操作页面,否则就进行报错。
(3)安全问题
1.登陆成功后,在前端获取到后端响应的信息
前端存储用户信息
2.在前端判断用户是否登录
目前除了访问login.vue是不需要登录,除此之外的组件,都必须是登录后才能访问
使用vue-router中的路由导航守卫,在前端每次发生路由跳转时会触发拦截
判断访问哪些组件,哪些组件需要登录,那些组件不需要登录
3.路由嵌套
在main路由下,嵌套其他的子路由
4.后端判断用户身份
添加cookie:
package com.ffyc.dormserver.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
后端向前端响应时,告诉前端,本次响应是安全的
*/
@WebFilter(urlPatterns = "/*")
public class CorsFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
//允许携带Cookie时不能设置为* 否则前端报错
httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("origin"));//允许所有请求跨域
httpResponse.setHeader("Access-Control-Allow-Methods", "*");//允许跨域的请求方法GET, POST, HEAD 等
httpResponse.setHeader("Access-Control-Allow-Headers", "*");//允许跨域的请求头
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");//是否携带cookie
filterChain.doFilter(servletRequest, servletResponse);
}
}
web会话跟踪:
因为http请求是无状态,一次请求响应结束后,就结束了,下一次再向服务器发送请求,服务器并不知道是谁向他发送的
我们需要对整个会话过程进行跟踪:
1.当登录时,后端验证账号密码是否正确,如果账号正确,就需要在后端为当前登录的用户生成一个令牌(token),将令牌信息响应给前端
2.前端存储token
3.后面每次从前端向后端发送请求,都要携带token
4.后端验证令牌,如果令牌有效,继续向后执行,如果令牌无效,向前端返回
package com.ffyc.dormserver.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffyc.dormserver.model.Result;
import com.ffyc.dormserver.util.JWTUtil;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/api/*")
public class TokenFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest)servletRequest;//向下转型
String token=request.getHeader("token");//请求头中的token
System.out.println("token验证过滤器");
//验证token
boolean res= JWTUtil.verify(token);
if(res){//token验证成功,继续向后执行,到达目标servlet程序
filterChain.doFilter(servletRequest,servletResponse);
}else{//token验证失败,向前端响应401
Result result=new Result(401,"token认证失败",null);
servletResponse.getWriter().print(new ObjectMapper().writeValueAsString(result));
}
}
}
三、后端代码
1.admin
package com.ffyc.dormserver.model;
public class Admin {
private int id;
private String account;
private String password;
private String gender;
private String phone;
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getId() {
return id;
}
public String getAccount() {
return account;
}
public String getPassword() {
return password;
}
public String getGender() {
return gender;
}
public String getPhone() {
return phone;
}
public void setId(int id) {
this.id = id;
}
public void setAccount(String account) {
this.account = account;
}
public void setPassword(String password) {
this.password = password;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
在后端获取管理员的信息
2.LoginServelet
package com.ffyc.dormserver.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffyc.dormserver.dao.LoginDao;
import com.ffyc.dormserver.model.Admin;
import com.ffyc.dormserver.model.Result;
import com.ffyc.dormserver.util.JWTUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
/*
登录处理的servlet程序--web层(与前端交互的一层)
*/
@WebServlet(urlPatterns = "/login",name="login",loadOnStartup = 1)
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收前端提交的数据
String account=req.getParameter("account");
String password=req.getParameter("password");
//调用其他的程序处理
LoginDao loginDao=new LoginDao();
Result<Admin>result=null;//标准的数据结果
try {
Admin admin=loginDao.login(account,password);
//向后端做出响应
if(admin!=null){
//登陆成功后为当前登录的用户生成token
String token= JWTUtil.getToken(admin);
admin.setToken(token);
result=new Result<>(200,"登陆成功",admin);
//resp.getWriter().print(new ObjectMapper().writeValueAsString(admin));
}else{
result=new Result<>(201,"账号或密码错误",null);
//resp.getWriter().print("账号或密码错误!");
}
} catch (Exception throwables) {
throwables.printStackTrace();
result=new Result<>(500,"系统忙!",null);
//resp.getWriter().print("系统忙!");
}
resp.getWriter().print(new ObjectMapper().writeValueAsString(result));
//向前端做出响应
}
}
登录界面只需要将数据返回到后端,所以只有dopost方法,没有doget方法
在dopost方法中,需要接受传到后端的账号和密码两个数据,然后调用dao类判断数据库中是否有对应的值,然后返回对应的admin值
3.LoginDao
package com.ffyc.dormserver.dao;
import com.ffyc.dormserver.model.Admin;
import com.mysql.jdbc.Driver;
import java.sql.*;
public class LoginDao {
public Admin login(String account, String passwords) throws SQLException {
DriverManager.registerDriver(new Driver());
String url = "jdbc:mysql://127.0.0.1:3306/dormdb?serverTimezone=Asia/Shanghai";
String user = "root";
String password = "root";
Connection connection = null;
PreparedStatement ps = null;
Admin admin = null;
try {
//建立与数据库的连接
connection = DriverManager.getConnection(url, user, password);
ps = connection.prepareStatement("select id,account,gender,phone from admin where account=? and password=?");
ps.setString(1, account);
ps.setString(2, passwords);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
admin = new Admin();
admin.setId(rs.getInt("id"));
admin.setAccount(rs.getString("account"));
admin.setGender(rs.getString("gender"));
admin.setPhone(rs.getString("phone"));
}
return admin;
} finally {
if (ps != null) {
ps.close();
}
if (connection != null) {
connection.close();
}
}
}
}
与后端交互的代码,只用查找前端传过来的数据在数据库中是否存在即可
连接好数据库后,进行查找,将查找出来的值传给damin
四、过滤器
package com.ffyc.dormserver.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/*
设置请求和响应编码集
*/
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
/*
执行过滤操作的方法
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("编码过滤器");
//设置请求编码集
servletRequest.setCharacterEncoding("utf-8");
//设置响应编码集
servletResponse.setContentType("text/html;charset=utf-8");
//让请求离开过滤器,继续向下执行,下一个可能是过滤器,也可能是目标访问的servlet
filterChain.doFilter(servletRequest,servletResponse);
}
}