之前已经对这个管理系统进行了大体上的结构的展现,后面的篇章将对其中的前端代码进行详细的介绍与展示。
目录
一、数据库建表
二、登录界面前端代码
1.样式展示
2.代码详解
(1)template部分
(2)script部分
(3)安全问题
web会话跟踪:
三、后端代码
1.admin
2.LoginServelet
3.LoginDao
四、过滤器
一、数据库建表
-- 管理员表
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");//是否携带cookiefilterChain.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 {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request=(HttpServletRequest)servletRequest;//向下转型String token=request.getHeader("token");//请求头中的tokenSystem.out.println("token验证过滤器");//验证tokenboolean res= JWTUtil.verify(token);if(res){//token验证成功,继续向后执行,到达目标servlet程序filterChain.doFilter(servletRequest,servletResponse);}else{//token验证失败,向前端响应401Result 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 {@Overrideprotected 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){//登陆成功后为当前登录的用户生成tokenString 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 {/*执行过滤操作的方法*/@Overridepublic 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");//让请求离开过滤器,继续向下执行,下一个可能是过滤器,也可能是目标访问的servletfilterChain.doFilter(servletRequest,servletResponse);}
}